summaryrefslogtreecommitdiff
path: root/sot/source/sdstor
diff options
context:
space:
mode:
Diffstat (limited to 'sot/source/sdstor')
-rw-r--r--sot/source/sdstor/makefile.mk64
-rw-r--r--sot/source/sdstor/stg.cxx1091
-rw-r--r--sot/source/sdstor/stgavl.cxx419
-rw-r--r--sot/source/sdstor/stgavl.hxx79
-rw-r--r--sot/source/sdstor/stgcache.cxx546
-rw-r--r--sot/source/sdstor/stgcache.hxx132
-rw-r--r--sot/source/sdstor/stgdir.cxx1054
-rw-r--r--sot/source/sdstor/stgdir.hxx132
-rw-r--r--sot/source/sdstor/stgelem.cxx425
-rw-r--r--sot/source/sdstor/stgelem.hxx166
-rw-r--r--sot/source/sdstor/stgio.cxx389
-rw-r--r--sot/source/sdstor/stgio.hxx80
-rw-r--r--sot/source/sdstor/stgole.cxx230
-rw-r--r--sot/source/sdstor/stgole.hxx77
-rw-r--r--sot/source/sdstor/stgstrms.cxx1247
-rw-r--r--sot/source/sdstor/stgstrms.hxx170
-rw-r--r--sot/source/sdstor/storage.cxx1563
-rw-r--r--sot/source/sdstor/storinfo.cxx111
-rw-r--r--sot/source/sdstor/ucbstorage.cxx3600
-rw-r--r--sot/source/sdstor/unostorageholder.cxx197
-rw-r--r--sot/source/sdstor/unostorageholder.hxx77
21 files changed, 11849 insertions, 0 deletions
diff --git a/sot/source/sdstor/makefile.mk b/sot/source/sdstor/makefile.mk
new file mode 100644
index 000000000000..f5c6d81548c9
--- /dev/null
+++ b/sot/source/sdstor/makefile.mk
@@ -0,0 +1,64 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# This file is part of OpenOffice.org.
+#
+# OpenOffice.org is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# only, as published by the Free Software Foundation.
+#
+# OpenOffice.org is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License version 3 for more details
+# (a copy is included in the LICENSE file that accompanied this code).
+#
+# You should have received a copy of the GNU Lesser General Public License
+# version 3 along with OpenOffice.org. If not, see
+# <http://www.openoffice.org/license.html>
+# for a copy of the LGPLv3 License.
+#
+#*************************************************************************
+
+PRJ=..$/..
+
+PRJNAME=sot
+TARGET=sdstor
+
+# --- Settings -----------------------------------------------------
+
+ENABLE_EXCEPTIONS=true
+
+.INCLUDE : settings.mk
+.INCLUDE : $(PRJ)$/util$/makefile.pmk
+
+# --- Files --------------------------------------------------------
+
+SLOFILES = \
+ $(SLO)$/unostorageholder.obj \
+ $(SLO)$/ucbstorage.obj \
+ $(SLO)$/stg.obj \
+ $(SLO)$/stgcache.obj \
+ $(SLO)$/stgstrms.obj \
+ $(SLO)$/stgelem.obj \
+ $(SLO)$/stgio.obj \
+ $(SLO)$/stgole.obj \
+ $(SLO)$/stgdir.obj \
+ $(SLO)$/stgavl.obj \
+ $(SLO)$/storinfo.obj \
+ $(SLO)$/storage.obj
+
+EXCEPTIONSFILES= \
+ $(SLO)$/unostorageholder.obj\
+ $(SLO)$/ucbstorage.obj\
+ $(SLO)$/storage.obj
+
+# --- Targets -------------------------------------------------------
+
+.INCLUDE : target.mk
+
diff --git a/sot/source/sdstor/stg.cxx b/sot/source/sdstor/stg.cxx
new file mode 100644
index 000000000000..1c749aa05cb8
--- /dev/null
+++ b/sot/source/sdstor/stg.cxx
@@ -0,0 +1,1091 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include <storinfo.hxx>
+#include <osl/file.hxx>
+#include <tools/tempfile.hxx>
+#include <tools/ownlist.hxx>
+#include <tools/string.hxx>
+#ifndef _TOOLS_FSYS_HXX
+#include <tools/fsys.hxx>
+#endif
+#ifndef _TOOLS_STREAM_HXX
+#include <tools/stream.hxx>
+#endif
+#include <tools/pstm.hxx>
+#include <tools/debug.hxx>
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+#include "stgole.hxx"
+
+static long nTmpCount = 0;
+
+// The internal open mode is STREAM_READ | STREAM_TRUNC, which is silly
+// by itself. It inhibits the checking of sharing modes and is used
+// during CopyTo() and MoveTo() for opening a stream in read mode
+// although it may be open in DENYALL mode
+
+#define INTERNAL_MODE ( STREAM_READ | STREAM_TRUNC )
+
+///////////////////////// class StorageBase //////////////////////////////
+
+TYPEINIT0( StorageBase );
+TYPEINIT1( BaseStorageStream, StorageBase );
+TYPEINIT1( BaseStorage, StorageBase );
+
+StorageBase::StorageBase()
+ : m_bAutoCommit( FALSE )
+{
+ m_nMode = STREAM_READ;
+ m_nError = SVSTREAM_OK;
+}
+
+StorageBase::~StorageBase()
+{
+}
+
+// The following three methods are declared as const, since they
+// may be called from within a const method.
+
+ULONG StorageBase::GetError() const
+{
+ ULONG n = m_nError;
+ ((StorageBase*) this)->m_nError = SVSTREAM_OK;
+ return n;
+}
+
+void StorageBase::SetError( ULONG n ) const
+{
+ if( !m_nError )
+ ((StorageBase*) this)->m_nError = n;
+}
+
+void StorageBase::ResetError() const
+{
+ ((StorageBase*) this)->m_nError = SVSTREAM_OK;
+}
+
+// Retrieve the underlying SvStream for info purposes
+
+const SvStream* OLEStorageBase::GetSvStream_Impl() const
+{
+ return pIo ? pIo->GetStrm() : NULL;
+}
+
+OLEStorageBase::OLEStorageBase( StgIo* p, StgDirEntry* pe, StreamMode& nMode )
+ : nStreamMode( nMode ), pIo( p ), pEntry( pe )
+{
+ p->IncRef();
+ if( pe )
+ pe->nRefCnt++;
+}
+
+OLEStorageBase::~OLEStorageBase()
+{
+ if( pEntry )
+ {
+ DBG_ASSERT( pEntry->nRefCnt, "RefCount unter 0" );
+ if( !--pEntry->nRefCnt )
+ {
+ if( pEntry->bZombie )
+ delete pEntry;
+ else
+ pEntry->Close();
+ }
+ }
+
+
+ if( !pIo->DecRef() )
+ delete pIo;
+}
+
+// Validate the instance for I/O
+
+BOOL OLEStorageBase::Validate_Impl( BOOL bWrite ) const
+{
+ if( pEntry
+ && !pEntry->bInvalid
+ && ( !bWrite || !pEntry->bDirect || ( nStreamMode & STREAM_WRITE ) ) )
+ return TRUE;
+ return FALSE;
+}
+
+BOOL OLEStorageBase::ValidateMode_Impl( StreamMode m, StgDirEntry* p ) const
+{
+ if( m == INTERNAL_MODE )
+ return TRUE;
+ USHORT nCurMode = ( p && p->nRefCnt ) ? p->nMode : 0xFFFF;
+ if( ( m & 3 ) == STREAM_READ )
+ {
+ // only SHARE_DENYWRITE or SHARE_DENYALL allowed
+ if( ( ( m & STREAM_SHARE_DENYWRITE )
+ && ( nCurMode & STREAM_SHARE_DENYWRITE ) )
+ || ( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) ) )
+ return TRUE;
+ }
+ else
+ {
+ // only SHARE_DENYALL allowed
+ // storages open in r/o mode are OK, since only
+ // the commit may fail
+ if( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+//////////////////////// class StorageStream /////////////////////////////
+
+TYPEINIT1( StorageStream, BaseStorageStream );
+
+StorageStream::StorageStream( StgIo* p, StgDirEntry* q, StreamMode m )
+ : OLEStorageBase( p, q, m_nMode ), nPos( 0L )
+{
+ // The dir entry may be 0; this means that the stream is invalid.
+ if( q )
+ {
+ if( q->nRefCnt == 1 )
+ {
+ q->nMode = m;
+ q->OpenStream( *p );
+ }
+ }
+ else
+ m &= ~STREAM_READWRITE;
+ m_nMode = m;
+}
+
+StorageStream::~StorageStream()
+{
+ // Do an auto-commit if the entry is open in direct mode
+ if( m_bAutoCommit )
+ Commit();
+ if( pEntry && pEntry->nRefCnt && pEntry->bDirect && (m_nMode & STREAM_WRITE) )
+ pEntry->Commit();
+}
+
+BOOL StorageStream::Equals( const BaseStorageStream& rStream ) const
+{
+ const StorageStream* pOther = PTR_CAST( StorageStream, &rStream );
+ return pOther && ( pOther->pEntry == pEntry );
+}
+
+ULONG StorageStream::Read( void* pData, ULONG nSize )
+{
+ if( Validate() )
+ {
+ pEntry->Seek( nPos );
+ nSize = pEntry->Read( pData, (INT32) nSize );
+ pIo->MoveError( *this );
+ nPos += nSize;
+ }
+ else
+ nSize = 0L;
+ return nSize;
+}
+
+ULONG StorageStream::Write( const void* pData, ULONG nSize )
+{
+ if( Validate( TRUE ) )
+ {
+ pEntry->Seek( nPos );
+ nSize = pEntry->Write( pData, (INT32) nSize );
+ pIo->MoveError( *this );
+ nPos += nSize;
+ }
+ else
+ nSize = 0L;
+ return nSize;
+}
+
+ULONG StorageStream::Seek( ULONG n )
+{
+ if( Validate() )
+ return nPos = pEntry->Seek( n );
+ else
+ return n;
+}
+
+void StorageStream::Flush()
+{
+ // Flushing means committing, since streams are never transacted
+ Commit();
+}
+
+BOOL StorageStream::SetSize( ULONG nNewSize )
+{
+ if( Validate( TRUE ) )
+ {
+ BOOL b = pEntry->SetSize( (INT32) nNewSize );
+ pIo->MoveError( *this );
+ return b;
+ }
+ else
+ return FALSE;
+}
+
+BOOL StorageStream::Commit()
+{
+ if( !Validate() )
+ return FALSE;
+ if( !( m_nMode & STREAM_WRITE ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ else
+ {
+ pEntry->Commit();
+ pIo->MoveError( *this );
+ return Good();
+ }
+}
+
+BOOL StorageStream::Revert()
+{
+ pEntry->Revert();
+ pIo->MoveError( *this );
+ return Good();
+}
+
+BOOL StorageStream::CopyTo( BaseStorageStream* pDest )
+{
+ if( !Validate() || !pDest->Validate( TRUE ) || Equals( *pDest ) )
+ return FALSE;
+ pEntry->Copy( *pDest );
+ pDest->Commit();
+ pIo->MoveError( *this );
+ SetError( pDest->GetError() );
+ return BOOL( Good() && pDest->Good() );
+}
+
+const SvStream* StorageStream::GetSvStream() const
+{
+ return GetSvStream_Impl();
+}
+
+BOOL StorageStream::Validate( BOOL bValidate ) const
+{
+ BOOL bRet = Validate_Impl( bValidate );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+BOOL StorageStream::ValidateMode( StreamMode nMode ) const
+{
+ BOOL bRet = ValidateMode_Impl( nMode, NULL );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+BOOL StorageStream::ValidateMode( StreamMode nMode, StgDirEntry* p ) const
+{
+ BOOL bRet = ValidateMode_Impl( nMode, p );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+///////////////////////// class SvStorageInfo //////////////////////////////
+
+SvStorageInfo::SvStorageInfo( const StgDirEntry& rE )
+{
+ rE.aEntry.GetName( aName );
+ bStorage = BOOL( rE.aEntry.GetType() == STG_STORAGE );
+ bStream = BOOL( rE.aEntry.GetType() == STG_STREAM );
+ nSize = bStorage ? 0 : rE.aEntry.GetSize();
+}
+
+/////////////////////////// class Storage ////////////////////////////////
+
+BOOL Storage::IsStorageFile( const String & rFileName )
+{
+ StgIo aIo;
+ if( aIo.Open( rFileName, STREAM_STD_READ ) )
+ return aIo.Load();
+ return FALSE;
+}
+
+BOOL Storage::IsStorageFile( SvStream* pStream )
+{
+ StgHeader aHdr;
+ ULONG nPos = pStream->Tell();
+ BOOL bRet = ( aHdr.Load( *pStream ) && aHdr.Check() );
+
+ // It's not a stream error if it is too small for a OLE storage header
+ if ( pStream->GetErrorCode() == ERRCODE_IO_CANTSEEK )
+ pStream->ResetError();
+ pStream->Seek( nPos );
+ return bRet;
+}
+
+// Open the storage file. If writing is permitted and the file is not
+// a storage file, initialize it.
+
+TYPEINIT1( Storage, BaseStorage );
+
+Storage::Storage( const String& rFile, StreamMode m, BOOL bDirect )
+ : OLEStorageBase( new StgIo, NULL, m_nMode ), aName( rFile ), bIsRoot( FALSE )
+{
+ BOOL bTemp = FALSE;
+ if( !aName.Len() )
+ {
+ // no name = temporary name!
+ aName = TempFile::CreateTempName();
+ bTemp = TRUE;
+ }
+ // the root storage creates the I/O system
+ m_nMode = m;
+ if( pIo->Open( aName, m ) )
+ {
+ Init( BOOL( ( m & ( STREAM_TRUNC | STREAM_NOCREATE ) ) == STREAM_TRUNC ) );
+ if( pEntry )
+ {
+ pEntry->bDirect = bDirect;
+ pEntry->nMode = m;
+ pEntry->bTemp = bTemp;
+ }
+ }
+ else
+ {
+ pIo->MoveError( *this );
+ pEntry = NULL;
+ }
+}
+
+// Create a storage on a given stream.
+
+Storage::Storage( SvStream& r, BOOL bDirect )
+ : OLEStorageBase( new StgIo, NULL, m_nMode ), bIsRoot( FALSE )
+{
+ m_nMode = STREAM_READ;
+ if( r.IsWritable() )
+ m_nMode = STREAM_READ | STREAM_WRITE;
+ if( r.GetError() == SVSTREAM_OK )
+ {
+ pIo->SetStrm( &r, FALSE );
+ ULONG nSize = r.Seek( STREAM_SEEK_TO_END );
+ r.Seek( 0L );
+ // Initializing is OK if the stream is empty
+ Init( BOOL( nSize == 0 ) );
+ if( pEntry )
+ {
+ pEntry->bDirect = bDirect;
+ pEntry->nMode = m_nMode;
+ }
+ pIo->MoveError( *this );
+ }
+ else
+ {
+ SetError( r.GetError() );
+ pEntry = NULL;
+ }
+}
+
+
+Storage::Storage( UCBStorageStream& rStrm, BOOL bDirect )
+ : OLEStorageBase( new StgIo, NULL, m_nMode ), bIsRoot( FALSE )
+{
+ m_nMode = STREAM_READ;
+
+ if ( rStrm.GetError() != SVSTREAM_OK )
+ {
+ SetError( rStrm.GetError() );
+ pEntry = NULL;
+ return;
+ }
+
+ SvStream* pStream = rStrm.GetModifySvStream();
+ if ( !pStream )
+ {
+ OSL_ENSURE( FALSE, "UCBStorageStream can not provide SvStream implementation!\n" );
+ SetError( SVSTREAM_GENERALERROR );
+ pEntry = NULL;
+ return;
+ }
+
+ if( pStream->IsWritable() )
+ m_nMode = STREAM_READ | STREAM_WRITE;
+
+ pIo->SetStrm( &rStrm );
+
+ ULONG nSize = pStream->Seek( STREAM_SEEK_TO_END );
+ pStream->Seek( 0L );
+ // Initializing is OK if the stream is empty
+ Init( BOOL( nSize == 0 ) );
+ if( pEntry )
+ {
+ pEntry->bDirect = bDirect;
+ pEntry->nMode = m_nMode;
+ }
+
+ pIo->MoveError( *this );
+}
+
+
+// Perform common code for both ctors above.
+
+void Storage::Init( BOOL bCreate )
+{
+ pEntry = NULL;
+ BOOL bHdrLoaded = FALSE;
+ bIsRoot = TRUE;
+ if( pIo->Good() )
+ {
+ ULONG nSize = pIo->GetStrm()->Seek( STREAM_SEEK_TO_END );
+ pIo->GetStrm()->Seek( 0L );
+ if( nSize )
+ {
+ bHdrLoaded = pIo->Load();
+ if( !bHdrLoaded && !bCreate )
+ {
+ // File is not a storage and not empty; do not destroy!
+ SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return;
+ }
+ }
+ }
+ // file is a storage, empty or should be overwritten
+ pIo->ResetError();
+ // we have to set up the data structures, since
+ // the file is empty
+ if( !bHdrLoaded )
+ pIo->Init();
+ if( pIo->Good() )
+ {
+ pEntry = pIo->pTOC->GetRoot();
+ pEntry->nRefCnt++;
+ }
+}
+
+// Internal ctor
+
+Storage::Storage( StgIo* p, StgDirEntry* q, StreamMode m )
+ : OLEStorageBase( p, q, m_nMode ), bIsRoot( FALSE )
+{
+ if( q )
+ q->aEntry.GetName( aName );
+ else
+ m &= ~STREAM_READWRITE;
+ m_nMode = m;
+ if( q && q->nRefCnt == 1 )
+ q->nMode = m;
+}
+
+Storage::~Storage()
+{
+ // Invalidate all open substorages
+ if( m_bAutoCommit )
+ Commit();
+ if( pEntry )
+ {
+ // Do an auto-commit if the entry is open in direct mode
+ if( pEntry->nRefCnt && pEntry->bDirect && (m_nMode & STREAM_WRITE) )
+ Commit();
+ if( pEntry->nRefCnt == 1 )
+ pEntry->Invalidate();
+ }
+ // close the stream is root storage
+ if( bIsRoot )
+ pIo->Close();
+ // remove the file if temporary root storage
+ if( bIsRoot && pEntry && pEntry->bTemp )
+ {
+ osl::File::remove( GetName() );
+ }
+}
+
+const String& Storage::GetName() const
+{
+ if( !bIsRoot && Validate() )
+ pEntry->aEntry.GetName( ((Storage*) this)->aName );
+ return aName;
+}
+
+// Fill in the info list for this storage
+
+void Storage::FillInfoList( SvStorageInfoList* pList ) const
+{
+ if( Validate() )
+ {
+ StgIterator aIter( *pEntry );
+ StgDirEntry* p = aIter.First();
+ while( p )
+ {
+ if( !p->bInvalid )
+ {
+ SvStorageInfo aInfo( *p );
+ pList->Append( aInfo );
+ }
+ p = aIter.Next();
+ }
+ }
+}
+
+// Open or create a substorage
+
+BaseStorage* Storage::OpenUCBStorage( const String& rName, StreamMode m, BOOL bDirect )
+{
+ DBG_ERROR("Not supported!");
+/*
+ BaseStorage* pStorage = new Storage( pIo, NULL, m );
+ SetError( ERRCODE_IO_NOTSUPPORTED );
+ return pStorage;
+ */
+ return OpenStorage( rName, m, bDirect );
+}
+
+BaseStorage* Storage::OpenOLEStorage( const String& rName, StreamMode m, BOOL bDirect )
+{
+ return OpenStorage( rName, m, bDirect );
+}
+
+BaseStorage* Storage::OpenStorage( const String& rName, StreamMode m, BOOL bDirect )
+{
+ if( !Validate() || !ValidateMode( m ) )
+ return new Storage( pIo, NULL, m );
+ BOOL bSetAutoCommit = FALSE;
+ if( bDirect && !pEntry->bDirect )
+ {
+ bSetAutoCommit = TRUE;
+ bDirect = FALSE;
+ }
+
+ StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName );
+ if( !p )
+ {
+ if( !( m & STREAM_NOCREATE ) )
+ {
+ BOOL bTemp = FALSE;
+ // create a new storage
+ String aNewName = rName;
+ if( !aNewName.Len() )
+ {
+ aNewName.AssignAscii( "Temp Stg " );
+ aNewName.Append( String::CreateFromInt32( ++nTmpCount ) );
+ bTemp = TRUE;
+ }
+ p = pIo->pTOC->Create( *pEntry, aNewName, STG_STORAGE );
+ if( p )
+ p->bTemp = bTemp;
+ }
+ if( !p )
+ pIo->SetError( ( m & STREAM_WRITE )
+ ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
+ }
+ else if( !ValidateMode( m, p ) )
+ p = NULL;
+ if( p && p->aEntry.GetType() != STG_STORAGE )
+ {
+ pIo->SetError( SVSTREAM_FILE_NOT_FOUND );
+ p = NULL;
+ }
+
+ // Either direct or transacted mode is supported
+ if( p && pEntry->nRefCnt == 1 )
+ p->bDirect = bDirect;
+
+ // Dont check direct conflict if opening readonly
+ if( p && (m & STREAM_WRITE ))
+ {
+ if( p->bDirect != bDirect )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ }
+ Storage* pStg = new Storage( pIo, p, m );
+ pIo->MoveError( *pStg );
+ if( m & STREAM_WRITE ) pStg->m_bAutoCommit = TRUE;
+ return pStg;
+}
+
+// Open a stream
+
+BaseStorageStream* Storage::OpenStream( const String& rName, StreamMode m, BOOL,
+const ByteString*
+#ifdef DBG_UTIL
+pB
+#endif
+)
+{
+ DBG_ASSERT(!pB, "Encryption not supported");
+
+ if( !Validate() || !ValidateMode( m ) )
+ return new StorageStream( pIo, NULL, m );
+ StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName );
+ BOOL bTemp = FALSE;
+ if( !p )
+ {
+ if( !( m & STREAM_NOCREATE ) )
+ {
+ // create a new stream
+ // make a name if the stream is temporary (has no name)
+ String aNewName( rName );
+ if( !aNewName.Len() )
+ {
+ aNewName.AssignAscii( "Temp Strm " );
+ aNewName.Append( String::CreateFromInt32( ++nTmpCount ) );
+ bTemp = TRUE;
+ }
+ p = pIo->pTOC->Create( *pEntry, aNewName, STG_STREAM );
+ }
+ if( !p )
+ pIo->SetError( ( m & STREAM_WRITE )
+ ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
+ }
+ else if( !ValidateMode( m, p ) )
+ p = NULL;
+ if( p && p->aEntry.GetType() != STG_STREAM )
+ {
+ pIo->SetError( SVSTREAM_FILE_NOT_FOUND );
+ p = NULL;
+ }
+ if( p )
+ {
+ p->bTemp = bTemp;
+ p->bDirect = pEntry->bDirect;
+ }
+ StorageStream* pStm = new StorageStream( pIo, p, m );
+ if( p && !p->bDirect )
+ pStm->SetAutoCommit( TRUE );
+ pIo->MoveError( *pStm );
+ return pStm;
+}
+
+// Delete a stream or substorage by setting the temp bit.
+
+BOOL Storage::Remove( const String& rName )
+{
+ if( !Validate( TRUE ) )
+ return FALSE;
+ StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName );
+ if( p )
+ {
+ p->Invalidate( TRUE );
+ return TRUE;
+ }
+ else
+ {
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+ }
+}
+
+// Rename a storage element
+
+BOOL Storage::Rename( const String& rOld, const String& rNew )
+{
+ if( Validate( TRUE ) )
+ {
+ BOOL b = pIo->pTOC->Rename( *pEntry, rOld, rNew );
+ pIo->MoveError( *this );
+ return b;
+ }
+ else
+ return FALSE;
+}
+
+// Copy one element
+
+BOOL Storage::CopyTo( const String& rElem, BaseStorage* pDest, const String& rNew )
+{
+ if( !Validate() || !pDest || !pDest->Validate( TRUE ) )
+ return FALSE;
+ StgDirEntry* pElem = pIo->pTOC->Find( *pEntry, rElem );
+ if( pElem )
+ {
+ /*
+ this lines are misterious !!! MM
+ if( !pElem->IsContained( pDest->pEntry ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ */
+ if( pElem->aEntry.GetType() == STG_STORAGE )
+ {
+ // copy the entire storage
+ BaseStorage* p1 = OpenStorage( rElem, INTERNAL_MODE );
+ BaseStorage* p2 = pDest->OpenOLEStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pEntry->bDirect );
+
+ ULONG nTmpErr = p2->GetError();
+ if( !nTmpErr )
+ {
+ p2->SetClassId( p1->GetClassId() );
+ p1->CopyTo( p2 );
+ SetError( p1->GetError() );
+
+ nTmpErr = p2->GetError();
+ if( !nTmpErr )
+ p2->Commit();
+ else
+ pDest->SetError( nTmpErr );
+ }
+ else
+ pDest->SetError( nTmpErr );
+
+ delete p1;
+ delete p2;
+ return BOOL( Good() && pDest->Good() );
+ }
+ else
+ {
+ // stream copy
+ BaseStorageStream* p1 = OpenStream( rElem, INTERNAL_MODE );
+ BaseStorageStream* p2 = pDest->OpenStream( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pEntry->bDirect );
+
+ ULONG nTmpErr = p2->GetError();
+ if( !nTmpErr )
+ {
+ p1->CopyTo( p2 );
+ SetError( p1->GetError() );
+
+ nTmpErr = p2->GetError();
+ if( !nTmpErr )
+ p2->Commit();
+ else
+ pDest->SetError( nTmpErr );
+ }
+ else
+ pDest->SetError( nTmpErr );
+
+ delete p1;
+ delete p2;
+ return BOOL( Good() && pDest->Good() );
+ }
+ }
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+}
+
+BOOL Storage::CopyTo( BaseStorage* pDest ) const
+{
+ if( !Validate() || !pDest || !pDest->Validate( TRUE ) || Equals( *pDest ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ Storage* pThis = (Storage*) this;
+ /*
+ if( !pThis->pEntry->IsContained( pDest->pEntry ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ */
+ pDest->SetClassId( GetClassId() );
+ pDest->SetDirty();
+ SvStorageInfoList aList;
+ FillInfoList( &aList );
+ BOOL bRes = TRUE;
+ for( USHORT i = 0; i < aList.Count() && bRes; i++ )
+ {
+ SvStorageInfo& rInfo = aList.GetObject( i );
+ bRes = pThis->CopyTo( rInfo.GetName(), pDest, rInfo.GetName() );
+ }
+ if( !bRes )
+ SetError( pDest->GetError() );
+ return BOOL( Good() && pDest->Good() );
+}
+
+// Move one element
+
+BOOL Storage::MoveTo( const String& rElem, BaseStorage* pODest, const String& rNew )
+{
+ if( !Validate() || !pODest || !pODest->Validate( TRUE ) || Equals( *pODest ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+
+ StgDirEntry* pElem = pIo->pTOC->Find( *pEntry, rElem );
+ if( pElem )
+ {
+ // Simplest case: both storages share the same file
+ BOOL bRes;
+ Storage *pOther = PTR_CAST( Storage, pODest );
+ if( pOther && pIo == pOther->pIo && rElem == rNew )
+ {
+ Storage *p = (Storage*) pODest;
+ Storage *pDest = p;
+ // both storages are conventional storages, use implementation dependent code
+ if( !pElem->IsContained( pDest->pEntry ) )
+ {
+ // cyclic move
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ bRes = pIo->pTOC->Move( *pEntry, *pDest->pEntry, rNew );
+ if( !bRes )
+ {
+ pIo->MoveError( *this );
+ pDest->pIo->MoveError( *pDest );
+ ULONG nErr = GetError();
+ if( !nErr )
+ nErr = pDest->GetError();
+ SetError( nErr );
+ pDest->SetError( nErr );
+ }
+ }
+ else
+ {
+ bRes = CopyTo( rElem, pODest, rNew );
+ if( bRes )
+ bRes = Remove( rElem );
+ }
+ if( !bRes )
+ SetError( pIo->GetError() );
+ return bRes;
+ }
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+}
+
+BOOL Storage::IsStorage( const String& rName ) const
+{
+ if( Validate() )
+ {
+ StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName );
+ if( p )
+ return BOOL( p->aEntry.GetType() == STG_STORAGE );
+ }
+ return FALSE;
+}
+
+BOOL Storage::IsStream( const String& rName ) const
+{
+ if( Validate() )
+ {
+ StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName );
+ if( p )
+ return BOOL( p->aEntry.GetType() == STG_STREAM );
+ }
+ return FALSE;
+}
+
+BOOL Storage::IsContained( const String& rName ) const
+{
+ if( Validate() )
+ return BOOL( pIo->pTOC->Find( *pEntry, rName ) != NULL );
+ else
+ return FALSE;
+}
+
+// Commit all sub-elements within this storage. If this is
+// the root, commit the FAT, the TOC and the header as well.
+
+BOOL Storage::Commit()
+{
+ BOOL bRes = TRUE;
+ if( !Validate() )
+ return FALSE;
+ if( !( m_nMode & STREAM_WRITE ) )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE;
+ }
+ else
+ {
+ // Also commit the sub-streams and Storages
+ StgIterator aIter( *pEntry );
+ for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
+ bRes = p->Commit();
+ if( bRes && bIsRoot )
+ {
+ bRes = pEntry->Commit();
+ if( bRes )
+ bRes = pIo->CommitAll();
+ }
+ pIo->MoveError( *this );
+ }
+ return bRes;
+}
+
+BOOL Storage::Revert()
+{
+ return TRUE;
+}
+
+///////////////////////////// OLE Support ////////////////////////////////
+
+// Set the storage type
+
+void Storage::SetClass( const SvGlobalName & rClass,
+ ULONG nOriginalClipFormat,
+ const String & rUserTypeName )
+{
+ if( Validate( TRUE ) )
+ {
+ // set the class name in the root entry
+ pEntry->aEntry.SetClassId( (const ClsId&) rClass.GetCLSID() );
+ pEntry->SetDirty();
+ // then create the streams
+ StgCompObjStream aCompObj( *this, TRUE );
+ aCompObj.GetClsId() = (const ClsId&) rClass.GetCLSID();
+ aCompObj.GetCbFormat() = nOriginalClipFormat;
+ aCompObj.GetUserName() = rUserTypeName;
+ if( !aCompObj.Store() )
+ SetError( aCompObj.GetError() );
+ else
+ {
+ StgOleStream aOle( *this, STREAM_WRITE );
+ if( !aOle.Store() )
+ SetError( aOle.GetError() );
+ }
+ }
+ else
+ SetError( SVSTREAM_ACCESS_DENIED );
+}
+
+void Storage::SetConvertClass( const SvGlobalName & rConvertClass,
+ ULONG nOriginalClipFormat,
+ const String & rUserTypeName )
+{
+ if( Validate( TRUE ) )
+ {
+ SetClass( rConvertClass, nOriginalClipFormat, rUserTypeName );
+ // plus the convert flag:
+ StgOleStream aOle( *this, TRUE );
+ aOle.GetFlags() |= 4;
+ if( !aOle.Store() )
+ SetError( aOle.GetError() );
+ }
+}
+
+SvGlobalName Storage::GetClassName()
+{
+ StgCompObjStream aCompObj( *this, FALSE );
+ if( aCompObj.Load() )
+ return SvGlobalName( (const CLSID&) aCompObj.GetClsId() );
+ pIo->ResetError();
+
+ if ( pEntry )
+ return SvGlobalName( (const CLSID&) pEntry->aEntry.GetClassId() );
+
+ return SvGlobalName();
+}
+
+ULONG Storage::GetFormat()
+{
+ StgCompObjStream aCompObj( *this, FALSE );
+ if( aCompObj.Load() )
+ return aCompObj.GetCbFormat();
+ pIo->ResetError();
+ return 0;
+}
+
+String Storage::GetUserName()
+{
+ StgCompObjStream aCompObj( *this, FALSE );
+ if( aCompObj.Load() )
+ return aCompObj.GetUserName();
+ pIo->ResetError();
+ return String();
+}
+
+BOOL Storage::ShouldConvert()
+{
+ StgOleStream aOle( *this, FALSE );
+ if( aOle.Load() )
+ return BOOL( ( aOle.GetFlags() & 4 ) != 0 );
+ else
+ {
+ pIo->ResetError();
+ return FALSE;
+ }
+}
+
+BOOL Storage::ValidateFAT()
+{
+ Link aLink = StgIo::GetErrorLink();
+ ErrCode nErr = pIo->ValidateFATs();
+ StgIo::SetErrorLink( aLink );
+ return nErr == ERRCODE_NONE;
+}
+
+void Storage::SetDirty()
+{
+ pEntry->SetDirty();
+}
+
+void Storage::SetClassId( const ClsId& rId )
+{
+ pEntry->aEntry.SetClassId( rId );
+}
+
+const ClsId& Storage::GetClassId() const
+{
+ return pEntry->aEntry.GetClassId();
+}
+
+const SvStream* Storage::GetSvStream() const
+{
+ return GetSvStream_Impl();
+}
+
+BOOL Storage::Validate( BOOL bValidate ) const
+{
+ BOOL bRet = Validate_Impl( bValidate );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+BOOL Storage::ValidateMode( StreamMode nMode ) const
+{
+ BOOL bRet = ValidateMode_Impl( nMode );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+BOOL Storage::ValidateMode( StreamMode nMode, StgDirEntry* p ) const
+{
+ BOOL bRet = ValidateMode_Impl( nMode, p );
+ if ( !bRet )
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return bRet;
+}
+
+BOOL Storage::Equals( const BaseStorage& rStorage ) const
+{
+ const Storage* pOther = PTR_CAST( Storage, &rStorage );
+ return pOther && ( pOther->pEntry == pEntry );
+}
+
+
diff --git a/sot/source/sdstor/stgavl.cxx b/sot/source/sdstor/stgavl.cxx
new file mode 100644
index 000000000000..3542a965da33
--- /dev/null
+++ b/sot/source/sdstor/stgavl.cxx
@@ -0,0 +1,419 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+
+#include "stgavl.hxx"
+
+StgAvlNode::StgAvlNode()
+{
+ pLeft = pRight = NULL;
+ nBalance = nId = 0;
+}
+
+StgAvlNode::~StgAvlNode()
+{
+ delete pLeft;
+ delete pRight;
+}
+
+StgAvlNode* StgAvlNode::Find( StgAvlNode* pFind )
+{
+ StgAvlNode* p = this;
+ while( p )
+ {
+ short nRes = p->Compare( pFind );
+ if( !nRes )
+ return p;
+ else p = ( nRes < 0 ) ? p->pLeft : p->pRight;
+ }
+ return NULL;
+}
+
+// find point to add node to AVL tree and returns
+// +/0/- for >/=/< previous
+
+short StgAvlNode::Locate
+ ( StgAvlNode* pFind,
+ StgAvlNode** pPivot, StgAvlNode **pParent, StgAvlNode** pPrev )
+{
+ short nRes = 0;
+ StgAvlNode* pCur = this;
+ *pParent = *pPrev = NULL;
+ *pPivot = this;
+
+ // search tree for insertion point
+
+ while( pCur != NULL )
+ {
+ // check for pPivot
+ if( pCur->nBalance != 0 )
+ *pPivot = pCur, *pParent = *pPrev;
+ // save pPrev location and see what direction to go
+ *pPrev = pCur;
+ nRes = pCur->Compare( pFind );
+ if( nRes == 0 )
+ break;
+ else pCur = ( nRes < 0 ) ? pCur->pLeft : pCur->pRight;
+ }
+ return( nRes );
+}
+
+// adjust balance factors in AVL tree from pivot down.
+// Returns delta balance.
+
+short StgAvlNode::Adjust( StgAvlNode** pHeavy, StgAvlNode* pNew )
+{
+ StgAvlNode* pCur = this;
+ short nDelta;
+ // no traversing
+ if( pCur == pNew )
+ return nBalance;
+ short nRes = Compare( pNew );
+ if( nRes > 0 )
+ {
+ *pHeavy = pCur = pRight;
+ nDelta = -1;
+ }
+ else
+ {
+ *pHeavy = pCur = pLeft;
+ nDelta = 1;
+ }
+ nBalance = 0;
+ while( pCur != pNew )
+ {
+ nRes = pCur->Compare( pNew );
+ if( nRes > 0 )
+ {
+ // height of right increases by 1
+ pCur->nBalance = -1;
+ pCur = pCur->pRight;
+ }
+ else
+ {
+ // height of left increases by 1
+ pCur->nBalance = 1;
+ pCur = pCur->pLeft;
+ }
+ }
+ nBalance = nBalance + nDelta;
+ return nDelta;
+}
+
+// perform LL rotation and return new root
+
+StgAvlNode* StgAvlNode::RotLL()
+{
+ StgAvlNode *pHeavy = pLeft;
+ pLeft = pHeavy->pRight;
+ pHeavy->pRight = this;
+ pHeavy->nBalance = nBalance = 0;
+ return pHeavy;
+}
+
+// perform LR rotation and return new root
+
+StgAvlNode* StgAvlNode::RotLR()
+{
+
+ StgAvlNode* pHeavy = pLeft;
+ StgAvlNode* pNewRoot = pHeavy->pRight;
+
+ pHeavy->pRight = pNewRoot->pLeft;
+ pLeft = pNewRoot->pRight;
+ pNewRoot->pLeft = pHeavy;
+ pNewRoot->pRight = this;
+
+ switch( pNewRoot->nBalance )
+ {
+ case 1: // LR( b )
+ nBalance = -1;
+ pHeavy->nBalance = 0;
+ break;
+ case -1: // LR( c )
+ pHeavy->nBalance = 1;
+ nBalance = 0;
+ break;
+ case 0: // LR( a )
+ nBalance = 0;
+ pHeavy->nBalance = 0;
+ break;
+ }
+ pNewRoot->nBalance = 0;
+ return pNewRoot;
+}
+
+// perform RR rotation and return new root
+
+StgAvlNode* StgAvlNode::RotRR()
+{
+ StgAvlNode* pHeavy = pRight;
+ pRight = pHeavy->pLeft;
+ pHeavy->pLeft = this;
+ nBalance = pHeavy->nBalance = 0;
+ return pHeavy;
+}
+
+// perform the RL rotation and return the new root
+
+StgAvlNode* StgAvlNode::RotRL()
+{
+ StgAvlNode* pHeavy = pRight;
+ StgAvlNode* pNewRoot = pHeavy->pLeft;
+ pHeavy->pLeft = pNewRoot->pRight;
+ pRight = pNewRoot->pLeft;
+ pNewRoot->pRight = pHeavy;
+ pNewRoot->pLeft = this;
+ switch( pNewRoot->nBalance )
+ {
+ case -1: // RL( b )
+ nBalance = 1;
+ pHeavy->nBalance = 0;
+ break;
+ case 1: // RL( c )
+ pHeavy->nBalance = -1;
+ nBalance = 0;
+ break;
+ case 0: // RL( a )
+ nBalance = 0;
+ pHeavy->nBalance = 0;
+ break;
+ }
+ pNewRoot->nBalance = 0;
+ return pNewRoot;
+}
+
+// Remove a tree element. Return the removed element or NULL.
+
+StgAvlNode* StgAvlNode::Rem( StgAvlNode** p, StgAvlNode* pDel, BOOL bPtrs )
+{
+ if( *p )
+ {
+ StgAvlNode* pCur = *p;
+ short nRes = bPtrs ? short( pCur == pDel ) : short(pCur->Compare( pDel ));
+ if( !nRes )
+ {
+ // Element found: remove
+ if( !pCur->pRight )
+ {
+ *p = pCur->pLeft; pCur->pLeft = NULL;
+ }
+ else if( !pCur->pLeft )
+ {
+ *p = pCur->pRight; pCur->pRight = NULL;
+ }
+ else
+ {
+ // The damn element has two leaves. Get the
+ // rightmost element of the left subtree (which
+ // is lexically before this element) and replace
+ // this element with the element found.
+ StgAvlNode* last = pCur;
+ StgAvlNode* l;
+ for( l = pCur->pLeft;
+ l->pRight; last = l, l = l->pRight ) {}
+ // remove the element from chain
+ if( l == last->pRight )
+ last->pRight = l->pLeft;
+ else
+ last->pLeft = l->pLeft;
+ // perform the replacement
+ l->pLeft = pCur->pLeft;
+ l->pRight = pCur->pRight;
+ *p = l;
+ // delete the element
+ pCur->pLeft = pCur->pRight = NULL;
+ }
+ return pCur;
+ }
+ else
+ {
+ if( nRes < 0 )
+ return Rem( &pCur->pLeft, pDel, bPtrs );
+ else
+ return Rem( &pCur->pRight, pDel, bPtrs );
+ }
+ }
+ return NULL;
+}
+
+// Enumerate the tree for later iteration
+
+void StgAvlNode::StgEnum( short& n )
+{
+ if( this )
+ {
+ if( pLeft )
+ pLeft->StgEnum( n );
+ nId = n++;
+ if( pRight )
+ pRight->StgEnum( n );
+ }
+}
+
+// Add node to AVL tree.
+// Return FALSE if the element already exists.
+
+BOOL StgAvlNode::Insert( StgAvlNode** pRoot, StgAvlNode* pIns )
+{
+ StgAvlNode* pPivot, *pHeavy, *pNewRoot, *pParent, *pPrev;
+ // special case - empty tree
+ if( *pRoot == NULL )
+ {
+ *pRoot = pIns;
+ return TRUE;
+ }
+ // find insertion point and return if already present
+ short nRes = (*pRoot)->Locate( pIns, &pPivot, &pParent, &pPrev );
+ if( !nRes )
+ return FALSE;
+ // add new node
+ if( nRes < 0 )
+ pPrev->pLeft = pIns;
+ else
+ pPrev->pRight = pIns;
+ // rebalance tree
+ short nDelta = pPivot->Adjust( &pHeavy, pIns );
+ if( pPivot->nBalance >= 2 || pPivot->nBalance <= -2 )
+ {
+ pHeavy = ( nDelta < 0 ) ? pPivot->pRight : pPivot->pLeft;
+ // left imbalance
+ if( nDelta > 0 )
+ if( pHeavy->nBalance == 1 )
+ pNewRoot = pPivot->RotLL();
+ else
+ pNewRoot = pPivot->RotLR();
+ // right imbalance
+ else if( pHeavy->nBalance == -1 )
+ pNewRoot = pPivot->RotRR();
+ else
+ pNewRoot = pPivot->RotRL();
+ // relink balanced subtree
+ if( pParent == NULL )
+ *pRoot = pNewRoot;
+ else if( pPivot == pParent->pLeft )
+ pParent->pLeft = pNewRoot;
+ else if( pPivot == pParent->pRight )
+ pParent->pRight = pNewRoot;
+ }
+ return TRUE;
+}
+
+// Remove node from tree. Returns TRUE is found and removed.
+// Actually delete if bDel
+
+BOOL StgAvlNode::Remove( StgAvlNode** pRoot, StgAvlNode* pDel, BOOL bDel )
+{
+ // special case - empty tree
+ if( *pRoot == NULL )
+ return FALSE;
+ // delete the element
+ pDel = Rem( pRoot, pDel, FALSE );
+ if( pDel )
+ {
+ if( bDel )
+ delete pDel;
+ // Rebalance the tree the hard way
+ // OS 22.09.95: Auf MD's Wunsch auskommentiert wg. Absturz
+/* StgAvlNode* pNew = NULL;
+ while( *pRoot )
+ {
+ StgAvlNode* p = Rem( pRoot, *pRoot, FALSE );
+ Insert( &pNew, p );
+ }
+ *pRoot = pNew;*/
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+// Move node to a different tree. Returns TRUE is found and moved. This routine
+// may be called when the key has changed.
+
+BOOL StgAvlNode::Move
+ ( StgAvlNode** pRoot1, StgAvlNode** pRoot2, StgAvlNode* pMove )
+{
+ // special case - empty tree
+ if( *pRoot1 == NULL )
+ return FALSE;
+ pMove = Rem( pRoot1, pMove, FALSE );
+ if( pMove )
+ return Insert( pRoot2, pMove );
+ else
+ return FALSE;
+}
+
+////////////////////////// class AvlIterator /////////////////////////
+
+// The iterator walks through a tree one entry by one.
+
+StgAvlIterator::StgAvlIterator( StgAvlNode* p )
+{
+ pRoot = p;
+ nCount = 0;
+ if( p )
+ p->StgEnum( nCount );
+}
+
+StgAvlNode* StgAvlIterator::Find( short n )
+{
+ StgAvlNode* p = pRoot;
+ while( p )
+ {
+ if( n == p->nId )
+ break;
+ else p = ( n < p->nId ) ? p->pLeft : p->pRight;
+ }
+ return p;
+}
+
+StgAvlNode* StgAvlIterator::First()
+{
+ nCur = -1;
+ return Next();
+}
+
+StgAvlNode* StgAvlIterator::Last()
+{
+ nCur = nCount;
+ return Prev();
+}
+
+StgAvlNode* StgAvlIterator::Next()
+{
+ return Find( ++nCur );
+}
+
+StgAvlNode* StgAvlIterator::Prev()
+{
+ return Find( --nCur );
+}
+
diff --git a/sot/source/sdstor/stgavl.hxx b/sot/source/sdstor/stgavl.hxx
new file mode 100644
index 000000000000..66f424888e6d
--- /dev/null
+++ b/sot/source/sdstor/stgavl.hxx
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _STGAVL_HXX
+#define _STGAVL_HXX
+
+#ifndef _TOOLS_SOLAR_H
+#include <tools/solar.h>
+#endif
+
+// This class must be overloaded to define real, living nodes.
+// Especially, the compare function must be implemented.
+
+class StgAvlNode
+{
+ friend class StgAvlIterator;
+private:
+ short Locate( StgAvlNode*, StgAvlNode**, StgAvlNode**, StgAvlNode** );
+ short Adjust( StgAvlNode**, StgAvlNode* );
+ StgAvlNode* RotLL();
+ StgAvlNode* RotLR();
+ StgAvlNode* RotRR();
+ StgAvlNode* RotRL();
+ void StgEnum( short& );
+ static StgAvlNode* Rem( StgAvlNode**, StgAvlNode*, BOOL );
+protected:
+ short nId; // iterator ID
+ short nBalance; // indicates tree balance
+ StgAvlNode* pLeft, *pRight; // leaves
+ StgAvlNode();
+public:
+ virtual ~StgAvlNode();
+ StgAvlNode* Find( StgAvlNode* );
+ static BOOL Insert( StgAvlNode**, StgAvlNode* );
+ static BOOL Remove( StgAvlNode**, StgAvlNode*, BOOL bDel = TRUE );
+ static BOOL Move( StgAvlNode**, StgAvlNode**, StgAvlNode* );
+ virtual short Compare( const StgAvlNode* ) const = 0;
+};
+
+// The iterator class provides single stepping through an AVL tree.
+
+class StgAvlIterator {
+ StgAvlNode* pRoot; // root entry (parent)
+ short nCount; // tree size
+ short nCur; // current element
+ StgAvlNode* Find( short );
+public:
+ StgAvlIterator( StgAvlNode* );
+ StgAvlNode* First();
+ StgAvlNode* Last();
+ StgAvlNode* Next();
+ StgAvlNode* Prev();
+};
+
+#endif
diff --git a/sot/source/sdstor/stgcache.cxx b/sot/source/sdstor/stgcache.cxx
new file mode 100644
index 000000000000..88a8187ee9dd
--- /dev/null
+++ b/sot/source/sdstor/stgcache.cxx
@@ -0,0 +1,546 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#if defined(_MSC_VER) && (_MSC_VER<1200)
+#include <tools/presys.h>
+#endif
+#include <hash_map>
+#if defined(_MSC_VER) && (_MSC_VER<1200)
+#include <tools/postsys.h>
+#endif
+#include <vos/macros.hxx>
+
+#include <string.h>
+#include <osl/endian.h>
+#include <tools/string.hxx>
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+
+/*************************************************************************/
+//-----------------------------------------------------------------------------
+typedef std::hash_map
+<
+ INT32,
+ StgPage *,
+ std::hash< INT32 >,
+ NAMESPACE_STD(equal_to)< INT32 >
+> UsrStgPagePtr_Impl;
+#ifdef _MSC_VER
+#pragma warning( disable: 4786 )
+#endif
+
+//#define CHECK_DIRTY 1
+//#define READ_AFTER_WRITE 1
+
+////////////////////////////// class StgPage /////////////////////////////
+// This class implements buffer functionality. The cache will always return
+// a page buffer, even if a read fails. It is up to the caller to determine
+// the correctness of the I/O.
+
+StgPage::StgPage( StgCache* p, short n )
+{
+ pCache = p;
+ nData = n;
+ bDirty = FALSE;
+ nPage = 0;
+ pData = new BYTE[ nData ];
+ pNext1 =
+ pNext2 =
+ pLast1 =
+ pLast2 = NULL;
+ pOwner = NULL;
+}
+
+StgPage::~StgPage()
+{
+ delete [] pData;
+}
+
+void StgPage::SetPage( short nOff, INT32 nVal )
+{
+ if( ( nOff < (short) ( nData / sizeof( INT32 ) ) ) && nOff >= 0 )
+ {
+#ifdef OSL_BIGENDIAN
+ nVal = SWAPLONG(nVal);
+#endif
+ ((INT32*) pData )[ nOff ] = nVal;
+ bDirty = TRUE;
+ }
+}
+
+//////////////////////////////// class StgCache ////////////////////////////
+
+// The disk cache holds the cached sectors. The sector type differ according
+// to their purpose.
+
+INT32 lcl_GetPageCount( ULONG nFileSize, short nPageSize )
+{
+// return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
+ // #i61980# reallife: last page may be incomplete, return number of *started* pages
+ return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
+}
+
+StgCache::StgCache()
+{
+ nRef = 0;
+ pStrm = NULL;
+ pCur = pElem1 = NULL;
+ nPageSize = 512;
+ nError = SVSTREAM_OK;
+ bMyStream = FALSE;
+ bFile = FALSE;
+ pLRUCache = NULL;
+ pStorageStream = NULL;
+}
+
+StgCache::~StgCache()
+{
+ Clear();
+ SetStrm( NULL, FALSE );
+ delete (UsrStgPagePtr_Impl*)pLRUCache;
+}
+
+void StgCache::SetPhysPageSize( short n )
+{
+ nPageSize = n;
+ ULONG nPos = pStrm->Tell();
+ ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
+ nPages = lcl_GetPageCount( nFileSize, nPageSize );
+ pStrm->Seek( nPos );
+}
+
+// Create a new cache element
+// pCur points to this element
+
+StgPage* StgCache::Create( INT32 nPg )
+{
+ StgPage* pElem = new StgPage( this, nPageSize );
+ pElem->nPage = nPg;
+ // For data security, clear the buffer contents
+ memset( pElem->pData, 0, pElem->nData );
+
+ // insert to LRU
+ if( pCur )
+ {
+ pElem->pNext1 = pCur;
+ pElem->pLast1 = pCur->pLast1;
+ pElem->pNext1->pLast1 =
+ pElem->pLast1->pNext1 = pElem;
+ }
+ else
+ pElem->pNext1 = pElem->pLast1 = pElem;
+ if( !pLRUCache )
+ pLRUCache = new UsrStgPagePtr_Impl();
+ (*(UsrStgPagePtr_Impl*)pLRUCache)[pElem->nPage] = pElem;
+ pCur = pElem;
+
+ // insert to Sorted
+ if( !pElem1 )
+ pElem1 = pElem->pNext2 = pElem->pLast2 = pElem;
+ else
+ {
+ StgPage* p = pElem1;
+ do
+ {
+ if( pElem->nPage < p->nPage )
+ break;
+ p = p->pNext2;
+ } while( p != pElem1 );
+ pElem->pNext2 = p;
+ pElem->pLast2 = p->pLast2;
+ pElem->pNext2->pLast2 =
+ pElem->pLast2->pNext2 = pElem;
+ if( p->nPage < pElem1->nPage )
+ pElem1 = pElem;
+ }
+ return pElem;
+}
+
+// Delete the given element
+
+void StgCache::Erase( StgPage* pElem )
+{
+ //remove from LRU
+ pElem->pNext1->pLast1 = pElem->pLast1;
+ pElem->pLast1->pNext1 = pElem->pNext1;
+ if( pCur == pElem )
+ pCur = ( pElem->pNext1 == pElem ) ? NULL : pElem->pNext1;
+ if( pLRUCache )
+ ((UsrStgPagePtr_Impl*)pLRUCache)->erase( pElem->nPage );
+ // remove from Sorted
+ pElem->pNext2->pLast2 = pElem->pLast2;
+ pElem->pLast2->pNext2 = pElem->pNext2;
+ if( pElem1 == pElem )
+ pElem1 = ( pElem->pNext2 == pElem ) ? NULL : pElem->pNext2;
+ delete pElem;
+}
+
+// remove all cache elements without flushing them
+
+void StgCache::Clear()
+{
+ StgPage* pElem = pCur;
+ if( pCur ) do
+ {
+ StgPage* pDelete = pElem;
+ pElem = pElem->pNext1;
+ delete pDelete;
+ }
+ while( pCur != pElem );
+ pCur = NULL;
+ pElem1 = NULL;
+ delete (UsrStgPagePtr_Impl*)pLRUCache;
+ pLRUCache = NULL;
+}
+
+// Look for a cached page
+
+StgPage* StgCache::Find( INT32 nPage )
+{
+ if( !pLRUCache )
+ return NULL;
+ UsrStgPagePtr_Impl::iterator aIt = ((UsrStgPagePtr_Impl*)pLRUCache)->find( nPage );
+ if( aIt != ((UsrStgPagePtr_Impl*)pLRUCache)->end() )
+ {
+ // page found
+ StgPage* pFound = (*aIt).second;
+
+ if( pFound != pCur )
+ {
+ // remove from LRU
+ pFound->pNext1->pLast1 = pFound->pLast1;
+ pFound->pLast1->pNext1 = pFound->pNext1;
+ // insert to LRU
+ pFound->pNext1 = pCur;
+ pFound->pLast1 = pCur->pLast1;
+ pFound->pNext1->pLast1 =
+ pFound->pLast1->pNext1 = pFound;
+ }
+ return pFound;
+ }
+ return NULL;
+}
+
+// Load a page into the cache
+
+StgPage* StgCache::Get( INT32 nPage, BOOL bForce )
+{
+ StgPage* p = Find( nPage );
+ if( !p )
+ {
+ p = Create( nPage );
+ if( !Read( nPage, p->pData, 1 ) && bForce )
+ {
+ Erase( p );
+ p = NULL;
+ SetError( SVSTREAM_READ_ERROR );
+ }
+ }
+ return p;
+}
+
+// Copy an existing page into a new page. Use this routine
+// to duplicate an existing stream or to create new entries.
+// The new page is initially marked dirty. No owner is copied.
+
+StgPage* StgCache::Copy( INT32 nNew, INT32 nOld )
+{
+ StgPage* p = Find( nNew );
+ if( !p )
+ p = Create( nNew );
+ if( nOld >= 0 )
+ {
+ // old page: we must have this data!
+ StgPage* q = Get( nOld, TRUE );
+ if( q )
+ memcpy( p->pData, q->pData, p->nData );
+ }
+ p->SetDirty();
+ return p;
+}
+
+// Flush the cache whose owner is given. NULL flushes all.
+
+BOOL StgCache::Commit( StgDirEntry* )
+{
+ StgPage* p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext2;
+ } while( p != pElem1 );
+ pStrm->Flush();
+ SetError( pStrm->GetError() );
+#ifdef CHECK_DIRTY
+ p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in Ordered List") ).Execute();
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext2;
+ } while( p != pElem1 );
+ p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in LRU List") ).Execute();
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext1;
+ } while( p != pElem1 );
+#endif
+ return TRUE;
+}
+
+void StgCache::Revert( StgDirEntry* )
+{}
+
+// Set a stream
+
+void StgCache::SetStrm( SvStream* p, BOOL bMy )
+{
+ if( pStorageStream )
+ {
+ pStorageStream->ReleaseRef();
+ pStorageStream = NULL;
+ }
+
+ if( bMyStream )
+ delete pStrm;
+ pStrm = p;
+ bMyStream = bMy;
+}
+
+void StgCache::SetStrm( UCBStorageStream* pStgStream )
+{
+ if( pStorageStream )
+ pStorageStream->ReleaseRef();
+ pStorageStream = pStgStream;
+
+ if( bMyStream )
+ delete pStrm;
+
+ pStrm = NULL;
+
+ if ( pStorageStream )
+ {
+ pStorageStream->AddRef();
+ pStrm = pStorageStream->GetModifySvStream();
+ }
+
+ bMyStream = FALSE;
+}
+
+// Open/close the disk file
+
+BOOL StgCache::Open( const String& rName, StreamMode nMode )
+{
+ // do not open in exclusive mode!
+ if( nMode & STREAM_SHARE_DENYALL )
+ nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
+ SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
+ // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
+ BOOL bAccessDenied = FALSE;
+ if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
+ {
+ pFileStrm->Close();
+ bAccessDenied = TRUE;
+ }
+ SetStrm( pFileStrm, TRUE );
+ if( pFileStrm->IsOpen() )
+ {
+ ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
+ nPages = lcl_GetPageCount( nFileSize, nPageSize );
+ pStrm->Seek( 0L );
+ }
+ else
+ nPages = 0;
+ bFile = TRUE;
+ SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
+ return Good();
+}
+
+void StgCache::Close()
+{
+ if( bFile )
+ {
+ ((SvFileStream*) pStrm)->Close();
+ SetError( pStrm->GetError() );
+ }
+}
+
+// low level I/O
+
+BOOL StgCache::Read( INT32 nPage, void* pBuf, INT32 nPg )
+{
+ if( Good() )
+ {
+ /* #i73846# real life: a storage may refer to a page one-behind the
+ last valid page (see document attached to the issue). In that case
+ (if nPage==nPages), just do nothing here and let the caller work on
+ the empty zero-filled buffer. */
+ if ( nPage > nPages )
+ SetError( SVSTREAM_READ_ERROR );
+ else if ( nPage < nPages )
+ {
+ ULONG nPos = Page2Pos( nPage );
+ INT32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
+ ULONG nBytes = nPg2 * nPageSize;
+ // fixed address and size for the header
+ if( nPage == -1 )
+ {
+ nPos = 0L, nBytes = 512;
+ nPg2 = nPg;
+ }
+ if( pStrm->Tell() != nPos )
+ {
+ if( pStrm->Seek( nPos ) != nPos ) {
+ #ifdef CHECK_DIRTY
+ ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
+ #endif
+ }
+ }
+ pStrm->Read( pBuf, nBytes );
+ if ( nPg != nPg2 )
+ SetError( SVSTREAM_READ_ERROR );
+ else
+ SetError( pStrm->GetError() );
+ }
+ }
+ return Good();
+}
+
+BOOL StgCache::Write( INT32 nPage, void* pBuf, INT32 nPg )
+{
+ if( Good() )
+ {
+ ULONG nPos = Page2Pos( nPage );
+ ULONG nBytes = nPg * nPageSize;
+ // fixed address and size for the header
+ if( nPage == -1 )
+ nPos = 0L, nBytes = 512;
+ if( pStrm->Tell() != nPos )
+ {
+ if( pStrm->Seek( nPos ) != nPos ) {
+#ifdef CHECK_DIRTY
+ ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
+#endif
+ }
+ }
+ ULONG nRes = pStrm->Write( pBuf, nBytes );
+ if( nRes != nBytes )
+ SetError( SVSTREAM_WRITE_ERROR );
+ else
+ SetError( pStrm->GetError() );
+#ifdef READ_AFTER_WRITE
+ BYTE cBuf[ 512 ];
+ pStrm->Flush();
+ pStrm->Seek( nPos );
+ BOOL bRes = ( pStrm->Read( cBuf, 512 ) == 512 );
+ if( bRes )
+ bRes = !memcmp( cBuf, pBuf, 512 );
+ if( !bRes )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute();
+ pStrm->SetError( SVSTREAM_WRITE_ERROR );
+ }
+#endif
+ }
+ return Good();
+}
+
+// set the file size in pages
+
+BOOL StgCache::SetSize( INT32 n )
+{
+ // Add the file header
+ INT32 nSize = n * nPageSize + 512;
+ pStrm->SetStreamSize( nSize );
+ SetError( pStrm->GetError() );
+ if( !nError )
+ nPages = n;
+ return Good();
+}
+
+void StgCache::SetError( ULONG n )
+{
+ if( n && !nError )
+ nError = n;
+}
+
+void StgCache::ResetError()
+{
+ nError = SVSTREAM_OK;
+ pStrm->ResetError();
+}
+
+void StgCache::MoveError( StorageBase& r )
+{
+ if( nError != SVSTREAM_OK )
+ {
+ r.SetError( nError );
+ ResetError();
+ }
+}
+
+// Utility functions
+
+INT32 StgCache::Page2Pos( INT32 nPage )
+{
+ if( nPage < 0 ) nPage = 0;
+ return( nPage * nPageSize ) + nPageSize;
+}
+
+INT32 StgCache::Pos2Page( INT32 nPos )
+{
+ return ( ( nPos + nPageSize - 1 ) / nPageSize ) * nPageSize - 1;
+}
+
diff --git a/sot/source/sdstor/stgcache.hxx b/sot/source/sdstor/stgcache.hxx
new file mode 100644
index 000000000000..5379f837b8b1
--- /dev/null
+++ b/sot/source/sdstor/stgcache.hxx
@@ -0,0 +1,132 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _STGCACHE_HXX
+#define _STGCACHE_HXX
+
+#include <osl/endian.h>
+#ifndef _TOOLS_SOLAR_H
+#include <tools/solar.h>
+#endif
+#ifndef _TOOLS_STREAM_HXX
+#include <tools/stream.hxx>
+#endif
+#include <stgelem.hxx>
+
+class UCBStorageStream;
+
+class StgIo;
+class StgPage;
+class StgDirEntry;
+class StorageBase;
+
+class StgCache {
+ StgPage* pCur; // top of LRU list
+ StgPage* pElem1; // top of ordered list
+ ULONG nError; // error code
+ INT32 nPages; // size of data area in pages
+ USHORT nRef; // reference count
+ void * pLRUCache; // hash table of cached objects
+ short nPageSize; // page size of the file
+ UCBStorageStream* pStorageStream; // holds reference to UCB storage stream
+
+ void Erase( StgPage* ); // delete a cache element
+ void InsertToLRU( StgPage* ); // insert into LRU list
+ void InsertToOrdered( StgPage* ); // insert into ordered list
+ StgPage* Create( INT32 ); // create a cached page
+protected:
+ SvStream* pStrm; // physical stream
+ BOOL bMyStream; // TRUE: delete stream in dtor
+ BOOL bFile; // TRUE: file stream
+ INT32 Page2Pos( INT32 ); // page address --> file position
+ INT32 Pos2Page( INT32 ); // file position --> page address
+public:
+ StgCache();
+ ~StgCache();
+ void IncRef() { nRef++; }
+ USHORT DecRef() { return --nRef; }
+ void SetPhysPageSize( short );
+ INT32 GetPhysPages() { return nPages; }
+ short GetPhysPageSize() { return nPageSize; }
+ SvStream* GetStrm() { return pStrm; }
+ void SetStrm( SvStream*, BOOL );
+ void SetStrm( UCBStorageStream* );
+ BOOL IsWritable() { return pStrm->IsWritable(); }
+ BOOL Good() { return BOOL( nError == SVSTREAM_OK ); }
+ BOOL Bad() { return BOOL( nError != SVSTREAM_OK ); }
+ ULONG GetError() { return nError; }
+ void MoveError( StorageBase& );
+ void SetError( ULONG );
+ void ResetError();
+ BOOL Open( const String& rName, StreamMode );
+ void Close();
+ BOOL Read( INT32 nPage, void* pBuf, INT32 nPages );
+ BOOL Write( INT32 nPage, void* pBuf, INT32 nPages );
+ BOOL SetSize( INT32 nPages );
+ StgPage* Find( INT32 ); // find a cached page
+ StgPage* Get( INT32, BOOL ); // get a cached page
+ StgPage* Copy( INT32, INT32=STG_FREE ); // copy a page
+ BOOL Commit( StgDirEntry* = NULL ); // flush all pages
+ void Revert( StgDirEntry* = NULL ); // revert dirty pages
+ void Clear(); // clear the cache
+};
+
+class StgPage {
+ friend class StgCache;
+ StgCache* pCache; // the cache
+ StgPage *pNext1, *pLast1; // LRU chain
+ StgPage *pNext2, *pLast2; // ordered chain
+ StgDirEntry* pOwner; // owner
+ INT32 nPage; // page #
+ BYTE* pData; // nPageSize characters
+ short nData; // size of this page
+ BOOL bDirty; // dirty flag
+ StgPage( StgCache*, short );
+ ~StgPage();
+public:
+ void SetDirty() { bDirty = TRUE; }
+ INT32 GetPage() { return nPage; }
+ void* GetData() { return pData; }
+ short GetSize() { return nData; }
+ void SetOwner( StgDirEntry* p ) { pOwner = p; }
+ // routines for accessing FAT pages
+ // Assume that the data is a FAT page and get/put FAT data.
+ INT32 GetPage( short nOff )
+ {
+ if( ( nOff >= (short) ( nData / sizeof( INT32 ) ) ) || nOff < 0 )
+ return -1;
+ INT32 n = ((INT32*) pData )[ nOff ];
+#ifdef OSL_BIGENDIAN
+ return SWAPLONG(n);
+#else
+ return n;
+#endif
+ }
+ void SetPage( short, INT32 ); // put an element
+};
+
+#endif
diff --git a/sot/source/sdstor/stgdir.cxx b/sot/source/sdstor/stgdir.cxx
new file mode 100644
index 000000000000..f093dc60cbe7
--- /dev/null
+++ b/sot/source/sdstor/stgdir.cxx
@@ -0,0 +1,1054 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include <string.h> // memcpy()
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+
+
+//////////////////////////// class StgDirEntry /////////////////////////////
+
+// This class holds the dir entry data and maintains dirty flags for both
+// the entry and the data.
+
+// Transacted mode for streams: On the first write, a temp stream pTmpStrm
+// is created and operated on. A commit moves pTmpStrm to pCurStrm, which
+// is used for subsequent reads. A new write creates a new copy of pTmpStrm
+// based on pCurStrm. Reverting throws away pTmpStrm.
+// Transacted mode for storages: A copy of the dir ents is kept in aSave.
+// Committing means copying aEntry to aSave. Reverting means to copy aSave
+// to aEntry, delete newly created entries and to reactivate removed entries.
+
+// Problem der Implementation: Keine Hierarchischen commits. Daher nur
+// insgesamt transaktionsorientert oder direkt.
+
+StgDirEntry::StgDirEntry( const void* pFrom, BOOL * pbOk ) : StgAvlNode()
+{
+ *pbOk = aEntry.Load( pFrom );
+
+ InitMembers();
+}
+
+StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
+{
+ InitMembers();
+}
+
+// Helper for all ctors
+
+void StgDirEntry::InitMembers()
+{
+ aSave = aEntry;
+ pUp =
+ pDown = NULL;
+ ppRoot = NULL;
+ pStgStrm = NULL;
+ pCurStrm =
+ pTmpStrm = NULL;
+ nPos =
+ nEntry =
+ nRefCnt = 0;
+ nMode = STREAM_READ;
+ bDirect = TRUE;
+ bInvalid =
+ bCreated =
+ bRenamed =
+ bRemoved =
+ bTemp =
+ bDirty =
+ bZombie = FALSE;
+}
+
+StgDirEntry::~StgDirEntry()
+{
+ Close();
+ delete pCurStrm;
+ delete pStgStrm;
+ delete pDown;
+}
+
+// Comparison function
+
+short StgDirEntry::Compare( const StgAvlNode* p ) const
+{
+ const StgDirEntry* pEntry = (const StgDirEntry*) p;
+ return aEntry.Compare( pEntry->aEntry );
+}
+
+// Enumerate the entry numbers.
+// n is incremented to show the total # of entries.
+// These number are later used as page numbers when storing
+// the TOC tree into the TOC stream. Remember that aSave is
+// stored, not aEntry.
+
+void StgDirEntry::Enum( INT32& n )
+{
+ INT32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
+ nEntry = n++;
+ if( pLeft )
+ {
+ ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
+ }
+ if( pRight )
+ {
+ ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
+ }
+ if( pDown )
+ {
+ pDown->Enum( n ); nDown = pDown->nEntry;
+ }
+ aSave.SetLeaf( STG_LEFT, nLeft );
+ aSave.SetLeaf( STG_RIGHT, nRight );
+ aSave.SetLeaf( STG_CHILD, nDown );
+}
+
+// Delete all temporary entries before writing the TOC stream.
+// Until now Deltem is never called with bForce True
+
+void StgDirEntry::DelTemp( BOOL bForce )
+{
+ if( pLeft )
+ ((StgDirEntry*) pLeft)->DelTemp( FALSE );
+ if( pRight )
+ ((StgDirEntry*) pRight)->DelTemp( FALSE );
+ if( pDown )
+ {
+ // If the storage is dead, of course all elements are dead, too
+ if( bInvalid && aEntry.GetType() == STG_STORAGE )
+ bForce = TRUE;
+ pDown->DelTemp( bForce );
+ }
+ if( ( bForce || bInvalid )
+ && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
+ {
+ Close();
+ if( pUp )
+ {
+ // this deletes the element if refcnt == 0!
+ BOOL bDel = nRefCnt == 0;
+ StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
+ if( !bDel )
+ {
+ pLeft = pRight = pDown = 0;
+ bInvalid = bZombie = TRUE;
+ }
+ }
+ }
+}
+
+// Save the tree into the given dir stream
+
+BOOL StgDirEntry::Store( StgDirStrm& rStrm )
+{
+ void* pEntry = rStrm.GetEntry( nEntry, TRUE );
+ if( !pEntry )
+ return FALSE;
+ // Do not store the current (maybe not commited) entry
+ aSave.Store( pEntry );
+ if( pLeft )
+ if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
+ return FALSE;
+ if( pRight )
+ if( !((StgDirEntry*) pRight)->Store( rStrm ) )
+ return FALSE;
+ if( pDown )
+ if( !pDown->Store( rStrm ) )
+ return FALSE;
+ return TRUE;
+}
+
+BOOL StgDirEntry::StoreStream( StgIo& rIo )
+{
+ if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
+ {
+ if( bInvalid )
+ {
+ // Delete the stream if needed
+ if( !pStgStrm )
+ {
+ OpenStream( rIo );
+ delete pStgStrm, pStgStrm = NULL;
+ }
+ else
+ pStgStrm->SetSize( 0 );
+ }
+ // or write the data stream
+ else if( !Tmp2Strm() )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// Save all dirty streams
+
+BOOL StgDirEntry::StoreStreams( StgIo& rIo )
+{
+ if( !StoreStream( rIo ) )
+ return FALSE;
+ if( pLeft )
+ if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
+ return FALSE;
+ if( pRight )
+ if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
+ return FALSE;
+ if( pDown )
+ if( !pDown->StoreStreams( rIo ) )
+ return FALSE;
+ return TRUE;
+}
+
+// Revert all directory entries after failure to write the TOC stream
+
+void StgDirEntry::RevertAll()
+{
+ aEntry = aSave;
+ if( pLeft )
+ ((StgDirEntry*) pLeft)->RevertAll();
+ if( pRight )
+ ((StgDirEntry*) pRight)->RevertAll();
+ if( pDown )
+ pDown->RevertAll();
+}
+
+// Look if any element of the tree is dirty
+
+BOOL StgDirEntry::IsDirty()
+{
+ if( bDirty || bInvalid )
+ return TRUE;
+ if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() )
+ return TRUE;
+ if( pRight && ((StgDirEntry*) pRight)->IsDirty() )
+ return TRUE;
+ if( pDown && pDown->IsDirty() )
+ return TRUE;
+ return FALSE;
+}
+
+// Set up a stream.
+
+void StgDirEntry::OpenStream( StgIo& rIo, BOOL bForceBig )
+{
+ INT32 nThreshold = (USHORT) rIo.aHdr.GetThreshold();
+ delete pStgStrm;
+ if( !bForceBig && aEntry.GetSize() < nThreshold )
+ pStgStrm = new StgSmallStrm( rIo, this );
+ else
+ pStgStrm = new StgDataStrm( rIo, this );
+ if( bInvalid && aEntry.GetSize() )
+ {
+ // This entry has invalid data, so delete that data
+ SetSize( 0L );
+// bRemoved = bInvalid = FALSE;
+ }
+ nPos = 0;
+}
+
+// Close the open stream without committing. If the entry is marked as
+// temporary, delete it.
+// Do not delete pCurStrm here!
+// (TLX:??? Zumindest pStgStrm muss deleted werden.)
+
+void StgDirEntry::Close()
+{
+ delete pTmpStrm;
+ pTmpStrm = NULL;
+// nRefCnt = 0;
+ bInvalid = bTemp;
+}
+
+// Get the current stream size
+
+INT32 StgDirEntry::GetSize()
+{
+ INT32 n;
+ if( pTmpStrm )
+ n = pTmpStrm->GetSize();
+ else if( pCurStrm )
+ n = pCurStrm->GetSize();
+ else n = aEntry.GetSize();
+ return n;
+}
+
+// Set the stream size. This means also creating a temp stream.
+
+BOOL StgDirEntry::SetSize( INT32 nNewSize )
+{
+ if (
+ !( nMode & STREAM_WRITE ) ||
+ (!bDirect && !pTmpStrm && !Strm2Tmp())
+ )
+ {
+ return FALSE;
+ }
+
+ if( nNewSize < nPos )
+ nPos = nNewSize;
+ if( pTmpStrm )
+ {
+ pTmpStrm->SetSize( nNewSize );
+ pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
+ return BOOL( pTmpStrm->GetError() == SVSTREAM_OK );
+ }
+ else
+ {
+ BOOL bRes = FALSE;
+ StgIo& rIo = pStgStrm->GetIo();
+ INT32 nThreshold = rIo.aHdr.GetThreshold();
+ // ensure the correct storage stream!
+ StgStrm* pOld = NULL;
+ USHORT nOldSize = 0;
+ if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() )
+ {
+ pOld = pStgStrm;
+ nOldSize = (USHORT) pOld->GetSize();
+ pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 );
+ }
+ else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() )
+ {
+ pOld = pStgStrm;
+ nOldSize = (USHORT) nNewSize;
+ pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
+ }
+ // now set the new size
+ if( pStgStrm->SetSize( nNewSize ) )
+ {
+ // did we create a new stream?
+ if( pOld )
+ {
+ // if so, we probably need to copy the old data
+ if( nOldSize )
+ {
+ void* pBuf = new BYTE[ nOldSize ];
+ pOld->Pos2Page( 0L );
+ pStgStrm->Pos2Page( 0L );
+ if( pOld->Read( pBuf, nOldSize )
+ && pStgStrm->Write( pBuf, nOldSize ) )
+ bRes = TRUE;
+ delete[] static_cast<BYTE*>(pBuf);
+ }
+ else
+ bRes = TRUE;
+ if( bRes )
+ {
+ pOld->SetSize( 0 );
+ delete pOld;
+ pStgStrm->Pos2Page( nPos );
+ pStgStrm->SetEntry( *this );
+ }
+ else
+ {
+ pStgStrm->SetSize( 0 );
+ delete pStgStrm;
+ pStgStrm = pOld;
+ }
+ }
+ else
+ {
+ pStgStrm->Pos2Page( nPos );
+ bRes = TRUE;
+ }
+ }
+ return bRes;
+ }
+}
+
+// Seek. On negative values, seek to EOF.
+
+INT32 StgDirEntry::Seek( INT32 nNew )
+{
+ if( pTmpStrm )
+ {
+ if( nNew < 0 )
+ nNew = pTmpStrm->GetSize();
+ nNew = pTmpStrm->Seek( nNew );
+ }
+ else if( pCurStrm )
+ {
+ if( nNew < 0 )
+ nNew = pCurStrm->GetSize();
+ nNew = pCurStrm->Seek( nNew );
+ }
+ else
+ {
+ INT32 nSize = aEntry.GetSize();
+
+ if( nNew < 0 )
+ nNew = nSize;
+
+ // try to enlarge, the readonly streams should not allow this
+ if( nNew > nSize )
+ {
+ if ( !( nMode & STREAM_WRITE ) || !SetSize( nNew ) )
+ {
+ OSL_ENSURE( nMode & STREAM_WRITE, "Trying to resize readonly stream by seeking, could be a wrong offset!" );
+ return nPos;
+ }
+ else
+ return Seek( nNew );
+ }
+ pStgStrm->Pos2Page( nNew );
+ nNew = pStgStrm->GetPos();
+ }
+ return nPos = nNew;
+}
+
+// Read
+
+INT32 StgDirEntry::Read( void* p, INT32 nLen )
+{
+ if( nLen <= 0 )
+ return 0;
+ if( pTmpStrm )
+ nLen = pTmpStrm->Read( p, nLen );
+ else if( pCurStrm )
+ nLen = pCurStrm->Read( p, nLen );
+ else
+ nLen = pStgStrm->Read( p, nLen );
+ nPos += nLen;
+ return nLen;
+}
+
+// Write
+
+INT32 StgDirEntry::Write( const void* p, INT32 nLen )
+{
+ if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
+ return 0;
+
+ // Was this stream committed internally and reopened in direct mode?
+ if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() )
+ return 0;
+ // Is this stream opened in transacted mode? Do we have to make a copy?
+ if( !bDirect && !pTmpStrm && !Strm2Tmp() )
+ return 0;
+ if( pTmpStrm )
+ {
+ nLen = pTmpStrm->Write( p, nLen );
+ pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
+ }
+ else
+ {
+ INT32 nNew = nPos + nLen;
+ if( nNew > pStgStrm->GetSize() )
+ {
+ if( !SetSize( nNew ) )
+ return 0L;
+ pStgStrm->Pos2Page( nPos );
+ }
+ nLen = pStgStrm->Write( p, nLen );
+ }
+ nPos += nLen;
+ return nLen;
+}
+
+// Copy the data of one entry into another entry.
+
+void StgDirEntry::Copy( StgDirEntry& rDest )
+{
+ INT32 n = GetSize();
+ if( rDest.SetSize( n ) && n )
+ {
+ BYTE aTempBytes[ 4096 ];
+ void* p = static_cast<void*>( aTempBytes );
+ Seek( 0L );
+ rDest.Seek( 0L );
+ while( n )
+ {
+ INT32 nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( Read( p, nn ) != nn )
+ break;
+ if( rDest.Write( p, nn ) != nn )
+ break;
+ n -= nn;
+ }
+ }
+}
+
+void StgDirEntry::Copy( BaseStorageStream& rDest )
+{
+ INT32 n = GetSize();
+ if( rDest.SetSize( n ) && n )
+ {
+ ULONG Pos = rDest.Tell();
+ BYTE aTempBytes[ 4096 ];
+ void* p = static_cast<void*>( aTempBytes );
+ Seek( 0L );
+ rDest.Seek( 0L );
+ while( n )
+ {
+ INT32 nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( Read( p, nn ) != nn )
+ break;
+ if( sal::static_int_cast<INT32>(rDest.Write( p, nn )) != nn )
+ break;
+ n -= nn;
+ }
+ rDest.Seek( Pos ); // ?! Seems to be undocumented !
+ }
+}
+
+// Commit this entry
+
+BOOL StgDirEntry::Commit()
+{
+ // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
+
+ aSave = aEntry;
+ BOOL bRes = TRUE;
+ if( aEntry.GetType() == STG_STREAM )
+ {
+ if( pTmpStrm )
+ delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
+ if( bRemoved )
+ // Delete the stream if needed
+ if( pStgStrm )
+ pStgStrm->SetSize( 0 );
+ }
+ else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
+ {
+ StgIterator aIter( *this );
+ for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
+ bRes = p->Commit();
+ }
+ return bRes;
+}
+
+// Revert the entry
+
+BOOL StgDirEntry::Revert()
+{
+ aEntry = aSave;
+ switch( aEntry.GetType() )
+ {
+ case STG_STREAM:
+ if( pCurStrm )
+ delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL;
+ break;
+ case STG_STORAGE:
+ {
+ BOOL bSomeRenamed = FALSE;
+ StgIterator aOIter( *this );
+ StgDirEntry* op = aOIter.First();
+ while( op )
+ {
+ op->aEntry = op->aSave;
+ op->bDirty = FALSE;
+ bSomeRenamed = BOOL( bSomeRenamed | op->bRenamed );
+ // Remove any new entries
+ if( op->bCreated )
+ {
+ op->bCreated = FALSE;
+ op->Close();
+ op->bInvalid = TRUE;
+ }
+ // Reactivate any removed entries
+ else if( op->bRemoved )
+ op->bRemoved = op->bInvalid = op->bTemp = FALSE;
+ op = aOIter.Next();
+ }
+ // Resort all renamed entries
+ if( bSomeRenamed )
+ {
+ StgIterator aIter( *this );
+ StgDirEntry* p = aIter.First();
+ while( p )
+ {
+ if( p->bRenamed )
+ {
+ StgAvlNode::Move
+ ( (StgAvlNode**) &p->pUp->pDown,
+ (StgAvlNode**) &p->pUp->pDown, p );
+ p->bRenamed = FALSE;
+ }
+ p = aIter.Next();
+ }
+ }
+ DelTemp( FALSE );
+ break;
+ }
+ case STG_EMPTY:
+ case STG_LOCKBYTES:
+ case STG_PROPERTY:
+ case STG_ROOT:
+ break;
+ }
+ return TRUE;
+}
+
+// Copy the stg stream to the temp stream
+
+BOOL StgDirEntry::Strm2Tmp()
+{
+ if( !pTmpStrm )
+ {
+ ULONG n = 0;
+ if( pCurStrm )
+ {
+ // It was already commited once
+ pTmpStrm = new StgTmpStrm;
+ if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
+ return TRUE;
+ n = 1; // indicates error
+ }
+ else
+ {
+ n = aEntry.GetSize();
+ pTmpStrm = new StgTmpStrm( n );
+ if( pTmpStrm->GetError() == SVSTREAM_OK )
+ {
+ if( n )
+ {
+ BYTE aTempBytes[ 4096 ];
+ void* p = static_cast<void*>( aTempBytes );
+ pStgStrm->Pos2Page( 0L );
+ while( n )
+ {
+ ULONG nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( (ULONG) pStgStrm->Read( p, nn ) != nn )
+ break;
+ if( pTmpStrm->Write( p, nn ) != nn )
+ break;
+ n -= nn;
+ }
+ pStgStrm->Pos2Page( nPos );
+ pTmpStrm->Seek( nPos );
+ }
+ }
+ else
+ n = 1;
+ }
+ if( n )
+ {
+ pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
+ delete pTmpStrm;
+ pTmpStrm = NULL;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// Copy the temp stream to the stg stream during the final commit
+
+BOOL StgDirEntry::Tmp2Strm()
+{
+ // We did commit once, but have not written since then
+ if( !pTmpStrm )
+ pTmpStrm = pCurStrm, pCurStrm = NULL;
+ if( pTmpStrm )
+ {
+ ULONG n = pTmpStrm->GetSize();
+ StgStrm* pNewStrm;
+ StgIo& rIo = pStgStrm->GetIo();
+ ULONG nThreshold = (ULONG) rIo.aHdr.GetThreshold();
+ if( n < nThreshold )
+ pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
+ else
+ pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
+ if( pNewStrm->SetSize( n ) )
+ {
+ BYTE p[ 4096 ];
+ pTmpStrm->Seek( 0L );
+ while( n )
+ {
+ ULONG nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( pTmpStrm->Read( p, nn ) != nn )
+ break;
+ if( (ULONG) pNewStrm->Write( p, nn ) != nn )
+ break;
+ n -= nn;
+ }
+ if( n )
+ {
+ pTmpStrm->Seek( nPos );
+ pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
+ delete pNewStrm;
+ return FALSE;
+ }
+ else
+ {
+ pStgStrm->SetSize( 0L );
+ delete pStgStrm;
+ pStgStrm = pNewStrm;
+ pNewStrm->SetEntry( *this );
+ pNewStrm->Pos2Page( nPos );
+ delete pTmpStrm;
+ delete pCurStrm;
+ pTmpStrm = pCurStrm = NULL;
+ aSave = aEntry;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// Check if the given entry is contained in this entry
+
+BOOL StgDirEntry::IsContained( StgDirEntry* pStg )
+{
+ if( aEntry.GetType() == STG_STORAGE )
+ {
+ StgIterator aIter( *this );
+ StgDirEntry* p = aIter.First();
+ while( p )
+ {
+ if( !p->aEntry.Compare( pStg->aEntry ) )
+ return FALSE;
+ if( p->aEntry.GetType() == STG_STORAGE )
+ if( !p->IsContained( pStg ) )
+ return FALSE;
+ p = aIter.Next();
+ }
+ }
+ return TRUE;
+}
+
+// Invalidate all open entries by setting the RefCount to 0. If the bDel
+// flag is set, also set the invalid flag to indicate deletion during the
+// next dir stream flush.
+
+void StgDirEntry::Invalidate( BOOL bDel )
+{
+// nRefCnt = 0;
+ if( bDel )
+ bRemoved = bInvalid = TRUE;
+ switch( aEntry.GetType() )
+ {
+ case STG_STORAGE:
+ case STG_ROOT:
+ {
+ StgIterator aIter( *this );
+ for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
+ p->Invalidate( bDel );
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+///////////////////////////// class StgDirStrm ////////////////////////////
+
+// This specialized stream is the maintenance stream for the directory tree.
+
+StgDirStrm::StgDirStrm( StgIo& r )
+ : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
+ , pRoot( NULL )
+ , nEntries( 0 )
+{
+ if( r.GetError() )
+ return;
+ nEntries = nPageSize / STGENTRY_SIZE;
+ if( nStart == STG_EOF )
+ {
+ StgEntry aRoot;
+ aRoot.Init();
+ aRoot.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Root Entry" ) ) );
+ aRoot.SetType( STG_ROOT );
+ pRoot = new StgDirEntry( aRoot );
+ pRoot->SetDirty();
+ }
+ else
+ {
+ // temporarily use this instance as owner, so
+ // the TOC pages can be removed.
+ pEntry = (StgDirEntry*) this; // just for a bit pattern
+ SetupEntry( 0, pRoot );
+ rIo.Revert( pEntry );
+ pEntry = NULL;
+ }
+}
+
+StgDirStrm::~StgDirStrm()
+{
+ delete pRoot;
+}
+
+// Recursively parse the directory tree during reading the TOC stream
+
+void StgDirStrm::SetupEntry( INT32 n, StgDirEntry* pUpper )
+{
+ void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
+ if( p )
+ {
+ BOOL bOk(FALSE);
+ StgDirEntry* pCur = new StgDirEntry( p, &bOk );
+
+ if( !bOk )
+ {
+ delete pCur;
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ // an error occured
+ return;
+ }
+
+ // better it is
+ if( !pUpper )
+ pCur->aEntry.SetType( STG_ROOT );
+
+ INT32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
+ INT32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
+ // substorage?
+ INT32 nLeaf = STG_FREE;
+ if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
+ {
+ nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
+ if (nLeaf != STG_FREE && nLeaf == n)
+ {
+ delete pCur;
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ return;
+ }
+ }
+
+ if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
+ {
+ if( StgAvlNode::Insert
+ ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
+ {
+ pCur->pUp = pUpper;
+ pCur->ppRoot = &pRoot;
+ }
+ else
+ {
+ rIo.SetError( SVSTREAM_CANNOT_MAKE );
+ delete pCur; pCur = NULL;
+ return;
+ }
+ SetupEntry( nLeft, pUpper );
+ SetupEntry( nRight, pUpper );
+ SetupEntry( nLeaf, pCur );
+ }
+ }
+}
+
+// Extend or shrink the directory stream.
+
+BOOL StgDirStrm::SetSize( INT32 nBytes )
+{
+ // Always allocate full pages
+ nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
+ return StgStrm::SetSize( nBytes );
+}
+
+// Save the TOC stream into a new substream after saving all data streams
+
+BOOL StgDirStrm::Store()
+{
+ if( !pRoot->IsDirty() )
+ return TRUE;
+ if( !pRoot->StoreStreams( rIo ) )
+ return FALSE;
+ // After writing all streams, the data FAT stream has changed,
+ // so we have to commit the root again
+ pRoot->Commit();
+ // We want a completely new stream, so fake an empty stream
+ INT32 nOldStart = nStart; // save for later deletion
+ INT32 nOldSize = nSize;
+ nStart = nPage = STG_EOF;
+ nSize = nPos = 0;
+ nOffset = 0;
+ // Delete all temporary entries
+ pRoot->DelTemp( FALSE );
+ // set the entry numbers
+ INT32 n = 0;
+ pRoot->Enum( n );
+ if( !SetSize( n * STGENTRY_SIZE ) )
+ {
+ nStart = nOldStart; nSize = nOldSize;
+ pRoot->RevertAll();
+ return FALSE;
+ }
+ // set up the cache elements for the new stream
+ if( !Copy( STG_FREE, nSize ) )
+ {
+ pRoot->RevertAll();
+ return FALSE;
+ }
+ // Write the data to the new stream
+ if( !pRoot->Store( *this ) )
+ {
+ pRoot->RevertAll();
+ return FALSE;
+ }
+ // fill any remaining entries with empty data
+ INT32 ne = nSize / STGENTRY_SIZE;
+ StgEntry aEmpty;
+ aEmpty.Init();
+ while( n < ne )
+ {
+ void* p = GetEntry( n++, TRUE );
+ if( !p )
+ {
+ pRoot->RevertAll();
+ return FALSE;
+ }
+ aEmpty.Store( p );
+ }
+ // Now we can release the old stream
+ pFat->FreePages( nOldStart, TRUE );
+ rIo.aHdr.SetTOCStart( nStart );
+ return TRUE;
+}
+
+// Get a dir entry.
+
+void* StgDirStrm::GetEntry( INT32 n, BOOL bDirty )
+{
+ n *= STGENTRY_SIZE;
+ if( n >= nSize )
+ return NULL;
+ return GetPtr( n, TRUE, bDirty );
+}
+
+// Find a dir entry.
+
+StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
+{
+ if( rStg.pDown )
+ {
+ StgEntry aEntry;
+ aEntry.Init();
+ if( !aEntry.SetName( rName ) )
+ {
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ return NULL;
+ }
+ // Look in the directory attached to the entry
+ StgDirEntry aTest( aEntry );
+ return (StgDirEntry*) rStg.pDown->Find( &aTest );
+ }
+ else
+ return NULL;
+}
+
+// Create a new entry.
+
+StgDirEntry* StgDirStrm::Create
+ ( StgDirEntry& rStg, const String& rName, StgEntryType eType )
+{
+ StgEntry aEntry;
+ aEntry.Init();
+ aEntry.SetType( eType );
+ if( !aEntry.SetName( rName ) )
+ {
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ return NULL;
+ }
+ StgDirEntry* pRes = Find( rStg, rName );
+ if( pRes )
+ {
+ if( !pRes->bInvalid )
+ {
+ rIo.SetError( SVSTREAM_CANNOT_MAKE );
+ return NULL;
+ }
+ pRes->bInvalid =
+ pRes->bRemoved =
+ pRes->bTemp = FALSE;
+ pRes->bCreated =
+ pRes->bDirty = TRUE;
+ }
+ else
+ {
+ pRes = new StgDirEntry( aEntry );
+ if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
+ {
+ pRes->pUp = &rStg;
+ pRes->ppRoot = &pRoot;
+ pRes->bCreated =
+ pRes->bDirty = TRUE;
+ }
+ else
+ {
+ rIo.SetError( SVSTREAM_CANNOT_MAKE );
+ delete pRes; pRes = NULL;
+ }
+ }
+ return pRes;
+}
+
+// Rename the given entry.
+
+BOOL StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
+{
+ StgDirEntry* p = Find( rStg, rOld );
+ if( p )
+ {
+
+ if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, FALSE ) )
+ return FALSE;
+ p->aEntry.SetName( rNew );
+ if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
+ return FALSE;
+ p->bRenamed = p->bDirty = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+ }
+}
+
+// Move the given entry to a different storage.
+
+BOOL StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
+{
+ StgDirEntry* p = Find( rStg1, rName );
+ if( p )
+ {
+ if( !StgAvlNode::Move
+ ( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
+ return FALSE;
+ p->bDirty = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+ }
+}
+
diff --git a/sot/source/sdstor/stgdir.hxx b/sot/source/sdstor/stgdir.hxx
new file mode 100644
index 000000000000..3ef510a12e50
--- /dev/null
+++ b/sot/source/sdstor/stgdir.hxx
@@ -0,0 +1,132 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _STGDIR_HXX
+#define _STGDIR_HXX
+
+#include "stgavl.hxx"
+#include "stgelem.hxx"
+#include "stgstrms.hxx"
+
+class StgIo;
+class StgEntry;
+class StgDirEntry;
+class StgDirStrm;
+
+class BaseStorageStream;
+class StgDirEntry : public StgAvlNode
+{
+ friend class StgIterator;
+ friend class StgDirStrm;
+ StgEntry aSave; // original dir entry
+ StgDirEntry* pUp; // parent directory
+ StgDirEntry* pDown; // child directory for storages
+ StgDirEntry** ppRoot; // root of TOC tree
+ StgStrm* pStgStrm; // storage stream
+ StgTmpStrm* pTmpStrm; // temporary stream
+ StgTmpStrm* pCurStrm; // temp stream after commit
+ INT32 nEntry; // entry # in TOC stream (temp)
+ INT32 nPos; // current position
+ BOOL bDirty; // dirty directory entry
+ BOOL bCreated; // newly created entry
+ BOOL bRemoved; // removed per Invalidate()
+ BOOL bRenamed; // renamed
+ void InitMembers(); // ctor helper
+ virtual short Compare( const StgAvlNode* ) const;
+ BOOL StoreStream( StgIo& ); // store the stream
+ BOOL StoreStreams( StgIo& ); // store all streams
+ void RevertAll(); // revert the whole tree
+ BOOL Strm2Tmp(); // copy stgstream to temp file
+ BOOL Tmp2Strm(); // copy temp file to stgstream
+public:
+ StgEntry aEntry; // entry data
+ INT32 nRefCnt; // reference count
+ StreamMode nMode; // open mode
+ BOOL bTemp; // TRUE: delete on dir flush
+ BOOL bDirect; // TRUE: direct mode
+ BOOL bZombie; // TRUE: Removed From StgIo
+ BOOL bInvalid; // TRUE: invalid entry
+ StgDirEntry( const void*, BOOL * pbOk );
+ StgDirEntry( const StgEntry& );
+ ~StgDirEntry();
+
+ void Invalidate( BOOL=FALSE ); // invalidate all open entries
+ void Enum( INT32& ); // enumerate entries for iteration
+ void DelTemp( BOOL ); // delete temporary entries
+ BOOL Store( StgDirStrm& ); // save entry into dir strm
+ BOOL IsContained( StgDirEntry* ); // check if subentry
+
+ void SetDirty() { bDirty = TRUE; }
+ BOOL IsDirty();
+ void ClearDirty();
+
+ BOOL Commit();
+ BOOL Revert();
+
+ void OpenStream( StgIo&, BOOL=FALSE ); // set up an approbiate stream
+ void Close();
+ INT32 GetSize();
+ BOOL SetSize( INT32 );
+ INT32 Seek( INT32 );
+ INT32 Tell() { return nPos; }
+ INT32 Read( void*, INT32 );
+ INT32 Write( const void*, INT32 );
+ void Copy( StgDirEntry& );
+ void Copy( BaseStorageStream& );
+};
+
+class StgDirStrm : public StgDataStrm
+{
+ friend class StgIterator;
+ StgDirEntry* pRoot; // root of dir tree
+ short nEntries; // entries per page
+ void SetupEntry( INT32, StgDirEntry* );
+public:
+ StgDirStrm( StgIo& );
+ ~StgDirStrm();
+ virtual BOOL SetSize( INT32 ); // change the size
+ BOOL Store();
+ void* GetEntry( INT32 n, BOOL=FALSE );// get an entry
+ StgDirEntry* GetRoot() { return pRoot; }
+ StgDirEntry* Find( StgDirEntry&, const String& );
+ StgDirEntry* Create( StgDirEntry&, const String&, StgEntryType );
+ BOOL Remove( StgDirEntry&, const String& );
+ BOOL Rename( StgDirEntry&, const String&, const String& );
+ BOOL Move( StgDirEntry&, StgDirEntry&, const String& );
+};
+
+class StgIterator : public StgAvlIterator
+{
+public:
+ StgIterator( StgDirEntry& rStg ) : StgAvlIterator( rStg.pDown ) {}
+ StgDirEntry* First() { return (StgDirEntry*) StgAvlIterator::First(); }
+ StgDirEntry* Next() { return (StgDirEntry*) StgAvlIterator::Next(); }
+ StgDirEntry* Last() { return (StgDirEntry*) StgAvlIterator::Last(); }
+ StgDirEntry* Prev() { return (StgDirEntry*) StgAvlIterator::Prev(); }
+};
+
+#endif
diff --git a/sot/source/sdstor/stgelem.cxx b/sot/source/sdstor/stgelem.cxx
new file mode 100644
index 000000000000..46d7f1803140
--- /dev/null
+++ b/sot/source/sdstor/stgelem.cxx
@@ -0,0 +1,425 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include <string.h> // memset(), memcpy()
+#include <rtl/ustring.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <unotools/charclass.hxx>
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+
+static BYTE cStgSignature[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 };
+
+////////////////////////////// struct ClsId /////////////////////////////
+
+SvStream& operator >>( SvStream& r, ClsId& rId )
+{
+ r >> rId.n1
+ >> rId.n2
+ >> rId.n3
+ >> rId.n4
+ >> rId.n5
+ >> rId.n6
+ >> rId.n7
+ >> rId.n8
+ >> rId.n9
+ >> rId.n10
+ >> rId.n11;
+ return r;
+}
+
+SvStream& operator <<( SvStream& r, const ClsId& rId )
+{
+ return
+ r << (INT32) rId.n1
+ << (INT16) rId.n2
+ << (INT16) rId.n3
+ << (UINT8) rId.n4
+ << (UINT8) rId.n5
+ << (UINT8) rId.n6
+ << (UINT8) rId.n7
+ << (UINT8) rId.n8
+ << (UINT8) rId.n9
+ << (UINT8) rId.n10
+ << (UINT8) rId.n11;
+}
+
+///////////////////////////// class StgHeader ////////////////////////////
+
+StgHeader::StgHeader()
+{
+ memset( this, 0, sizeof( StgHeader ) );
+}
+
+void StgHeader::Init()
+{
+ memset( this, 0, sizeof( StgHeader ) );
+ memcpy( cSignature, cStgSignature, 8 );
+ nVersion = 0x0003003B;
+ nByteOrder = 0xFFFE;
+ nPageSize = 9; // 512 bytes
+ nDataPageSize = 6; // 64 bytes
+ nThreshold = 4096;
+ nDataFATSize = 0;
+ nMasterChain = STG_EOF;
+ SetTOCStart( STG_EOF );
+ SetDataFATStart( STG_EOF );
+ for( short i = 0; i < 109; i++ )
+ SetFATPage( i, STG_FREE );
+}
+
+BOOL StgHeader::Load( StgIo& rIo )
+{
+ SvStream& r = *rIo.GetStrm();
+ Load( r );
+ return rIo.Good();
+}
+
+BOOL StgHeader::Load( SvStream& r )
+{
+ r.Seek( 0L );
+ r.Read( cSignature, 8 );
+ r >> aClsId // 08 Class ID
+ >> nVersion // 1A version number
+ >> nByteOrder // 1C Unicode byte order indicator
+ >> nPageSize // 1E 1 << nPageSize = block size
+ >> nDataPageSize; // 20 1 << this size == data block size
+ r.SeekRel( 10 );
+ r >> nFATSize // 2C total number of FAT pages
+ >> nTOCstrm // 30 starting page for the TOC stream
+ >> nReserved // 34
+ >> nThreshold // 38 minimum file size for big data
+ >> nDataFAT // 3C page # of 1st data FAT block
+ >> nDataFATSize // 40 # of data FATpages
+ >> nMasterChain // 44 chain to the next master block
+ >> nMaster; // 48 # of additional master blocks
+ for( short i = 0; i < 109; i++ )
+ r >> nMasterFAT[ i ];
+ return r.GetErrorCode() == ERRCODE_NONE;
+}
+
+BOOL StgHeader::Store( StgIo& rIo )
+{
+ if( !bDirty )
+ return TRUE;
+ SvStream& r = *rIo.GetStrm();
+ r.Seek( 0L );
+ r.Write( cSignature, 8 + 16 );
+ r << nVersion // 1A version number
+ << nByteOrder // 1C Unicode byte order indicator
+ << nPageSize // 1E 1 << nPageSize = block size
+ << nDataPageSize // 20 1 << this size == data block size
+ << (INT32) 0 << (INT32) 0 << (INT16) 0
+ << nFATSize // 2C total number of FAT pages
+ << nTOCstrm // 30 starting page for the TOC stream
+ << nReserved // 34
+ << nThreshold // 38 minimum file size for big data
+ << nDataFAT // 3C page # of 1st data FAT block
+ << nDataFATSize // 40 # of data FAT pages
+ << nMasterChain // 44 chain to the next master block
+ << nMaster; // 48 # of additional master blocks
+ for( short i = 0; i < 109; i++ )
+ r << nMasterFAT[ i ];
+ bDirty = !rIo.Good();
+ return BOOL( !bDirty );
+}
+
+static bool lcl_wontoverflow(short shift)
+{
+ return shift >= 0 && shift < (short)sizeof(short) * 8 - 1;
+}
+
+// Perform thorough checks also on unknown variables
+BOOL StgHeader::Check()
+{
+ return BOOL( memcmp( cSignature, cStgSignature, 8 ) == 0
+ && (short) ( nVersion >> 16 ) == 3 )
+ && lcl_wontoverflow(nPageSize)
+ && lcl_wontoverflow(nDataPageSize);
+}
+
+INT32 StgHeader::GetFATPage( short n ) const
+{
+ if( n >= 0 && n < 109 )
+ return nMasterFAT[ n ];
+ else
+ return STG_EOF;
+}
+
+void StgHeader::SetFATPage( short n, INT32 nb )
+{
+ if( n >= 0 && n < 109 )
+ {
+ if( nMasterFAT[ n ] != nb )
+ bDirty = TRUE, nMasterFAT[ n ] = nb;
+ }
+}
+
+void StgHeader::SetClassId( const ClsId& r )
+{
+ if( memcmp( &aClsId, &r, sizeof( ClsId ) ) )
+ bDirty = TRUE, memcpy( &aClsId, &r, sizeof( ClsId ) );
+}
+
+void StgHeader::SetTOCStart( INT32 n )
+{
+ if( n != nTOCstrm ) bDirty = TRUE, nTOCstrm = n;
+}
+
+void StgHeader::SetDataFATStart( INT32 n )
+{
+ if( n != nDataFAT ) bDirty = TRUE, nDataFAT = n;
+}
+
+void StgHeader::SetDataFATSize( INT32 n )
+{
+ if( n != nDataFATSize ) bDirty = TRUE, nDataFATSize = n;
+}
+
+void StgHeader::SetFATSize( INT32 n )
+{
+ if( n != nFATSize ) bDirty = TRUE, nFATSize = n;
+}
+
+void StgHeader::SetFATChain( INT32 n )
+{
+ if( n != nMasterChain )
+ bDirty = TRUE, nMasterChain = n;
+}
+
+void StgHeader::SetMasters( INT32 n )
+{
+ if( n != nMaster ) bDirty = TRUE, nMaster = n;
+}
+
+///////////////////////////// class StgEntry /////////////////////////////
+
+// This class is only a wrapper around teh dir entry structure
+// which retrieves and sets data.
+
+// The name must be smaller than 32 chars. Conversion into Unicode
+// is easy, since the 1st 256 characters of the Windows ANSI set
+// equal the 1st 256 Unicode characters.
+/*
+void ToUnicode_Impl( String& rName )
+{
+ rName.Erase( 32 );
+ rName.Convert( ::GetSystemCharSet(), CHARSET_ANSI );
+ // brute force is OK
+ BYTE* p = (BYTE*) rName.GetCharStr();
+ for( USHORT i = 0; i < rName.Len(); i++, p++ )
+ {
+ // check each character and substitute blanks for illegal ones
+ BYTE cChar = *p;
+ if( cChar == '!' || cChar == ':' || cChar == '\\' || cChar == '/' )
+ *p = ' ';
+ }
+}
+*/
+/*
+static void FromUnicode( String& rName )
+{
+ rName.Convert( CHARSET_ANSI, ::GetSystemCharSet() );
+}
+*/
+BOOL StgEntry::Init()
+{
+ memset( this, 0, sizeof (StgEntry) - sizeof( String ) );
+ SetLeaf( STG_LEFT, STG_FREE );
+ SetLeaf( STG_RIGHT, STG_FREE );
+ SetLeaf( STG_CHILD, STG_FREE );
+ SetLeaf( STG_DATA, STG_EOF );
+ return TRUE;
+}
+
+static String ToUpperUnicode( const String & rStr )
+{
+ // I don't know the locale, so en_US is hopefully fine
+ /*
+ com.sun.star.lang.Locale aLocale;
+ aLocale.Language = OUString::createFromAscii( "en" );
+ aLocale.Country = OUString::createFromAscii( "US" );
+ */
+ static rtl::OUString aEN=rtl::OUString::createFromAscii( "en" );
+ static rtl::OUString aUS=rtl::OUString::createFromAscii( "US" );
+ static CharClass aCC( com::sun::star::lang::Locale( aEN, aUS, rtl::OUString() ) );
+ return aCC.toUpper( rStr, 0, rStr.Len() );
+}
+
+
+BOOL StgEntry::SetName( const String& rName )
+{
+ // I don't know the locale, so en_US is hopefully fine
+ aName = ToUpperUnicode( rName );
+ aName.Erase( 31 );
+
+ int i;
+ for( i = 0; i < aName.Len() && i < 32; i++ )
+ nName[ i ] = rName.GetChar( sal_uInt16( i ));
+ while( i < 32 )
+ nName[ i++ ] = 0;
+ nNameLen = ( aName.Len() + 1 ) << 1;
+ return TRUE;
+}
+
+INT32 StgEntry::GetLeaf( StgEntryRef eRef ) const
+{
+ INT32 n = -1;
+ switch( eRef )
+ {
+ case STG_LEFT: n = nLeft; break;
+ case STG_RIGHT: n = nRight; break;
+ case STG_CHILD: n = nChild; break;
+ case STG_DATA: n = nPage1; break;
+ }
+ return n;
+}
+
+void StgEntry::SetLeaf( StgEntryRef eRef, INT32 nPage )
+{
+ switch( eRef )
+ {
+ case STG_LEFT: nLeft = nPage; break;
+ case STG_RIGHT: nRight = nPage; break;
+ case STG_CHILD: nChild = nPage; break;
+ case STG_DATA: nPage1 = nPage; break;
+ }
+}
+
+const INT32* StgEntry::GetTime( StgEntryTime eTime ) const
+{
+ return( eTime == STG_MODIFIED ) ? nMtime : nAtime;
+}
+
+void StgEntry::SetTime( StgEntryTime eTime, INT32* pTime )
+{
+ if( eTime == STG_MODIFIED )
+ nMtime[ 0 ] = *pTime++, nMtime[ 1 ] = *pTime;
+ else
+ nAtime[ 0 ] = *pTime++, nAtime[ 1 ] = *pTime;
+}
+
+void StgEntry::SetClassId( const ClsId& r )
+{
+ memcpy( &aClsId, &r, sizeof( ClsId ) );
+}
+
+void StgEntry::GetName( String& rName ) const
+{
+ UINT16 n = nNameLen;
+ if( n )
+ n = ( n >> 1 ) - 1;
+ rName = String( nName, n );
+}
+
+// Compare two entries. Do this case-insensitive.
+
+short StgEntry::Compare( const StgEntry& r ) const
+{
+ /*
+ short nRes = r.nNameLen - nNameLen;
+ if( !nRes ) return strcmp( r.aName, aName );
+ else return nRes;
+ */
+ sal_Int32 nRes = r.nNameLen - nNameLen;
+ if( !nRes )
+ nRes = r.aName.CompareTo( aName );
+ return (short)nRes;
+ //return aName.CompareTo( r.aName );
+}
+
+// These load/store operations are a bit more complicated,
+// since they have to copy their contents into a packed structure.
+
+BOOL StgEntry::Load( const void* pFrom )
+{
+ SvMemoryStream r( (sal_Char*) pFrom, 128, STREAM_READ );
+ for( short i = 0; i < 32; i++ )
+ r >> nName[ i ]; // 00 name as WCHAR
+ r >> nNameLen // 40 size of name in bytes including 00H
+ >> cType // 42 entry type
+ >> cFlags // 43 0 or 1 (tree balance?)
+ >> nLeft // 44 left node entry
+ >> nRight // 48 right node entry
+ >> nChild // 4C 1st child entry if storage
+ >> aClsId // 50 class ID (optional)
+ >> nFlags // 60 state flags(?)
+ >> nMtime[ 0 ] // 64 modification time
+ >> nMtime[ 1 ] // 64 modification time
+ >> nAtime[ 0 ] // 6C creation and access time
+ >> nAtime[ 1 ] // 6C creation and access time
+ >> nPage1 // 74 starting block (either direct or translated)
+ >> nSize // 78 file size
+ >> nUnknown; // 7C unknown
+
+ UINT16 n = nNameLen;
+ if( n )
+ n = ( n >> 1 ) - 1;
+ if( n > 31 || (nSize < 0 && cType != STG_STORAGE) )
+ {
+ // the size makes no sence for the substorage
+ // TODO/LATER: actually the size should be an unsigned value, but in this case it would mean a stream of more than 2Gb
+ return FALSE;
+ }
+
+ aName = String( nName, n );
+ // I don't know the locale, so en_US is hopefully fine
+ aName = ToUpperUnicode( aName );
+ aName.Erase( 31 );
+
+ return TRUE;
+}
+
+void StgEntry::Store( void* pTo )
+{
+ SvMemoryStream r( (sal_Char *)pTo, 128, STREAM_WRITE );
+ for( short i = 0; i < 32; i++ )
+ r << nName[ i ]; // 00 name as WCHAR
+ r << nNameLen // 40 size of name in bytes including 00H
+ << cType // 42 entry type
+ << cFlags // 43 0 or 1 (tree balance?)
+ << nLeft // 44 left node entry
+ << nRight // 48 right node entry
+ << nChild // 4C 1st child entry if storage;
+ << aClsId // 50 class ID (optional)
+ << nFlags // 60 state flags(?)
+ << nMtime[ 0 ] // 64 modification time
+ << nMtime[ 1 ] // 64 modification time
+ << nAtime[ 0 ] // 6C creation and access time
+ << nAtime[ 1 ] // 6C creation and access time
+ << nPage1 // 74 starting block (either direct or translated)
+ << nSize // 78 file size
+ << nUnknown; // 7C unknown
+}
+
diff --git a/sot/source/sdstor/stgelem.hxx b/sot/source/sdstor/stgelem.hxx
new file mode 100644
index 000000000000..7a5b7bc52b26
--- /dev/null
+++ b/sot/source/sdstor/stgelem.hxx
@@ -0,0 +1,166 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// This file reflects the structure of MS file elements.
+// It is very sensitive to alignment!
+
+#ifndef _STGELEM_HXX
+#define _STGELEM_HXX
+
+#ifndef _TOOLS_SOLAR_H
+#include <tools/solar.h>
+#endif
+
+#include <stg.hxx>
+
+class StgIo;
+class SvStream;
+class String;
+
+SvStream& operator>>( SvStream&, ClsId& );
+SvStream& operator<<( SvStream&, const ClsId& );
+
+class StgHeader
+{
+ BYTE cSignature[ 8 ]; // 00 signature (see below)
+ ClsId aClsId; // 08 Class ID
+ INT32 nVersion; // 18 version number
+ UINT16 nByteOrder; // 1C Unicode byte order indicator
+ INT16 nPageSize; // 1E 1 << nPageSize = block size
+ INT16 nDataPageSize; // 20 1 << this size == data block size
+ BYTE bDirty; // 22 internal dirty flag
+ BYTE cReserved[ 9 ]; // 23
+ INT32 nFATSize; // 2C total number of FAT pages
+ INT32 nTOCstrm; // 30 starting page for the TOC stream
+ INT32 nReserved; // 34
+ INT32 nThreshold; // 38 minimum file size for big data
+ INT32 nDataFAT; // 3C page # of 1st data FAT block
+ INT32 nDataFATSize; // 40 # of data fat blocks
+ INT32 nMasterChain; // 44 chain to the next master block
+ INT32 nMaster; // 48 # of additional master blocks
+ INT32 nMasterFAT[ 109 ]; // 4C first 109 master FAT pages
+public:
+ StgHeader();
+ void Init(); // initialize the header
+ BOOL Load( StgIo& );
+ BOOL Load( SvStream& );
+ BOOL Store( StgIo& );
+ BOOL Check(); // check the signature and version
+ short GetByteOrder() const { return nByteOrder; }
+ INT32 GetTOCStart() const { return nTOCstrm; }
+ void SetTOCStart( INT32 n );
+ INT32 GetDataFATStart() const { return nDataFAT; }
+ void SetDataFATStart( INT32 n );
+ INT32 GetDataFATSize() const { return nDataFATSize; }
+ void SetDataFATSize( INT32 n );
+ INT32 GetThreshold() const { return nThreshold; }
+ short GetPageSize() const { return nPageSize; }
+ short GetDataPageSize() const { return nDataPageSize; }
+ INT32 GetFATSize() const { return nFATSize; }
+ void SetFATSize( INT32 n );
+ INT32 GetFATChain() const { return nMasterChain; }
+ void SetFATChain( INT32 n );
+ INT32 GetMasters() const { return nMaster; }
+ void SetMasters( INT32 n );
+ short GetFAT1Size() const { return 109; }
+ const ClsId& GetClassId() const { return aClsId; }
+ void SetClassId( const ClsId& );
+ INT32 GetFATPage( short ) const;
+ void SetFATPage( short, INT32 );
+};
+
+enum StgEntryType { // dir entry types:
+ STG_EMPTY = 0,
+ STG_STORAGE = 1,
+ STG_STREAM = 2,
+ STG_LOCKBYTES = 3,
+ STG_PROPERTY = 4,
+ STG_ROOT = 5
+};
+
+enum StgEntryRef { // reference blocks:
+ STG_LEFT = 0, // left
+ STG_RIGHT = 1, // right
+ STG_CHILD = 2, // child
+ STG_DATA = 3 // data start
+};
+
+enum StgEntryTime { // time codes:
+ STG_MODIFIED = 0, // last modification
+ STG_ACCESSED = 1 // last access
+};
+
+class StgStream;
+
+#define STGENTRY_SIZE 128
+
+class StgEntry { // directory enty
+ UINT16 nName[ 32 ]; // 00 name as WCHAR
+ INT16 nNameLen; // 40 size of name in bytes including 00H
+ BYTE cType; // 42 entry type
+ BYTE cFlags; // 43 0 or 1 (tree balance?)
+ INT32 nLeft; // 44 left node entry
+ INT32 nRight; // 48 right node entry
+ INT32 nChild; // 4C 1st child entry if storage
+ ClsId aClsId; // 50 class ID (optional)
+ INT32 nFlags; // 60 state flags(?)
+ INT32 nMtime[ 2 ]; // 64 modification time
+ INT32 nAtime[ 2 ]; // 6C creation and access time
+ INT32 nPage1; // 74 starting block (either direct or translated)
+ INT32 nSize; // 78 file size
+ INT32 nUnknown; // 7C unknown
+ String aName; // Name as Compare String (ascii, upper)
+public:
+ BOOL Init(); // initialize the data
+ BOOL SetName( const String& ); // store a name (ASCII, up to 32 chars)
+ void GetName( String& rName ) const;
+ // fill in the name
+ short Compare( const StgEntry& ) const; // compare two entries
+ BOOL Load( const void* );
+ void Store( void* );
+ StgEntryType GetType() const { return (StgEntryType) cType; }
+ INT32 GetStartPage() const { return nPage1; }
+ void SetType( StgEntryType t ) { cType = (BYTE) t; }
+ BYTE GetFlags() const { return cFlags; }
+ void SetFlags( BYTE c ) { cFlags = c; }
+ INT32 GetSize() const { return nSize; }
+ void SetSize( INT32 n ) { nSize = n; }
+ const ClsId& GetClassId() const { return aClsId; }
+ void SetClassId( const ClsId& );
+ INT32 GetLeaf( StgEntryRef ) const;
+ void SetLeaf( StgEntryRef, INT32 );
+ const INT32* GetTime( StgEntryTime ) const;
+ void SetTime( StgEntryTime, INT32* );
+};
+
+
+#define STG_FREE -1L // page is free
+#define STG_EOF -2L // page is last page in chain
+#define STG_FAT -3L // page is FAT page
+#define STG_MASTER -4L // page is master FAT page
+
+#endif
diff --git a/sot/source/sdstor/stgio.cxx b/sot/source/sdstor/stgio.cxx
new file mode 100644
index 000000000000..00dd454233e2
--- /dev/null
+++ b/sot/source/sdstor/stgio.cxx
@@ -0,0 +1,389 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+#include <rtl/instance.hxx>
+
+///////////////////////////// class StgIo //////////////////////////////
+
+// This class holds the storage header and all internal streams.
+
+StgIo::StgIo() : StgCache()
+{
+ pTOC = NULL;
+ pDataFAT = NULL;
+ pDataStrm = NULL;
+ pFAT = NULL;
+ bCopied = FALSE;
+}
+
+StgIo::~StgIo()
+{
+ delete pTOC;
+ delete pDataFAT;
+ delete pDataStrm;
+ delete pFAT;
+}
+
+// Load the header. Do not set an error code if the header is invalid.
+
+BOOL StgIo::Load()
+{
+ if( pStrm )
+ {
+ if( aHdr.Load( *this ) )
+ {
+ if( aHdr.Check() )
+ SetupStreams();
+ else
+ return FALSE;
+ }
+ }
+ return Good();
+}
+
+// Set up an initial, empty storage
+
+BOOL StgIo::Init()
+{
+ aHdr.Init();
+ SetupStreams();
+ return CommitAll();
+}
+
+void StgIo::SetupStreams()
+{
+ delete pTOC;
+ delete pDataFAT;
+ delete pDataStrm;
+ delete pFAT;
+ pTOC = NULL;
+ pDataFAT = NULL;
+ pDataStrm = NULL;
+ pFAT = NULL;
+ ResetError();
+ SetPhysPageSize( 1 << aHdr.GetPageSize() );
+ pFAT = new StgFATStrm( *this );
+ pTOC = new StgDirStrm( *this );
+ if( !GetError() )
+ {
+ StgDirEntry* pRoot = pTOC->GetRoot();
+ if( pRoot )
+ {
+ pDataFAT = new StgDataStrm( *this, aHdr.GetDataFATStart(), -1 );
+ pDataStrm = new StgDataStrm( *this, pRoot );
+ pDataFAT->SetIncrement( 1 << aHdr.GetPageSize() );
+ pDataStrm->SetIncrement( GetDataPageSize() );
+ pDataStrm->SetEntry( *pRoot );
+ }
+ else
+ SetError( SVSTREAM_FILEFORMAT_ERROR );
+ }
+}
+
+// get the logical data page size
+
+short StgIo::GetDataPageSize()
+{
+ return 1 << aHdr.GetDataPageSize();
+}
+
+// Commit everything
+
+BOOL StgIo::CommitAll()
+{
+ // Store the data (all streams and the TOC)
+ if( pTOC->Store() )
+ {
+ if( Commit( NULL ) )
+ {
+ aHdr.SetDataFATStart( pDataFAT->GetStart() );
+ aHdr.SetDataFATSize( pDataFAT->GetPages() );
+ aHdr.SetTOCStart( pTOC->GetStart() );
+ if( aHdr.Store( *this ) )
+ {
+ pStrm->Flush();
+ ULONG n = pStrm->GetError();
+ SetError( n );
+#ifdef DBG_UTIL
+ if( n==0 ) ValidateFATs();
+#endif
+ return BOOL( n == 0 );
+ }
+ }
+ }
+ SetError( SVSTREAM_WRITE_ERROR );
+ return FALSE;
+}
+
+
+class EasyFat
+{
+ INT32 *pFat;
+ BOOL *pFree;
+ INT32 nPages;
+ INT32 nPageSize;
+
+public:
+ EasyFat( StgIo & rIo, StgStrm *pFatStream, INT32 nPSize );
+ ~EasyFat() { delete pFat; delete pFree; }
+ INT32 GetPageSize() { return nPageSize; }
+ INT32 Count() { return nPages; }
+ INT32 operator[]( INT32 nOffset ) { return pFat[ nOffset ]; }
+
+ ULONG Mark( INT32 nPage, INT32 nCount, INT32 nExpect );
+ BOOL HasUnrefChains();
+};
+
+EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, INT32 nPSize )
+{
+ nPages = pFatStream->GetSize() >> 2;
+ nPageSize = nPSize;
+ pFat = new INT32[ nPages ];
+ pFree = new BOOL[ nPages ];
+
+ StgPage *pPage = NULL;
+ INT32 nFatPageSize = (1 << rIo.aHdr.GetPageSize()) - 2;
+
+ for( INT32 nPage = 0; nPage < nPages; nPage++ )
+ {
+ if( ! (nPage % nFatPageSize) )
+ {
+ pFatStream->Pos2Page( nPage << 2 );
+ INT32 nPhysPage = pFatStream->GetPage();
+ pPage = rIo.Get( nPhysPage, TRUE );
+ }
+
+ pFat[ nPage ] = pPage->GetPage( short( nPage % nFatPageSize ) );
+ pFree[ nPage ] = TRUE;
+ }
+}
+
+BOOL EasyFat::HasUnrefChains()
+{
+ for( INT32 nPage = 0; nPage < nPages; nPage++ )
+ {
+ if( pFree[ nPage ] && pFat[ nPage ] != -1 )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+ULONG EasyFat::Mark( INT32 nPage, INT32 nCount, INT32 nExpect )
+{
+ if( nCount > 0 )
+ --nCount /= GetPageSize(), nCount++;
+
+ INT32 nCurPage = nPage;
+ while( nCount != 0 )
+ {
+ pFree[ nCurPage ] = FALSE;
+ nCurPage = pFat[ nCurPage ];
+ //Stream zu lang
+ if( nCurPage != nExpect && nCount == 1 )
+ return FAT_WRONGLENGTH;
+ //Stream zu kurz
+ if( nCurPage == nExpect && nCount != 1 && nCount != -1 )
+ return FAT_WRONGLENGTH;
+ // letzter Block bei Stream ohne Laenge
+ if( nCurPage == nExpect && nCount == -1 )
+ nCount = 1;
+ if( nCount != -1 )
+ nCount--;
+ // Naechster Block nicht in der FAT
+ if( nCount && ( nCurPage < 0 || nCurPage >= nPages ) )
+ return FAT_OUTOFBOUNDS;
+ }
+ return FAT_OK;
+}
+
+class Validator
+{
+ ULONG nError;
+
+ EasyFat aSmallFat;
+ EasyFat aFat;
+
+ StgIo &rIo;
+
+ ULONG ValidateMasterFATs();
+ ULONG ValidateDirectoryEntries();
+ ULONG FindUnrefedChains();
+ ULONG MarkAll( StgDirEntry *pEntry );
+
+public:
+
+ Validator( StgIo &rIo );
+ BOOL IsError() { return nError != 0; }
+};
+
+Validator::Validator( StgIo &rIoP )
+ : aSmallFat( rIoP, rIoP.pDataFAT, 1 << rIoP.aHdr.GetDataPageSize() ),
+ aFat( rIoP, rIoP.pFAT, 1 << rIoP.aHdr.GetPageSize() ),
+ rIo( rIoP )
+{
+ ULONG nErr = nError = FAT_OK;
+
+ if( ( nErr = ValidateMasterFATs() ) != FAT_OK )
+ nError = nErr;
+ else if( ( nErr = ValidateDirectoryEntries() ) != FAT_OK )
+ nError = nErr;
+ else if( ( nErr = FindUnrefedChains()) != FAT_OK )
+ nError = nErr;
+}
+
+ULONG Validator::ValidateMasterFATs()
+{
+ INT32 nCount = rIo.aHdr.GetFATSize();
+ ULONG nErr;
+ for( INT32 i = 0; i < nCount; i++ )
+ {
+ if( ( nErr = aFat.Mark(rIo.pFAT->GetPage( short(i), FALSE ), aFat.GetPageSize(), -3 )) != FAT_OK )
+ return nErr;
+ }
+ if( rIo.aHdr.GetMasters() )
+ if( ( nErr = aFat.Mark(rIo.aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FAT_OK )
+ return nErr;
+ return FAT_OK;
+}
+
+ULONG Validator::MarkAll( StgDirEntry *pEntry )
+{
+ StgIterator aIter( *pEntry );
+ ULONG nErr = FAT_OK;
+ for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() )
+ {
+ if( p->aEntry.GetType() == STG_STORAGE )
+ {
+ nErr = MarkAll( p );
+ if( nErr != FAT_OK )
+ return nErr;
+ }
+ else
+ {
+ INT32 nSize = p->aEntry.GetSize();
+ if( nSize < rIo.aHdr.GetThreshold() )
+ nErr = aSmallFat.Mark( p->aEntry.GetStartPage(),nSize, -2 );
+ else
+ nErr = aFat.Mark( p->aEntry.GetStartPage(),nSize, -2 );
+ if( nErr != FAT_OK )
+ return nErr;
+ }
+ }
+ return FAT_OK;
+}
+
+ULONG Validator::ValidateDirectoryEntries()
+{
+ // Normale DirEntries
+ ULONG nErr = MarkAll( rIo.pTOC->GetRoot() );
+ if( nErr != FAT_OK )
+ return nErr;
+ // Small Data
+ nErr = aFat.Mark( rIo.pTOC->GetRoot()->aEntry.GetStartPage(),
+ rIo.pTOC->GetRoot()->aEntry.GetSize(), -2 );
+ if( nErr != FAT_OK )
+ return nErr;
+ // Small Data FAT
+ nErr = aFat.Mark(
+ rIo.aHdr.GetDataFATStart(),
+ rIo.aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
+ if( nErr != FAT_OK )
+ return nErr;
+ // TOC
+ nErr = aFat.Mark(
+ rIo.aHdr.GetTOCStart(), -1, -2 );
+ return nErr;
+}
+
+ULONG Validator::FindUnrefedChains()
+{
+ if( aSmallFat.HasUnrefChains() ||
+ aFat.HasUnrefChains() )
+ return FAT_UNREFCHAIN;
+ else
+ return FAT_OK;
+}
+
+namespace { struct ErrorLink : public rtl::Static<Link, ErrorLink > {}; }
+
+void StgIo::SetErrorLink( const Link& rLink )
+{
+ ErrorLink::get() = rLink;
+}
+
+const Link& StgIo::GetErrorLink()
+{
+ return ErrorLink::get();
+}
+
+ULONG StgIo::ValidateFATs()
+{
+ if( bFile )
+ {
+ Validator *pV = new Validator( *this );
+ BOOL bRet1 = !pV->IsError(), bRet2 = TRUE ;
+ delete pV;
+ SvFileStream *pFileStrm = ( SvFileStream *) GetStrm();
+ StgIo aIo;
+ if( aIo.Open( pFileStrm->GetFileName(),
+ STREAM_READ | STREAM_SHARE_DENYNONE) &&
+ aIo.Load() )
+ {
+ pV = new Validator( aIo );
+ bRet2 = !pV->IsError();
+ delete pV;
+ }
+
+ ULONG nErr;
+ if( bRet1 != bRet2 )
+ nErr = bRet1 ? FAT_ONFILEERROR : FAT_INMEMORYERROR;
+ else nErr = bRet1 ? FAT_OK : FAT_BOTHERROR;
+ if( nErr != FAT_OK && !bCopied )
+ {
+ StgLinkArg aArg;
+ aArg.aFile = pFileStrm->GetFileName();
+ aArg.nErr = nErr;
+ ErrorLink::get().Call( &aArg );
+ bCopied = TRUE;
+ }
+// DBG_ASSERT( nErr == FAT_OK ,"Storage kaputt");
+ return nErr;
+ }
+// DBG_ERROR("Validiere nicht (kein FileStorage)");
+ return FAT_OK;
+}
+
+
diff --git a/sot/source/sdstor/stgio.hxx b/sot/source/sdstor/stgio.hxx
new file mode 100644
index 000000000000..db1c00b8d521
--- /dev/null
+++ b/sot/source/sdstor/stgio.hxx
@@ -0,0 +1,80 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _STGIO_HXX
+#define _STGIO_HXX
+
+#include <stgcache.hxx>
+#include <stgelem.hxx>
+#include <tools/string.hxx>
+
+class StgFATStrm;
+class StgDataStrm;
+class StgDirStrm;
+class String;
+
+enum FAT_ERROR
+{
+ FAT_OK,
+ FAT_WRONGLENGTH,
+ FAT_UNREFCHAIN,
+ FAT_OVERWRITE,
+ FAT_OUTOFBOUNDS,
+
+ FAT_INMEMORYERROR,
+ FAT_ONFILEERROR,
+ FAT_BOTHERROR
+};
+
+struct StgLinkArg
+{
+ String aFile;
+ ULONG nErr;
+};
+
+class StgIo : public StgCache {
+ void SetupStreams(); // load all internal streams
+ BOOL bCopied;
+public:
+ StgIo();
+ ~StgIo();
+ StgHeader aHdr; // storage file header
+ StgFATStrm* pFAT; // FAT stream
+ StgDirStrm* pTOC; // TOC stream
+ StgDataStrm* pDataFAT; // small data FAT stream
+ StgDataStrm* pDataStrm; // small data stream
+ short GetDataPageSize(); // get the logical data page size
+ BOOL Load(); // load a storage file
+ BOOL Init(); // set up an empty file
+ BOOL CommitAll(); // commit everything (root commit)
+
+ static void SetErrorLink( const Link& );
+ static const Link& GetErrorLink();
+ ULONG ValidateFATs( );
+};
+
+#endif
diff --git a/sot/source/sdstor/stgole.cxx b/sot/source/sdstor/stgole.cxx
new file mode 100644
index 000000000000..148f1e87585c
--- /dev/null
+++ b/sot/source/sdstor/stgole.cxx
@@ -0,0 +1,230 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include "rtl/string.h"
+#include "rtl/string.h"
+#include "stgole.hxx"
+#include "storinfo.hxx" // Read/WriteClipboardFormat()
+
+#include <tools/debug.hxx>
+#if defined(_MSC_VER) && (_MSC_VER>=1400)
+#pragma warning(disable: 4342)
+#endif
+///////////////////////// class StgInternalStream ////////////////////////
+
+StgInternalStream::StgInternalStream
+ ( BaseStorage& rStg, const String& rName, BOOL bWr )
+{
+ bIsWritable = TRUE;
+ USHORT nMode = bWr
+ ? STREAM_WRITE | STREAM_SHARE_DENYALL
+ : STREAM_READ | STREAM_SHARE_DENYWRITE | STREAM_NOCREATE;
+ pStrm = rStg.OpenStream( rName, nMode );
+
+ // set the error code right here in the stream
+ SetError( rStg.GetError() );
+ SetBufferSize( 1024 );
+}
+
+StgInternalStream::~StgInternalStream()
+{
+ delete pStrm;
+}
+
+ULONG StgInternalStream::GetData( void* pData, ULONG nSize )
+{
+ if( pStrm )
+ {
+ nSize = pStrm->Read( pData, nSize );
+ SetError( pStrm->GetError() );
+ return nSize;
+ }
+ else
+ return 0;
+}
+
+ULONG StgInternalStream::PutData( const void* pData, ULONG nSize )
+{
+ if( pStrm )
+ {
+ nSize = pStrm->Write( pData, nSize );
+ SetError( pStrm->GetError() );
+ return nSize;
+ }
+ else
+ return 0;
+}
+
+ULONG StgInternalStream::SeekPos( ULONG nPos )
+{
+ return pStrm ? pStrm->Seek( nPos ) : 0;
+}
+
+void StgInternalStream::FlushData()
+{
+ if( pStrm )
+ {
+ pStrm->Flush();
+ SetError( pStrm->GetError() );
+ }
+}
+
+void StgInternalStream::Commit()
+{
+ Flush();
+ pStrm->Commit();
+}
+
+///////////////////////// class StgCompObjStream /////////////////////////
+
+StgCompObjStream::StgCompObjStream( BaseStorage& rStg, BOOL bWr )
+ : StgInternalStream( rStg, String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "\1CompObj" ) ), bWr )
+{
+ memset( &aClsId, 0, sizeof( ClsId ) );
+ nCbFormat = 0;
+}
+
+BOOL StgCompObjStream::Load()
+{
+ memset( &aClsId, 0, sizeof( ClsId ) );
+ nCbFormat = 0;
+ aUserName.Erase();
+ if( GetError() != SVSTREAM_OK )
+ return FALSE;
+ Seek( 8L ); // skip the first part
+ INT32 nMarker = 0;
+ *this >> nMarker;
+ if( nMarker == -1L )
+ {
+ *this >> aClsId;
+ INT32 nLen1 = 0;
+ *this >> nLen1;
+ // higher bits are ignored
+ nLen1 &= 0xFFFF;
+ sal_Char* p = new sal_Char[ (USHORT) nLen1 ];
+ if( Read( p, nLen1 ) == (ULONG) nLen1 )
+ {
+ aUserName = nLen1 ? String( p, gsl_getSystemTextEncoding() ) : String();
+/* // Now we can read the CB format
+ INT32 nLen2 = 0;
+ *this >> nLen2;
+ if( nLen2 > 0 )
+ {
+ // get a string name
+ if( nLen2 > nLen1 )
+ delete p, p = new char[ nLen2 ];
+ if( Read( p, nLen2 ) == (ULONG) nLen2 && nLen2 )
+ nCbFormat = Exchange::RegisterFormatName( String( p ) );
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ }
+ else if( nLen2 == -1L )
+ // Windows clipboard format
+ *this >> nCbFormat;
+ else
+ // unknown identifier
+ SetError( SVSTREAM_GENERALERROR );
+*/
+ nCbFormat = ReadClipboardFormat( *this );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ delete [] p;
+ }
+ return BOOL( GetError() == SVSTREAM_OK );
+}
+
+BOOL StgCompObjStream::Store()
+{
+ if( GetError() != SVSTREAM_OK )
+ return FALSE;
+ Seek( 0L );
+ ByteString aAsciiUserName( aUserName, RTL_TEXTENCODING_ASCII_US );
+ *this << (INT16) 1 // Version?
+ << (INT16) -2 // 0xFFFE = Byte Order Indicator
+ << (INT32) 0x0A03 // Windows 3.10
+ << (INT32) -1L
+ << aClsId // Class ID
+ << (INT32) (aAsciiUserName.Len() + 1)
+ << (const char *)aAsciiUserName.GetBuffer()
+ << (UINT8) 0; // string terminator
+/* // determine the clipboard format string
+ String aCbFmt;
+ if( nCbFormat > FORMAT_GDIMETAFILE )
+ aCbFmt = Exchange::GetFormatName( nCbFormat );
+ if( aCbFmt.Len() )
+ *this << (INT32) aCbFmt.Len() + 1
+ << (const char*) aCbFmt
+ << (UINT8) 0;
+ else if( nCbFormat )
+ *this << (INT32) -1 // for Windows
+ << (INT32) nCbFormat;
+ else
+ *this << (INT32) 0; // no clipboard format
+*/
+ WriteClipboardFormat( *this, nCbFormat );
+ *this << (INT32) 0; // terminator
+ Commit();
+ return BOOL( GetError() == SVSTREAM_OK );
+}
+
+/////////////////////////// class StgOleStream ///////////////////////////
+
+StgOleStream::StgOleStream( BaseStorage& rStg, BOOL bWr )
+ : StgInternalStream( rStg, String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "\1Ole" ) ), bWr )
+{
+ nFlags = 0;
+}
+
+BOOL StgOleStream::Load()
+{
+ nFlags = 0;
+ if( GetError() != SVSTREAM_OK )
+ return FALSE;
+ INT32 version = 0;
+ Seek( 0L );
+ *this >> version >> nFlags;
+ return BOOL( GetError() == SVSTREAM_OK );
+}
+
+BOOL StgOleStream::Store()
+{
+ if( GetError() != SVSTREAM_OK )
+ return FALSE;
+ Seek( 0L );
+ *this << (INT32) 0x02000001 // OLE version, format
+ << (INT32) nFlags // Object flags
+ << (INT32) 0 // Update Options
+ << (INT32) 0 // reserved
+ << (INT32) 0; // Moniker 1
+ Commit();
+ return BOOL( GetError() == SVSTREAM_OK );
+}
+
diff --git a/sot/source/sdstor/stgole.hxx b/sot/source/sdstor/stgole.hxx
new file mode 100644
index 000000000000..346f21035f06
--- /dev/null
+++ b/sot/source/sdstor/stgole.hxx
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _SDSTOR_STGOLE_HXX
+#define _SDSTOR_STGOLE_HXX
+
+#include <string.h> // memset()
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+
+class StgInternalStream : public SvStream
+{
+ BaseStorageStream* pStrm;
+ virtual ULONG GetData( void* pData, ULONG nSize );
+ virtual ULONG PutData( const void* pData, ULONG nSize );
+ virtual ULONG SeekPos( ULONG nPos );
+ virtual void FlushData();
+public:
+ StgInternalStream( BaseStorage&, const String&, BOOL );
+ ~StgInternalStream();
+ void Commit();
+};
+
+// standard stream "\1CompObj"
+
+class StgCompObjStream : public StgInternalStream
+{
+ ClsId aClsId;
+ String aUserName;
+ ULONG nCbFormat;
+public:
+ StgCompObjStream( BaseStorage&, BOOL );
+ ClsId& GetClsId() { return aClsId; }
+ String& GetUserName() { return aUserName; }
+ ULONG& GetCbFormat() { return nCbFormat; }
+ BOOL Load();
+ BOOL Store();
+};
+
+// standard stream "\1Ole"
+
+class StgOleStream : public StgInternalStream
+{
+ sal_uInt32 nFlags;
+public:
+ StgOleStream( BaseStorage&, BOOL );
+ sal_uInt32& GetFlags() { return nFlags; }
+ BOOL Load();
+ BOOL Store();
+};
+
+#endif
diff --git a/sot/source/sdstor/stgstrms.cxx b/sot/source/sdstor/stgstrms.cxx
new file mode 100644
index 000000000000..07711133bf4b
--- /dev/null
+++ b/sot/source/sdstor/stgstrms.cxx
@@ -0,0 +1,1247 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include <string.h> // memcpy()
+
+#include <osl/file.hxx>
+#include <tools/tempfile.hxx>
+#include <tools/debug.hxx>
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+
+#if defined(W31)
+ #include <tools/svwin.h>
+ #define memcpy hmemcpy
+ #define __HUGE _huge
+#else
+ #define __HUGE
+#endif
+
+///////////////////////////// class StgFAT ///////////////////////////////
+
+// The FAT class performs FAT operations on an underlying storage stream.
+// This stream is either the master FAT stream (m == TRUE ) or a normal
+// storage stream, which then holds the FAT for small data allocations.
+
+StgFAT::StgFAT( StgStrm& r, BOOL m ) : rStrm( r )
+{
+ bPhys = m;
+ nPageSize = rStrm.GetIo().GetPhysPageSize();
+ nEntries = nPageSize >> 2;
+ nOffset = 0;
+ nMaxPage = 0;
+ nLimit = 0;
+}
+
+// Retrieve the physical page for a given byte offset.
+
+StgPage* StgFAT::GetPhysPage( INT32 nByteOff )
+{
+ StgPage* pPg = NULL;
+ // Position within the underlying stream
+ // use the Pos2Page() method of the stream
+ if( rStrm.Pos2Page( nByteOff ) )
+ {
+ nOffset = rStrm.GetOffset();
+ INT32 nPhysPage = rStrm.GetPage();
+ // get the physical page (must be present)
+ pPg = rStrm.GetIo().Get( nPhysPage, TRUE );
+ }
+ return pPg;
+}
+
+// Get the follow page for a certain FAT page.
+
+INT32 StgFAT::GetNextPage( INT32 nPg )
+{
+ if( nPg >= 0 )
+ {
+ StgPage* pPg = GetPhysPage( nPg << 2 );
+ nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF;
+ }
+ return nPg;
+}
+
+// Find the best fit block for the given size. Return
+// the starting block and its size or STG_EOF and 0.
+// nLastPage is a stopper which tells the current
+// underlying stream size. It is treated as a recommendation
+// to abort the search to inhibit excessive file growth.
+
+INT32 StgFAT::FindBlock( INT32& nPgs )
+{
+ INT32 nMinStart = STG_EOF, nMinLen = 0;
+ INT32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
+ INT32 nTmpStart = STG_EOF, nTmpLen = 0;
+ INT32 nPages = rStrm.GetSize() >> 2;
+ BOOL bFound = FALSE;
+ StgPage* pPg = NULL;
+ short nEntry = 0;
+ for( INT32 i = 0; i < nPages; i++, nEntry++ )
+ {
+ if( !( nEntry % nEntries ) )
+ {
+ // load the next page for that stream
+ nEntry = 0;
+ pPg = GetPhysPage( i << 2 );
+ if( !pPg )
+ return STG_EOF;
+ }
+ INT32 nCur = pPg->GetPage( nEntry );
+ if( nCur == STG_FREE )
+ {
+ // count the size of this area
+ if( nTmpLen )
+ nTmpLen++;
+ else
+ nTmpStart = i,
+ nTmpLen = 1;
+ if( nTmpLen == nPgs
+ // If we already did find a block, stop when reaching the limit
+ || ( bFound && ( nEntry >= nLimit ) ) )
+ break;
+ }
+ else if( nTmpLen )
+ {
+ if( nTmpLen > nPgs && nTmpLen < nMaxLen )
+ // block > requested size
+ nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = TRUE;
+ else if( nTmpLen >= nMinLen )
+ {
+ // block < requested size
+ nMinLen = nTmpLen, nMinStart = nTmpStart;
+ bFound = TRUE;
+ if( nTmpLen == nPgs )
+ break;
+ }
+ nTmpStart = STG_EOF;
+ nTmpLen = 0;
+ }
+ }
+ // Determine which block to use.
+ if( nTmpLen )
+ {
+ if( nTmpLen > nPgs && nTmpLen < nMaxLen )
+ // block > requested size
+ nMaxLen = nTmpLen, nMaxStart = nTmpStart;
+ else if( nTmpLen >= nMinLen )
+ // block < requested size
+ nMinLen = nTmpLen, nMinStart = nTmpStart;
+ }
+ if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
+ {
+ // two areas found; return the best fit area
+ INT32 nMinDiff = nPgs - nMinLen;
+ INT32 nMaxDiff = nMaxLen - nPgs;
+ if( nMinDiff > nMaxDiff )
+ nMinStart = STG_EOF;
+ }
+ if( nMinStart != STG_EOF )
+ {
+ nPgs = nMinLen; return nMinStart;
+ }
+ else
+ {
+ return nMaxStart;
+ }
+}
+
+// Set up the consecutive chain for a given block.
+
+BOOL StgFAT::MakeChain( INT32 nStart, INT32 nPgs )
+{
+ INT32 nPos = nStart << 2;
+ StgPage* pPg = GetPhysPage( nPos );
+ if( !pPg || !nPgs )
+ return FALSE;
+ while( --nPgs )
+ {
+ if( nOffset >= nPageSize )
+ {
+ pPg = GetPhysPage( nPos );
+ if( !pPg )
+ return FALSE;
+ }
+ pPg->SetPage( nOffset >> 2, ++nStart );
+ nOffset += 4;
+ nPos += 4;
+ }
+ if( nOffset >= nPageSize )
+ {
+ pPg = GetPhysPage( nPos );
+ if( !pPg )
+ return FALSE;
+ }
+ pPg->SetPage( nOffset >> 2, STG_EOF );
+ return TRUE;
+}
+
+// Allocate a block of data from the given page number on.
+// It the page number is != STG_EOF, chain the block.
+
+INT32 StgFAT::AllocPages( INT32 nBgn, INT32 nPgs )
+{
+ INT32 nOrig = nBgn;
+ INT32 nLast = nBgn;
+ INT32 nBegin = STG_EOF;
+ INT32 nAlloc;
+ INT32 nPages = rStrm.GetSize() >> 2;
+ short nPasses = 0;
+ // allow for two passes
+ while( nPasses < 2 )
+ {
+ // try to satisfy the request from the pool of free pages
+ while( nPgs )
+ {
+ nAlloc = nPgs;
+ nBegin = FindBlock( nAlloc );
+ // no more blocks left in present alloc chain
+ if( nBegin == STG_EOF )
+ break;
+ if( ( nBegin + nAlloc ) > nMaxPage )
+ nMaxPage = nBegin + nAlloc;
+ if( !MakeChain( nBegin, nAlloc ) )
+ return STG_EOF;
+ if( nOrig == STG_EOF )
+ nOrig = nBegin;
+ else
+ {
+ // Patch the chain
+ StgPage* pPg = GetPhysPage( nLast << 2 );
+ if( !pPg )
+ return STG_EOF;
+ pPg->SetPage( nOffset >> 2, nBegin );
+ }
+ nLast = nBegin + nAlloc - 1;
+ nPgs -= nAlloc;
+ }
+ if( nPgs && !nPasses )
+ {
+ // we need new, fresh space, so allocate and retry
+ if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
+ return STG_EOF;
+ if( !bPhys && !InitNew( nPages ) )
+ return FALSE;
+ nPages = rStrm.GetSize() >> 2;
+ nPasses++;
+ }
+ else
+ break;
+ }
+ // now we should have a chain for the complete block
+ if( nBegin == STG_EOF || nPgs )
+ {
+ rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return STG_EOF; // bad structure
+ }
+ return nOrig;
+}
+
+// Initialize newly allocated pages for a standard FAT stream
+// It can be assumed that the stream size is always on
+// a page boundary
+
+BOOL StgFAT::InitNew( INT32 nPage1 )
+{
+ INT32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
+ while( n-- )
+ {
+ StgPage* pPg = NULL;
+ // Position within the underlying stream
+ // use the Pos2Page() method of the stream
+ rStrm.Pos2Page( nPage1 << 2 );
+ // Initialize the page
+ pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
+ if ( !pPg )
+ return FALSE;
+ for( short i = 0; i < nEntries; i++ )
+ pPg->SetPage( i, STG_FREE );
+ nPage1++;
+ }
+ return TRUE;
+}
+
+// Release a chain
+
+BOOL StgFAT::FreePages( INT32 nStart, BOOL bAll )
+{
+ while( nStart >= 0 )
+ {
+ StgPage* pPg = GetPhysPage( nStart << 2 );
+ if( !pPg )
+ return FALSE;
+ nStart = pPg->GetPage( nOffset >> 2 );
+ // The first released page is either set to EOF or FREE
+ pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF );
+ bAll = TRUE;
+ }
+ return TRUE;
+}
+
+///////////////////////////// class StgStrm ////////////////////////////////
+
+// The base stream class provides basic functionality for seeking
+// and accessing the data on a physical basis. It uses the built-in
+// FAT class for the page allocations.
+
+StgStrm::StgStrm( StgIo& r ) : rIo( r )
+{
+ pFat = NULL;
+ nStart = nPage = STG_EOF;
+ nOffset = 0;
+ pEntry = NULL;
+ nPos = nSize = 0;
+ nPageSize = rIo.GetPhysPageSize();
+}
+
+StgStrm::~StgStrm()
+{
+ delete pFat;
+}
+
+// Attach the stream to the given entry.
+
+void StgStrm::SetEntry( StgDirEntry& r )
+{
+ r.aEntry.SetLeaf( STG_DATA, nStart );
+ r.aEntry.SetSize( nSize );
+ pEntry = &r;
+ r.SetDirty();
+}
+
+// Compute page number and offset for the given byte position.
+// If the position is behind the size, set the stream right
+// behind the EOF.
+
+BOOL StgStrm::Pos2Page( INT32 nBytePos )
+{
+ INT32 nRel, nBgn;
+ // Values < 0 seek to the end
+ if( nBytePos < 0 || nBytePos >= nSize )
+ nBytePos = nSize;
+ // Adjust the position back to offset 0
+ nPos -= nOffset;
+ INT32 nMask = ~( nPageSize - 1 );
+ INT32 nOld = nPos & nMask;
+ INT32 nNew = nBytePos & nMask;
+ nOffset = (short) ( nBytePos & ~nMask );
+ nPos = nBytePos;
+ if( nOld == nNew )
+ return TRUE;
+ if( nNew > nOld )
+ {
+ // the new position is behind the current, so an incremental
+ // positioning is OK. Set the page relative position
+ nRel = nNew - nOld;
+ nBgn = nPage;
+ }
+ else
+ {
+ // the new position is before the current, so we have to scan
+ // the entire chain.
+ nRel = nNew;
+ nBgn = nStart;
+ }
+ // now, traverse the FAT chain.
+ nRel /= nPageSize;
+ INT32 nLast = STG_EOF;
+ while( nRel && nBgn >= 0 )
+ {
+ nLast = nBgn;
+ nBgn = pFat->GetNextPage( nBgn );
+ nRel--;
+ }
+ // special case: seek to 1st byte of new, unallocated page
+ // (in case the file size is a multiple of the page size)
+ if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset )
+ nBgn = nLast, nOffset = nPageSize;
+ if( nBgn < 0 && nBgn != STG_EOF )
+ {
+ rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ nBgn = STG_EOF;
+ nOffset = nPageSize;
+ }
+ nPage = nBgn;
+ return BOOL( nRel == 0 && nPage >= 0 );
+}
+
+// Retrieve the physical page for a given byte offset.
+
+StgPage* StgStrm::GetPhysPage( INT32 nBytePos, BOOL bForce )
+{
+ if( !Pos2Page( nBytePos ) )
+ return NULL;
+ return rIo.Get( nPage, bForce );
+}
+
+// Copy an entire stream. Both streams are allocated in the FAT.
+// The target stream is this stream.
+
+BOOL StgStrm::Copy( INT32 nFrom, INT32 nBytes )
+{
+ INT32 nTo = nStart;
+ INT32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
+ while( nPgs-- )
+ {
+ if( nTo < 0 )
+ {
+ rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return FALSE;
+ }
+ rIo.Copy( nTo, nFrom );
+ if( nFrom >= 0 )
+ {
+ nFrom = pFat->GetNextPage( nFrom );
+ if( nFrom < 0 )
+ {
+ rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return FALSE;
+ }
+ }
+ nTo = pFat->GetNextPage( nTo );
+ }
+ return TRUE;
+}
+
+BOOL StgStrm::SetSize( INT32 nBytes )
+{
+ // round up to page size
+ INT32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
+ INT32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
+ if( nNew > nOld )
+ {
+ if( !Pos2Page( nSize ) )
+ return FALSE;
+ INT32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
+ if( nBgn == STG_EOF )
+ return FALSE;
+ if( nStart == STG_EOF )
+ nStart = nPage = nBgn;
+ }
+ else if( nNew < nOld )
+ {
+ BOOL bAll = BOOL( nBytes == 0 );
+ if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
+ return FALSE;
+ if( bAll )
+ nStart = nPage = STG_EOF;
+ }
+ if( pEntry )
+ {
+ // change the dir entry?
+ if( !nSize || !nBytes )
+ pEntry->aEntry.SetLeaf( STG_DATA, nStart );
+ pEntry->aEntry.SetSize( nBytes );
+ pEntry->SetDirty();
+ }
+ nSize = nBytes;
+ pFat->SetLimit( GetPages() );
+ return TRUE;
+}
+
+// Return the # of allocated pages
+
+INT32 StgStrm::GetPages()
+{
+ return ( nSize + nPageSize - 1 ) / nPageSize;
+}
+
+//////////////////////////// class StgFATStrm //////////////////////////////
+
+// The FAT stream class provides physical access to the master FAT.
+// Since this access is implemented as a StgStrm, we can use the
+// FAT allocator.
+
+StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
+{
+ pFat = new StgFAT( *this, TRUE );
+ nSize = rIo.aHdr.GetFATSize() * nPageSize;
+}
+
+BOOL StgFATStrm::Pos2Page( INT32 nBytePos )
+{
+ // Values < 0 seek to the end
+ if( nBytePos < 0 || nBytePos >= nSize )
+ nBytePos = nSize ? nSize - 1 : 0;
+ nPage = nBytePos / nPageSize;
+ nOffset = (short) ( nBytePos % nPageSize );
+ nPos = nBytePos;
+ nPage = GetPage( (short) nPage, FALSE );
+ return BOOL( nPage >= 0 );
+}
+
+// Retrieve the physical page for a given byte offset.
+// Since Pos2Page() already has computed the physical offset,
+// use the byte offset directly.
+
+StgPage* StgFATStrm::GetPhysPage( INT32 nBytePos, BOOL bForce )
+{
+ return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
+}
+
+// Get the page number entry for the given page offset.
+
+INT32 StgFATStrm::GetPage( short nOff, BOOL bMake, USHORT *pnMasterAlloc )
+{
+ if( pnMasterAlloc ) *pnMasterAlloc = 0;
+ if( nOff < rIo.aHdr.GetFAT1Size() )
+ return rIo.aHdr.GetFATPage( nOff );
+ INT32 nMaxPage = nSize >> 2;
+ nOff = nOff - rIo.aHdr.GetFAT1Size();
+ // Anzahl der Masterpages, durch die wir iterieren muessen
+ USHORT nMasterCount = ( nPageSize >> 2 ) - 1;
+ USHORT nBlocks = nOff / nMasterCount;
+ // Offset in letzter Masterpage
+ nOff = nOff % nMasterCount;
+
+ StgPage* pOldPage = 0;
+ StgPage* pMaster = 0;
+ INT32 nFAT = rIo.aHdr.GetFATChain();
+ for( USHORT nCount = 0; nCount <= nBlocks; nCount++ )
+ {
+ if( nFAT == STG_EOF || nFAT == STG_FREE )
+ {
+ if( bMake )
+ {
+ // create a new master page
+ nFAT = nMaxPage++;
+ pMaster = rIo.Copy( nFAT, STG_FREE );
+ if ( pMaster )
+ {
+ for( short k = 0; k < ( nPageSize >> 2 ); k++ )
+ pMaster->SetPage( k, STG_FREE );
+ // Verkettung herstellen
+ if( !pOldPage )
+ rIo.aHdr.SetFATChain( nFAT );
+ else
+ pOldPage->SetPage( nMasterCount, nFAT );
+ if( nMaxPage >= rIo.GetPhysPages() )
+ if( !rIo.SetSize( nMaxPage ) )
+ return STG_EOF;
+ // mark the page as used
+ // Platz fuer Masterpage schaffen
+ if( !pnMasterAlloc ) // Selbst Platz schaffen
+ {
+ if( !Pos2Page( nFAT << 2 ) )
+ return STG_EOF;
+ StgPage* pPg = rIo.Get( nPage, TRUE );
+ if( !pPg )
+ return STG_EOF;
+ pPg->SetPage( nOffset >> 2, STG_MASTER );
+ }
+ else
+ (*pnMasterAlloc)++;
+ rIo.aHdr.SetMasters( nCount + 1 );
+ pOldPage = pMaster;
+ }
+ }
+ }
+ else
+ {
+ pMaster = rIo.Get( nFAT, TRUE );
+ if ( pMaster )
+ {
+ nFAT = pMaster->GetPage( nMasterCount );
+ pOldPage = pMaster;
+ }
+ }
+ }
+ if( pMaster )
+ return pMaster->GetPage( nOff );
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ return STG_EOF;
+}
+
+
+// Set the page number entry for the given page offset.
+
+BOOL StgFATStrm::SetPage( short nOff, INT32 nNewPage )
+{
+ BOOL bRes = TRUE;
+ if( nOff < rIo.aHdr.GetFAT1Size() )
+ rIo.aHdr.SetFATPage( nOff, nNewPage );
+ else
+ {
+ nOff = nOff - rIo.aHdr.GetFAT1Size();
+ // Anzahl der Masterpages, durch die wir iterieren muessen
+ USHORT nMasterCount = ( nPageSize >> 2 ) - 1;
+ USHORT nBlocks = nOff / nMasterCount;
+ // Offset in letzter Masterpage
+ nOff = nOff % nMasterCount;
+
+ StgPage* pMaster = 0;
+ INT32 nFAT = rIo.aHdr.GetFATChain();
+ for( USHORT nCount = 0; nCount <= nBlocks; nCount++ )
+ {
+ if( nFAT == STG_EOF || nFAT == STG_FREE )
+ {
+ pMaster = 0;
+ break;
+ }
+ pMaster = rIo.Get( nFAT, TRUE );
+ if ( pMaster )
+ nFAT = pMaster->GetPage( nMasterCount );
+ }
+ if( pMaster )
+ pMaster->SetPage( nOff, nNewPage );
+ else
+ {
+ rIo.SetError( SVSTREAM_GENERALERROR );
+ bRes = FALSE;
+ }
+ }
+
+ // lock the page against access
+ if( bRes )
+ {
+ Pos2Page( nNewPage << 2 );
+ StgPage* pPg = rIo.Get( nPage, TRUE );
+ if( pPg )
+ pPg->SetPage( nOffset >> 2, STG_FAT );
+ else
+ bRes = FALSE;
+ }
+ return bRes;
+}
+
+BOOL StgFATStrm::SetSize( INT32 nBytes )
+{
+ // Set the number of entries to a multiple of the page size
+ short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
+ short nNew = (short) (
+ ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
+ if( nNew < nOld )
+ {
+ // release master pages
+ for( short i = nNew; i < nOld; i++ )
+ SetPage( i, STG_FREE );
+ }
+ else
+ {
+ while( nOld < nNew )
+ {
+ // allocate master pages
+ // find a free master page slot
+ INT32 nPg = 0;
+ USHORT nMasterAlloc = 0;
+ nPg = GetPage( nOld, TRUE, &nMasterAlloc );
+ if( nPg == STG_EOF )
+ return FALSE;
+ // 4 Bytes have been used for Allocation of each MegaMasterPage
+ nBytes += nMasterAlloc << 2;
+
+ // find a free page using the FAT allocator
+ INT32 n = 1;
+ INT32 nNewPage = pFat->FindBlock( n );
+ if( nNewPage == STG_EOF )
+ {
+ // no free pages found; create a new page
+ // Since all pages are allocated, extend
+ // the file size for the next page!
+ nNewPage = nSize >> 2;
+ // if a MegaMasterPage was created avoid taking
+ // the same Page
+ nNewPage += nMasterAlloc;
+ // adjust the file size if necessary
+ if( nNewPage >= rIo.GetPhysPages() )
+ if( !rIo.SetSize( nNewPage + 1 ) )
+ return FALSE;
+ }
+ // Set up the page with empty entries
+ StgPage* pPg = rIo.Copy( nNewPage, STG_FREE );
+ if ( !pPg )
+ return FALSE;
+ for( short j = 0; j < ( nPageSize >> 2 ); j++ )
+ pPg->SetPage( j, STG_FREE );
+
+ // store the page number into the master FAT
+ // Set the size before so the correct FAT can be found
+ nSize = ( nOld + 1 ) * nPageSize;
+ SetPage( nOld, nNewPage );
+
+ // MegaMasterPages were created, mark it them as used
+
+ UINT32 nMax = rIo.aHdr.GetMasters( );
+ UINT32 nFAT = rIo.aHdr.GetFATChain();
+ if( nMasterAlloc )
+ for( USHORT nCount = 0; nCount < nMax; nCount++ )
+ {
+ if( !Pos2Page( nFAT << 2 ) )
+ return FALSE;
+ if( nMax - nCount <= nMasterAlloc )
+ {
+ StgPage* piPg = rIo.Get( nPage, TRUE );
+ if( !piPg )
+ return FALSE;
+ piPg->SetPage( nOffset >> 2, STG_MASTER );
+ }
+ StgPage* pPage = rIo.Get( nFAT, TRUE );
+ if( !pPage ) return FALSE;
+ nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 );
+ }
+
+ nOld++;
+ // We have used up 4 bytes for the STG_FAT entry
+ nBytes += 4;
+ nNew = (short) (
+ ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
+ }
+ }
+ nSize = nNew * nPageSize;
+ rIo.aHdr.SetFATSize( nNew );
+ return TRUE;
+}
+
+/////////////////////////// class StgDataStrm //////////////////////////////
+
+// This class is a normal physical stream which can be initialized
+// either with an existing dir entry or an existing FAT chain.
+// The stream has a size increment which normally is 1, but which can be
+// set to any value is you want the size to be incremented by certain values.
+
+StgDataStrm::StgDataStrm( StgIo& r, INT32 nBgn, INT32 nLen ) : StgStrm( r )
+{
+ Init( nBgn, nLen );
+}
+
+StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r )
+{
+ pEntry = p;
+ Init( p->aEntry.GetLeaf( STG_DATA ),
+ p->aEntry.GetSize() );
+}
+
+void StgDataStrm::Init( INT32 nBgn, INT32 nLen )
+{
+ pFat = new StgFAT( *rIo.pFAT, TRUE );
+ nStart = nPage = nBgn;
+ nSize = nLen;
+ nIncr = 1;
+ nOffset = 0;
+ if( nLen < 0 )
+ {
+ // determine the actual size of the stream by scanning
+ // the FAT chain and counting the # of pages allocated
+ nSize = 0;
+ INT32 nOldBgn = -1;
+ while( nBgn >= 0 && nBgn != nOldBgn )
+ {
+ nOldBgn = nBgn;
+ nBgn = pFat->GetNextPage( nBgn );
+ if( nBgn == nOldBgn )
+ rIo.SetError( ERRCODE_IO_WRONGFORMAT );
+ nSize += nPageSize;
+ }
+ }
+}
+
+// Set the size of a physical stream.
+
+BOOL StgDataStrm::SetSize( INT32 nBytes )
+{
+ nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
+ INT32 nOldSz = nSize;
+ if( ( nOldSz != nBytes ) )
+ {
+ if( !StgStrm::SetSize( nBytes ) )
+ return FALSE;
+ INT32 nMaxPage = pFat->GetMaxPage();
+ if( nMaxPage > rIo.GetPhysPages() )
+ if( !rIo.SetSize( nMaxPage ) )
+ return FALSE;
+ // If we only allocated one page or less, create this
+ // page in the cache for faster throughput. The current
+ // position is the former EOF point.
+ if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
+ {
+ Pos2Page( nBytes );
+ if( nPage >= 0 )
+ rIo.Copy( nPage, STG_FREE );
+ }
+ }
+ return TRUE;
+}
+
+// Get the address of the data byte at a specified offset.
+// If bForce = TRUE, a read of non-existent data causes
+// a read fault.
+
+void* StgDataStrm::GetPtr( INT32 Pos, BOOL bForce, BOOL bDirty )
+{
+ if( Pos2Page( Pos ) )
+ {
+ StgPage* pPg = rIo.Get( nPage, bForce );
+ if( pPg )
+ {
+ pPg->SetOwner( pEntry );
+ if( bDirty )
+ pPg->SetDirty();
+ return ((BYTE *)pPg->GetData()) + nOffset;
+ }
+ }
+ return NULL;
+}
+
+// This could easily be adapted to a better algorithm by determining
+// the amount of consecutable blocks before doing a read. The result
+// is the number of bytes read. No error is generated on EOF.
+
+INT32 StgDataStrm::Read( void* pBuf, INT32 n )
+{
+ if ( n < 0 )
+ return 0;
+
+ if( ( nPos + n ) > nSize )
+ n = nSize - nPos;
+ INT32 nDone = 0;
+ while( n )
+ {
+ short nBytes = nPageSize - nOffset;
+ short nRes;
+ StgPage* pPg;
+ if( (INT32) nBytes > n )
+ nBytes = (short) n;
+ if( nBytes )
+ {
+ void *p = (BYTE *) pBuf + nDone;
+ if( nBytes == nPageSize )
+ {
+ pPg = rIo.Find( nPage );
+ if( pPg )
+ {
+ // data is present, so use the cached data
+ pPg->SetOwner( pEntry );
+ memcpy( p, pPg->GetData(), nBytes );
+ nRes = nBytes;
+ }
+ else
+ // do a direct (unbuffered) read
+ nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
+ }
+ else
+ {
+ // partial block read thru the cache.
+ pPg = rIo.Get( nPage, FALSE );
+ if( !pPg )
+ break;
+ pPg->SetOwner( pEntry );
+ memcpy( p, (BYTE*)pPg->GetData() + nOffset, nBytes );
+ nRes = nBytes;
+ }
+ nDone += nRes;
+ nPos += nRes;
+ n -= nRes;
+ nOffset = nOffset + nRes;
+ if( nRes != nBytes )
+ break; // read error or EOF
+ }
+ // Switch to next page if necessary
+ if( nOffset >= nPageSize && !Pos2Page( nPos ) )
+ break;
+ }
+ return nDone;
+}
+
+INT32 StgDataStrm::Write( const void* pBuf, INT32 n )
+{
+ INT32 nDone = 0;
+ if( ( nPos + n ) > nSize )
+ {
+ INT32 nOld = nPos;
+ if( !SetSize( nPos + n ) )
+ return FALSE;
+ Pos2Page( nOld );
+ }
+ while( n )
+ {
+ short nBytes = nPageSize - nOffset;
+ short nRes;
+ StgPage* pPg;
+ if( (INT32) nBytes > n )
+ nBytes = (short) n;
+ if( nBytes )
+ {
+ const void *p = (const BYTE *) pBuf + nDone;
+ if( nBytes == nPageSize )
+ {
+ pPg = rIo.Find( nPage );
+ if( pPg )
+ {
+ // data is present, so use the cached data
+ pPg->SetOwner( pEntry );
+ memcpy( pPg->GetData(), p, nBytes );
+ pPg->SetDirty();
+ nRes = nBytes;
+ }
+ else
+ // do a direct (unbuffered) write
+ nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
+ }
+ else
+ {
+ // partial block read thru the cache.
+ pPg = rIo.Get( nPage, FALSE );
+ if( !pPg )
+ break;
+ pPg->SetOwner( pEntry );
+ memcpy( (BYTE*)pPg->GetData() + nOffset, p, nBytes );
+ pPg->SetDirty();
+ nRes = nBytes;
+ }
+ nDone += nRes;
+ nPos += nRes;
+ n -= nRes;
+ nOffset = nOffset + nRes;
+ if( nRes != nBytes )
+ break; // read error
+ }
+ // Switch to next page if necessary
+ if( nOffset >= nPageSize && !Pos2Page( nPos ) )
+ break;
+ }
+ return nDone;
+}
+
+//////////////////////////// class StgSmallStream ///////////////////////////
+
+// The small stream class provides access to streams with a size < 4096 bytes.
+// This stream is a StgStream containing small pages. The FAT for this stream
+// is also a StgStream. The start of the FAT is in the header at DataRootPage,
+// the stream itself is pointed to by the root entry (it holds start & size).
+
+StgSmallStrm::StgSmallStrm( StgIo& r, INT32 nBgn, INT32 nLen ) : StgStrm( r )
+{
+ Init( nBgn, nLen );
+}
+
+StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r )
+{
+ pEntry = p;
+ Init( p->aEntry.GetLeaf( STG_DATA ),
+ p->aEntry.GetSize() );
+}
+
+void StgSmallStrm::Init( INT32 nBgn, INT32 nLen )
+{
+ pFat = new StgFAT( *rIo.pDataFAT, FALSE );
+ pData = rIo.pDataStrm;
+ nPageSize = rIo.GetDataPageSize();
+ nStart =
+ nPage = nBgn;
+ nSize = nLen;
+}
+
+// This could easily be adapted to a better algorithm by determining
+// the amount of consecutable blocks before doing a read. The result
+// is the number of bytes read. No error is generated on EOF.
+
+INT32 StgSmallStrm::Read( void* pBuf, INT32 n )
+{
+ // We can safely assume that reads are not huge, since the
+ // small stream is likely to be < 64 KBytes.
+ if( ( nPos + n ) > nSize )
+ n = nSize - nPos;
+ short nDone = 0;
+ while( n )
+ {
+ short nBytes = nPageSize - nOffset;
+ if( (INT32) nBytes > n )
+ nBytes = (short) n;
+ if( nBytes )
+ {
+ if( !pData->Pos2Page( nPage * nPageSize + nOffset ) )
+ break;
+ // all reading thru the stream
+ short nRes = (short) pData->Read( (BYTE*)pBuf + nDone, nBytes );
+ nDone = nDone + nRes;
+ nPos += nRes;
+ n -= nRes;
+ nOffset = nOffset + nRes;
+ // read problem?
+ if( nRes != nBytes )
+ break;
+ }
+ // Switch to next page if necessary
+ if( nOffset >= nPageSize && !Pos2Page( nPos ) )
+ break;
+ }
+ return nDone;
+}
+
+INT32 StgSmallStrm::Write( const void* pBuf, INT32 n )
+{
+ // you can safely assume that reads are not huge, since the
+ // small stream is likely to be < 64 KBytes.
+ short nDone = 0;
+ if( ( nPos + n ) > nSize )
+ {
+ INT32 nOld = nPos;
+ if( !SetSize( nPos + n ) )
+ return FALSE;
+ Pos2Page( nOld );
+ }
+ while( n )
+ {
+ short nBytes = nPageSize - nOffset;
+ if( (INT32) nBytes > n )
+ nBytes = (short) n;
+ if( nBytes )
+ {
+ // all writing goes thru the stream
+ INT32 nDataPos = nPage * nPageSize + nOffset;
+ if( pData->GetSize() < ( nDataPos + nBytes ) )
+ if( !pData->SetSize( nDataPos + nBytes ) )
+ break;
+ if( !pData->Pos2Page( nDataPos ) )
+ break;
+ short nRes = (short) pData->Write( (BYTE*)pBuf + nDone, nBytes );
+ nDone = nDone + nRes;
+ nPos += nRes;
+ n -= nRes;
+ nOffset = nOffset + nRes;
+ // write problem?
+ if( nRes != nBytes )
+ break;
+ }
+ // Switch to next page if necessary
+ if( nOffset >= nPageSize && !Pos2Page( nPos ) )
+ break;
+ }
+ return nDone;
+}
+
+/////////////////////////// class StgTmpStrm /////////////////////////////
+
+// The temporary stream uses a memory stream if < 32K, otherwise a
+// temporary file.
+
+#define THRESHOLD 32768L
+
+StgTmpStrm::StgTmpStrm( ULONG nInitSize )
+ : SvMemoryStream( nInitSize > THRESHOLD
+ ? 16
+ : ( nInitSize ? nInitSize : 16 ), 4096 )
+{
+ pStrm = NULL;
+ // this calls FlushData, so all members should be set by this time
+ SetBufferSize( 0 );
+ if( nInitSize > THRESHOLD )
+ SetSize( nInitSize );
+}
+
+BOOL StgTmpStrm::Copy( StgTmpStrm& rSrc )
+{
+ ULONG n = rSrc.GetSize();
+ ULONG nCur = rSrc.Tell();
+ SetSize( n );
+ if( GetError() == SVSTREAM_OK )
+ {
+ BYTE* p = new BYTE[ 4096 ];
+ rSrc.Seek( 0L );
+ Seek( 0L );
+ while( n )
+ {
+ ULONG nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( rSrc.Read( p, nn ) != nn )
+ break;
+ if( Write( p, nn ) != nn )
+ break;
+ n -= nn;
+ }
+ delete [] p;
+ rSrc.Seek( nCur );
+ Seek( nCur );
+ return BOOL( n == 0 );
+ }
+ else
+ return FALSE;
+}
+
+StgTmpStrm::~StgTmpStrm()
+{
+ if( pStrm )
+ {
+ pStrm->Close();
+ osl::File::remove( aName );
+ delete pStrm;
+ }
+}
+
+ULONG StgTmpStrm::GetSize() const
+{
+ ULONG n;
+ if( pStrm )
+ {
+ ULONG old = pStrm->Tell();
+ n = pStrm->Seek( STREAM_SEEK_TO_END );
+ pStrm->Seek( old );
+ }
+ else
+ n = nEndOfData;
+ return n;
+}
+
+void StgTmpStrm::SetSize( ULONG n )
+{
+ if( pStrm )
+ pStrm->SetStreamSize( n );
+ else
+ {
+ if( n > THRESHOLD )
+ {
+ aName = TempFile::CreateTempName();
+ SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
+ ULONG nCur = Tell();
+ ULONG i = nEndOfData;
+ if( i )
+ {
+ BYTE* p = new BYTE[ 4096 ];
+ Seek( 0L );
+ while( i )
+ {
+ ULONG nb = ( i > 4096 ) ? 4096 : i;
+ if( Read( p, nb ) == nb
+ && s->Write( p, nb ) == nb )
+ i -= nb;
+ else
+ break;
+ }
+ delete [] p;
+ }
+ if( !i && n > nEndOfData )
+ {
+ // We have to write one byte at the end of the file
+ // if the file is bigger than the memstream to see
+ // if it fits on disk
+ s->Seek( n - 1 );
+ s->Write( &i, 1 );
+ s->Flush();
+ if( s->GetError() != SVSTREAM_OK )
+ i = 1;
+ }
+ Seek( nCur );
+ s->Seek( nCur );
+ if( i )
+ {
+ SetError( s->GetError() );
+ delete s;
+ return;
+ }
+ pStrm = s;
+ // Shrink the memory to 16 bytes, which seems to be the minimum
+ ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
+ }
+ else
+ {
+ if( n > nEndOfData )
+ {
+ ULONG nCur = Tell();
+ Seek( nEndOfData - 1 );
+ *this << (BYTE) 0;
+ Seek( nCur );
+ }
+ else
+ nEndOfData = n;
+ }
+ }
+}
+
+ULONG StgTmpStrm::GetData( void* pData, ULONG n )
+{
+ if( pStrm )
+ {
+ n = pStrm->Read( pData, n );
+ SetError( pStrm->GetError() );
+ return n;
+ }
+ else
+ return SvMemoryStream::GetData( (sal_Char *)pData, n );
+}
+
+ULONG StgTmpStrm::PutData( const void* pData, ULONG n )
+{
+ UINT32 nCur = Tell();
+ UINT32 nNew = nCur + n;
+ if( nNew > THRESHOLD && !pStrm )
+ {
+ SetSize( nNew );
+ if( GetError() != SVSTREAM_OK )
+ return 0;
+ }
+ if( pStrm )
+ {
+ nNew = pStrm->Write( pData, n );
+ SetError( pStrm->GetError() );
+ }
+ else
+ nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
+ return nNew;
+}
+
+ULONG StgTmpStrm::SeekPos( ULONG n )
+{
+ if( n == STREAM_SEEK_TO_END )
+ n = GetSize();
+ if( n && n > THRESHOLD && !pStrm )
+ {
+ SetSize( n );
+ if( GetError() != SVSTREAM_OK )
+ return Tell();
+ else
+ return n;
+ }
+ else if( pStrm )
+ {
+ n = pStrm->Seek( n );
+ SetError( pStrm->GetError() );
+ return n;
+ }
+ else
+ return SvMemoryStream::SeekPos( n );
+}
+
+void StgTmpStrm::FlushData()
+{
+ if( pStrm )
+ {
+ pStrm->Flush();
+ SetError( pStrm->GetError() );
+ }
+ else
+ SvMemoryStream::FlushData();
+}
+
diff --git a/sot/source/sdstor/stgstrms.hxx b/sot/source/sdstor/stgstrms.hxx
new file mode 100644
index 000000000000..9b09f51db43f
--- /dev/null
+++ b/sot/source/sdstor/stgstrms.hxx
@@ -0,0 +1,170 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _STGSTRMS_HXX
+#define _STGSTRMS_HXX
+
+#ifndef _TOOLS_STREAM_HXX
+#include <tools/stream.hxx>
+#endif
+
+class StgIo;
+class StgStrm;
+class StgPage;
+class StgDirEntry;
+
+// The FAT class performs FAT operations on an underlying storage stream.
+// This stream is either the physical FAT stream (bPhys == TRUE ) or a normal
+// storage stream, which then holds the FAT for small data allocations.
+
+class StgFAT
+{ // FAT allocator
+ StgStrm& rStrm; // underlying stream
+ INT32 nMaxPage; // highest page allocated so far
+ short nPageSize; // physical page size
+ short nEntries; // FAT entries per page
+ short nOffset; // current offset within page
+ INT32 nLimit; // search limit recommendation
+ BOOL bPhys; // TRUE: physical FAT
+ StgPage* GetPhysPage( INT32 nPage );
+ BOOL MakeChain( INT32 nStart, INT32 nPages );
+ BOOL InitNew( INT32 nPage1 );
+public:
+ StgFAT( StgStrm& rStrm, BOOL bMark );
+ INT32 FindBlock( INT32& nPages );
+ INT32 GetNextPage( INT32 nPg );
+ INT32 AllocPages( INT32 nStart, INT32 nPages );
+ BOOL FreePages( INT32 nStart, BOOL bAll );
+ INT32 GetMaxPage() { return nMaxPage; }
+ void SetLimit( INT32 n ) { nLimit = n; }
+};
+
+// The base stream class provides basic functionality for seeking
+// and accessing the data on a physical basis. It uses the built-in
+// FAT class for the page allocations.
+
+class StgStrm { // base class for all streams
+protected:
+ StgIo& rIo; // I/O system
+ StgFAT* pFat; // FAT stream for allocations
+ StgDirEntry* pEntry; // dir entry (for ownership)
+ INT32 nStart; // 1st data page
+ INT32 nSize; // stream size in bytes
+ INT32 nPos; // current byte position
+ INT32 nPage; // current logical page
+ short nOffset; // offset into current page
+ short nPageSize; // logical page size
+ BOOL Copy( INT32 nFrom, INT32 nBytes );
+ StgStrm( StgIo& );
+public:
+ virtual ~StgStrm();
+ StgIo& GetIo() { return rIo; }
+ INT32 GetPos() { return nPos; }
+ INT32 GetStart() { return nStart; }
+ INT32 GetSize() { return nSize; }
+ INT32 GetPage() { return nPage; }
+ short GetPageSize() { return nPageSize; }
+ INT32 GetPages();
+ short GetOffset() { return nOffset;}
+ void SetEntry( StgDirEntry& );
+ virtual BOOL SetSize( INT32 );
+ virtual BOOL Pos2Page( INT32 nBytePos );
+ virtual INT32 Read( void*, INT32 ) { return 0; }
+ virtual INT32 Write( const void*, INT32 ) { return 0; }
+ virtual StgPage* GetPhysPage( INT32 nBytePos, BOOL bForce = FALSE );
+ virtual BOOL IsSmallStrm() { return FALSE; }
+};
+
+// The FAT stream class provides physical access to the master FAT.
+// Since this access is implemented as a StgStrm, we can use the
+// FAT allocator.
+
+class StgFATStrm : public StgStrm { // the master FAT stream
+ virtual BOOL Pos2Page( INT32 nBytePos );
+ BOOL SetPage( short, INT32 );
+public:
+ StgFATStrm( StgIo& );
+ virtual ~StgFATStrm() {}
+ using StgStrm::GetPage;
+ INT32 GetPage( short, BOOL, USHORT *pnMasterAlloc = 0);
+ virtual BOOL SetSize( INT32 );
+ virtual StgPage* GetPhysPage( INT32 nBytePos, BOOL bForce = FALSE );
+};
+
+// The stream has a size increment which normally is 1, but which can be
+// set to any value is you want the size to be incremented by certain values.
+
+class StgDataStrm : public StgStrm // a physical data stream
+{
+ short nIncr; // size adjust increment
+ void Init( INT32 nBgn, INT32 nLen );
+public:
+ StgDataStrm( StgIo&, INT32 nBgn, INT32 nLen=-1 );
+ StgDataStrm( StgIo&, StgDirEntry* );
+ void* GetPtr( INT32 nPos, BOOL bForce, BOOL bDirty );
+ void SetIncrement( short n ) { nIncr = n ; }
+ virtual BOOL SetSize( INT32 );
+ virtual INT32 Read( void*, INT32 );
+ virtual INT32 Write( const void*, INT32 );
+};
+
+// The small stream class provides access to streams with a size < 4096 bytes.
+// This stream is a StgStream containing small pages. The FAT for this stream
+// is also a StgStream. The start of the FAT is in the header at DataRootPage,
+// the stream itself is pointed to by the root entry (it holds start & size).
+
+class StgSmallStrm : public StgStrm // a logical data stream
+{
+ StgStrm* pData; // the data stream
+ void Init( INT32 nBgn, INT32 nLen );
+public:
+ StgSmallStrm( StgIo&, INT32 nBgn, INT32 nLen );
+ StgSmallStrm( StgIo&, StgDirEntry* );
+ virtual INT32 Read( void*, INT32 );
+ virtual INT32 Write( const void*, INT32 );
+ virtual BOOL IsSmallStrm() { return TRUE; }
+};
+
+class StgTmpStrm : public SvMemoryStream
+{
+ String aName;
+ SvFileStream* pStrm;
+ using SvMemoryStream::GetData;
+ virtual ULONG GetData( void* pData, ULONG nSize );
+ virtual ULONG PutData( const void* pData, ULONG nSize );
+ virtual ULONG SeekPos( ULONG nPos );
+ virtual void FlushData();
+
+public:
+ StgTmpStrm( ULONG=16 );
+ ~StgTmpStrm();
+ BOOL Copy( StgTmpStrm& );
+ void SetSize( ULONG );
+ ULONG GetSize() const;
+};
+
+#endif
diff --git a/sot/source/sdstor/storage.cxx b/sot/source/sdstor/storage.cxx
new file mode 100644
index 000000000000..136abb29a98e
--- /dev/null
+++ b/sot/source/sdstor/storage.cxx
@@ -0,0 +1,1563 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <rtl/digest.h>
+#include <osl/file.hxx>
+#include <stg.hxx>
+#include <storinfo.hxx>
+#include <sot/storage.hxx>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#ifndef _TOOLS_FSYS_HXX
+#include <tools/fsys.hxx>
+#endif
+#include <tools/cachestr.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include "unostorageholder.hxx"
+
+using namespace ::com::sun::star;
+
+/************** class SotStorageStream ***********************************/
+class SotStorageStreamFactory : public SotFactory
+{
+public:
+ TYPEINFO();
+ SotStorageStreamFactory( const SvGlobalName & rName,
+ const String & rClassName,
+ CreateInstanceType pCreateFuncP )
+ : SotFactory( rName, rClassName, pCreateFuncP )
+ {}
+};
+TYPEINIT1(SotStorageStreamFactory,SotFactory);
+
+
+SO2_IMPL_BASIC_CLASS1_DLL(SotStorageStream,SotStorageStreamFactory,SotObject,
+ SvGlobalName( 0xd7deb420, 0xf902, 0x11d0,
+ 0xaa, 0xa1, 0x0, 0xa0, 0x24, 0x9d, 0x55, 0x90 ) )
+SO2_IMPL_INVARIANT(SotStorageStream)
+
+
+void SotStorageStream::TestMemberObjRef( BOOL /*bFree*/ )
+{
+}
+
+#ifdef TEST_INVARIANT
+void SotStorageStream::TestMemberInvariant( BOOL /*bPrint*/ )
+{
+}
+#endif
+
+/************************************************************************
+|* SotStorageStream::SotStorageStream()
+|*
+|* Beschreibung
+*************************************************************************/
+SvLockBytesRef MakeLockBytes_Impl( const String & rName, StreamMode nMode )
+{
+ SvLockBytesRef xLB;
+ if( rName.Len() )
+ {
+ SvStream * pFileStm = new SvFileStream( rName, nMode );
+ xLB = new SvLockBytes( pFileStm, TRUE );
+ }
+ else
+ {
+ SvStream * pCacheStm = new SvCacheStream();
+ xLB = new SvLockBytes( pCacheStm, TRUE );
+ }
+ return xLB;
+}
+
+SotStorageStream::SotStorageStream( const String & rName, StreamMode nMode,
+ StorageMode
+ #ifdef DBG_UTIL
+ nStorageMode
+ #endif
+ )
+ : SvStream( MakeLockBytes_Impl( rName, nMode ) )
+ , pOwnStm( NULL )
+{
+ if( nMode & STREAM_WRITE )
+ bIsWritable = TRUE;
+ else
+ bIsWritable = FALSE;
+
+ DBG_ASSERT( !nStorageMode,"StorageModes ignored" );
+}
+
+SotStorageStream::SotStorageStream( BaseStorageStream * pStm )
+{
+ if( pStm )
+ {
+ if( STREAM_WRITE & pStm->GetMode() )
+ bIsWritable = TRUE;
+ else
+ bIsWritable = FALSE;
+
+ pOwnStm = pStm;
+ SetError( pStm->GetError() );
+ pStm->ResetError();
+ }
+ else
+ {
+ pOwnStm = NULL;
+ bIsWritable = TRUE;
+ SetError( SVSTREAM_INVALID_PARAMETER );
+ }
+}
+
+SotStorageStream::SotStorageStream()
+ : pOwnStm( NULL )
+{
+ // ??? wenn Init virtuell ist, entsprechen setzen
+ bIsWritable = TRUE;
+}
+
+/************************************************************************
+|* SotStorageStream::~SotStorageStream()
+|*
+|* Beschreibung
+*************************************************************************/
+SotStorageStream::~SotStorageStream()
+{
+ Flush(); //SetBufferSize(0);
+ delete pOwnStm;
+}
+
+/*************************************************************************
+|* SotStorageStream::SyncSvStream()
+|*
+|* Beschreibung: Der SvStream wird auf den Zustand des Standard-Streams
+|* gesetzt. Der Puffer des SvStreams wird weggeworfen.
+*************************************************************************/
+void SotStorageStream::SyncSvStream()
+{
+ ULONG nPos = 0;
+ if( pOwnStm )
+ {
+ pOwnStm->Flush();
+ nPos = pOwnStm->Tell();
+ SetError( pOwnStm->GetError() );
+ SvStream::SyncSvStream( nPos );
+ }
+}
+
+/*************************************************************************
+|* SotStorageStream::ResetError()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorageStream::ResetError()
+{
+ SvStream::ResetError();
+ if( pOwnStm )
+ pOwnStm->ResetError();
+}
+
+/*************************************************************************
+|* SotStorageStream::GetData()
+|*
+|* Beschreibung
+*************************************************************************/
+ULONG SotStorageStream::GetData( void* pData, ULONG nSize )
+{
+ ULONG nRet = 0;
+
+ if( pOwnStm )
+ {
+ nRet = pOwnStm->Read( pData, nSize );
+ SetError( pOwnStm->GetError() );
+ }
+ else
+ nRet = SvStream::GetData( (sal_Char *)pData, nSize );
+ return nRet;
+}
+
+/*************************************************************************
+|* SotStorageStream::PutData()
+|*
+|* Beschreibung
+*************************************************************************/
+ULONG SotStorageStream::PutData( const void* pData, ULONG nSize )
+{
+ ULONG nRet = 0;
+
+ if( pOwnStm )
+ {
+ nRet = pOwnStm->Write( pData, nSize );
+ SetError( pOwnStm->GetError() );
+ }
+ else
+ nRet = SvStream::PutData( (sal_Char *)pData, nSize );
+ return nRet;
+}
+
+/*************************************************************************
+|* SotStorageStream::SeekPos()
+|*
+|* Beschreibung
+*************************************************************************/
+ULONG SotStorageStream::SeekPos( ULONG nPos )
+{
+ ULONG nRet = 0;
+
+ if( pOwnStm )
+ {
+ nRet = pOwnStm->Seek( nPos );
+ SetError( pOwnStm->GetError() );
+ }
+ else
+ nRet = SvStream::SeekPos( nPos );
+ return nRet;
+}
+
+/*************************************************************************
+|* SotStorageStream::Flush()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorageStream::FlushData()
+{
+ if( pOwnStm )
+ {
+ pOwnStm->Flush();
+ SetError( pOwnStm->GetError() );
+ }
+ else
+ SvStream::FlushData();
+}
+
+/*************************************************************************
+|* SotStorageStream::SetSize()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorageStream::SetSize( ULONG nNewSize )
+{
+ ULONG nPos = Tell();
+ if( pOwnStm )
+ {
+ pOwnStm->SetSize( nNewSize );
+ SetError( pOwnStm->GetError() );
+ }
+ else
+ SvStream::SetSize( nNewSize );
+
+ if( nNewSize < nPos )
+ // ans Ende setzen
+ Seek( nNewSize );
+
+ //return GetError() == SVSTREAM_OK;
+}
+
+/*************************************************************************
+|*
+|* SotStorageStream::GetSize()
+|*
+|* Beschreibung
+|*
+*************************************************************************/
+UINT32 SotStorageStream::GetSize() const
+{
+ ULONG nPos = Tell();
+ ((SotStorageStream *)this)->Seek( STREAM_SEEK_TO_END );
+ ULONG nSize = Tell();
+ ((SotStorageStream *)this)->Seek( nPos );
+ return nSize;
+}
+
+/*************************************************************************
+|* SotStorageStream::CopyTo()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorageStream::CopyTo( SotStorageStream * pDestStm )
+{
+ Flush(); // alle Daten schreiben
+ pDestStm->ClearBuffer();
+ if( !pOwnStm || !pDestStm->pOwnStm )
+ { // Wenn Ole2 oder nicht nur eigene StorageStreams
+
+ ULONG nPos = Tell(); // Position merken
+ Seek( 0L );
+ pDestStm->SetSize( 0 ); // Ziel-Stream leeren
+
+ void * pMem = new BYTE[ 8192 ];
+ ULONG nRead;
+ while( 0 != (nRead = Read( pMem, 8192 )) )
+ {
+ if( nRead != pDestStm->Write( pMem, nRead ) )
+ {
+ SetError( SVSTREAM_GENERALERROR );
+ break;
+ }
+ }
+ delete [] static_cast<BYTE*>(pMem);
+ // Position setzen
+ pDestStm->Seek( nPos );
+ Seek( nPos );
+ }
+ else
+ {
+ /*
+ // Kopieren
+ nErr = pObjI->CopyTo( pDestStm->pObjI, uSize, NULL, &uWrite );
+ if( SUCCEEDED( nErr ) )
+ {
+ // Ziel-Streamzeiger steht hinter den Daten
+ // SvSeek abgleichen
+ pDestStm->Seek( uWrite.LowPart );
+ }
+ else if( GetScode( nErr ) == E_NOTIMPL )
+ { // Eines Tages werden alle MS... ?!#
+ */
+ pOwnStm->CopyTo( pDestStm->pOwnStm );
+ SetError( pOwnStm->GetError() );
+ }
+ return GetError() == SVSTREAM_OK;
+}
+
+/*************************************************************************
+|* SotStorageStream::Commit()
+|* SotStorageStream::Revert()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorageStream::Commit()
+{
+ if( pOwnStm )
+ {
+ pOwnStm->Flush();
+ if( pOwnStm->GetError() == SVSTREAM_OK )
+ pOwnStm->Commit();
+ SetError( pOwnStm->GetError() );
+ }
+ return GetError() == SVSTREAM_OK;
+}
+
+BOOL SotStorageStream::Revert()
+{
+ if( !pOwnStm )
+ {
+ pOwnStm->Revert();
+ SetError( pOwnStm->GetError() );
+ }
+ return GetError() == SVSTREAM_OK;
+}
+
+BOOL SotStorageStream::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pOwnStm );
+ if ( pStg )
+ {
+ return pStg->SetProperty( rName, rValue );
+ }
+ else
+ {
+ DBG_ERROR("Not implemented!");
+ return FALSE;
+ }
+}
+
+BOOL SotStorageStream::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pOwnStm );
+ if ( pStg )
+ {
+ return pStg->GetProperty( rName, rValue );
+ }
+ else
+ {
+ DBG_ERROR("Not implemented!");
+ return FALSE;
+ }
+}
+
+::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > SotStorageStream::GetXInputStream() const
+{
+ UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pOwnStm );
+ if ( pStg )
+ {
+ return pStg->GetXInputStream();
+ }
+ else
+ {
+ DBG_ERROR("Not implemented!");
+ return ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream >();
+ }
+}
+
+
+
+/************** class SotStorage ******************************************
+*************************************************************************/
+class SotStorageFactory : public SotFactory
+{
+public:
+ TYPEINFO();
+ SotStorageFactory( const SvGlobalName & rName,
+ const String & rClassName,
+ CreateInstanceType pCreateFuncP )
+ : SotFactory( rName, rClassName, pCreateFuncP )
+ {}
+};
+TYPEINIT1(SotStorageFactory,SotFactory);
+
+
+SO2_IMPL_BASIC_CLASS1_DLL(SotStorage,SotStorageFactory,SotObject,
+ SvGlobalName( 0x980ce7e0, 0xf905, 0x11d0,
+ 0xaa, 0xa1, 0x0, 0xa0, 0x24, 0x9d, 0x55, 0x90 ) )
+SO2_IMPL_INVARIANT(SotStorage)
+
+
+/************************************************************************
+|*
+|* SotStorage::Tes*()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorage::TestMemberObjRef( BOOL /*bFree*/ )
+{
+}
+
+#ifdef TEST_INVARIANT
+void SotStorage::TestMemberInvariant( BOOL /*bPrint*/ )
+{
+}
+#endif
+
+/************************************************************************
+|*
+|* SotStorage::SotStorage()
+|*
+|* Beschreibung Es muss ein I... Objekt an SvObject uebergeben
+|* werden, da es sonst selbst ein IUnknown anlegt und
+|* festlegt, dass alle weiteren I... Objekte mit
+|* delete zerstoert werden (Owner() == TRUE).
+|* Es werden aber nur IStorage Objekte benutzt und nicht
+|* selbst implementiert, deshalb wird so getan, als ob
+|* das IStorage Objekt von aussen kam und es wird mit
+|* Release() freigegeben.
+|* Die CreateStorage Methoden werden benoetigt, um
+|* ein IStorage Objekt vor dem Aufruf von SvObject
+|* zu erzeugen (Own, !Own automatik).
+|* Hat CreateStorage ein Objekt erzeugt, dann wurde
+|* der RefCounter schon um 1 erhoet.
+|* Die Uebergabe erfolgt in pStorageCTor. Die Variable
+|* ist NULL, wenn es nicht geklappt hat.
+|* Ersterstellung MM 23.06.94
+|* Letzte Aenderung MM 23.06.94
+|*
+*************************************************************************/
+#define INIT_SotStorage() \
+ : m_pOwnStg( NULL ) \
+ , m_pStorStm( NULL ) \
+ , m_nError( SVSTREAM_OK ) \
+ , m_bIsRoot( FALSE ) \
+ , m_bDelStm( FALSE ) \
+ , m_nVersion( SOFFICE_FILEFORMAT_CURRENT )
+
+SotStorage::SotStorage()
+ INIT_SotStorage()
+{
+ // ??? What's this ???
+}
+
+#define ERASEMASK ( STREAM_TRUNC | STREAM_WRITE | STREAM_SHARE_DENYALL )
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <ucbhelper/content.hxx>
+
+SotStorage::SotStorage( const ::ucbhelper::Content& rContent, const String & rName, StreamMode nMode, StorageMode nStorageMode )
+ INIT_SotStorage()
+{
+ m_aName = rName; // Namen merken
+ m_pOwnStg = new UCBStorage( rContent, m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+
+ SetError( m_pOwnStg->GetError() );
+
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+
+ SignAsRoot( m_pOwnStg->IsRoot() );
+}
+
+SotStorage::SotStorage( const String & rName, StreamMode nMode, StorageMode nStorageMode )
+ INIT_SotStorage()
+{
+ m_aName = rName; // Namen merken
+ CreateStorage( TRUE, nMode, nStorageMode );
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+}
+
+void SotStorage::CreateStorage( BOOL bForceUCBStorage, StreamMode nMode, StorageMode nStorageMode )
+{
+ DBG_ASSERT( !m_pStorStm && !m_pOwnStg, "Use only in ctor!" );
+ if( m_aName.Len() )
+ {
+ // named storage
+ if( ( ( nMode & ERASEMASK ) == ERASEMASK ) )
+ ::utl::UCBContentHelper::Kill( m_aName );
+
+ INetURLObject aObj( m_aName );
+ if ( aObj.GetProtocol() == INET_PROT_NOT_VALID )
+ {
+ String aURL;
+ ::utl::LocalFileHelper::ConvertPhysicalNameToURL( m_aName, aURL );
+ aObj.SetURL( aURL );
+ m_aName = aObj.GetMainURL( INetURLObject::NO_DECODE );
+ }
+
+ // a new unpacked storage should be created
+ if ( nStorageMode == STORAGE_CREATE_UNPACKED )
+ {
+ // don't open stream readwrite, content provider may not support this !
+ String aURL = UCBStorage::CreateLinkFile( m_aName );
+ if ( aURL.Len() )
+ {
+ ::ucbhelper::Content aContent( aURL, ::com::sun::star::uno::Reference < ::com::sun::star::ucb::XCommandEnvironment >() );
+ m_pOwnStg = new UCBStorage( aContent, aURL, nMode, FALSE );
+ }
+ else
+ {
+ m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ SetError( ERRCODE_IO_NOTSUPPORTED );
+ }
+ }
+ else
+ {
+ // check the stream
+ m_pStorStm = ::utl::UcbStreamHelper::CreateStream( m_aName, nMode );
+ if ( m_pStorStm && m_pStorStm->GetError() )
+ DELETEZ( m_pStorStm );
+
+ if ( m_pStorStm )
+ {
+ // try as UCBStorage, next try as OLEStorage
+ BOOL bIsUCBStorage = UCBStorage::IsStorageFile( m_pStorStm );
+ if ( !bIsUCBStorage && bForceUCBStorage )
+ // if UCBStorage has priority, it should not be used only if it is really an OLEStorage
+ bIsUCBStorage = !Storage::IsStorageFile( m_pStorStm );
+
+ if ( bIsUCBStorage )
+ {
+ if ( UCBStorage::GetLinkedFile( *m_pStorStm ).Len() )
+ {
+ // detect special unpacked storages
+ m_pOwnStg = new UCBStorage( *m_pStorStm, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ m_bDelStm = TRUE;
+ }
+ else
+ {
+ // detect special disk spanned storages
+ if ( UCBStorage::IsDiskSpannedFile( m_pStorStm ) )
+ nMode |= STORAGE_DISKSPANNED_MODE;
+
+ // UCBStorage always works directly on the UCB content, so discard the stream first
+ DELETEZ( m_pStorStm );
+ m_pOwnStg = new UCBStorage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ }
+ }
+ else
+ {
+ // OLEStorage can be opened with a stream
+ m_pOwnStg = new Storage( *m_pStorStm, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ m_bDelStm = TRUE;
+ }
+ }
+ else if ( bForceUCBStorage )
+ {
+ m_pOwnStg = new UCBStorage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ SetError( ERRCODE_IO_NOTSUPPORTED );
+ }
+ else
+ {
+ m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ SetError( ERRCODE_IO_NOTSUPPORTED );
+ }
+ }
+ }
+ else
+ {
+ // temporary storage
+ if ( bForceUCBStorage )
+ m_pOwnStg = new UCBStorage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ else
+ m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ m_aName = m_pOwnStg->GetName();
+ }
+
+ SetError( m_pOwnStg->GetError() );
+
+ SignAsRoot( m_pOwnStg->IsRoot() );
+}
+
+SotStorage::SotStorage( BOOL bUCBStorage, const String & rName, StreamMode nMode, StorageMode nStorageMode )
+ INIT_SotStorage()
+{
+ m_aName = rName;
+ CreateStorage( bUCBStorage, nMode, nStorageMode );
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+}
+
+SotStorage::SotStorage( BaseStorage * pStor )
+ INIT_SotStorage()
+{
+ if ( pStor )
+ {
+ m_aName = pStor->GetName(); // Namen merken
+ SignAsRoot( pStor->IsRoot() );
+ SetError( pStor->GetError() );
+ }
+
+ m_pOwnStg = pStor;
+ ULONG nErr = m_pOwnStg ? m_pOwnStg->GetError() : SVSTREAM_CANNOT_MAKE;
+ SetError( nErr );
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+}
+
+SotStorage::SotStorage( BOOL bUCBStorage, SvStream & rStm )
+ INIT_SotStorage()
+{
+ SetError( rStm.GetError() );
+
+ // try as UCBStorage, next try as OLEStorage
+ if ( UCBStorage::IsStorageFile( &rStm ) || bUCBStorage )
+ m_pOwnStg = new UCBStorage( rStm, FALSE );
+ else
+ m_pOwnStg = new Storage( rStm, FALSE );
+
+ SetError( m_pOwnStg->GetError() );
+
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+
+ SignAsRoot( m_pOwnStg->IsRoot() );
+}
+
+SotStorage::SotStorage( SvStream & rStm )
+ INIT_SotStorage()
+{
+ SetError( rStm.GetError() );
+
+ // try as UCBStorage, next try as OLEStorage
+ if ( UCBStorage::IsStorageFile( &rStm ) )
+ m_pOwnStg = new UCBStorage( rStm, FALSE );
+ else
+ m_pOwnStg = new Storage( rStm, FALSE );
+
+ SetError( m_pOwnStg->GetError() );
+
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+
+ SignAsRoot( m_pOwnStg->IsRoot() );
+}
+
+SotStorage::SotStorage( SvStream * pStm, BOOL bDelete )
+ INIT_SotStorage()
+{
+ SetError( pStm->GetError() );
+
+ // try as UCBStorage, next try as OLEStorage
+ if ( UCBStorage::IsStorageFile( pStm ) )
+ m_pOwnStg = new UCBStorage( *pStm, FALSE );
+ else
+ m_pOwnStg = new Storage( *pStm, FALSE );
+
+ SetError( m_pOwnStg->GetError() );
+
+ m_pStorStm = pStm;
+ m_bDelStm = bDelete;
+ if ( IsOLEStorage() )
+ m_nVersion = SOFFICE_FILEFORMAT_50;
+
+ SignAsRoot( m_pOwnStg->IsRoot() );
+}
+
+/*************************************************************************
+|* SotStorage::~SotStorage()
+|*
+|* Beschreibung
+*************************************************************************/
+SotStorage::~SotStorage()
+{
+ delete m_pOwnStg;
+ if( m_bDelStm )
+ delete m_pStorStm;
+}
+
+/*************************************************************************
+|* SotStorage::RemoveUNOStorageHolder()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorage::RemoveUNOStorageHolder( UNOStorageHolder* pHolder )
+{
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ if ( pStg )
+ {
+ pStg->GetUNOStorageHolderList()->remove( pHolder );
+ pHolder->release();
+ }
+ else
+ {
+ DBG_ERROR("Not implemented!");
+ }
+}
+
+/*************************************************************************
+|* SotStorage::GetUNOAPIDuplicate()
+|*
+|* Beschreibung
+*************************************************************************/
+uno::Reference< embed::XStorage > SotStorage::GetUNOAPIDuplicate( const String& rEleName, sal_Int32 nUNOStorageMode )
+{
+ // after we create a duplicate we will register wrapper
+ // for storage messages, the wrapper will control the real storage
+ // the real storage will be able to ask the duplicate to dispose if it's parent is disposed
+
+ uno::Reference< embed::XStorage > xResult;
+
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ if ( !pStg )
+ return xResult;
+
+ UNOStorageHolderList* pUNOStorageHolderList = pStg->GetUNOStorageHolderList();
+ if ( !pUNOStorageHolderList )
+ return xResult;
+
+ for ( UNOStorageHolderList::iterator aIter = pUNOStorageHolderList->begin();
+ aIter != pUNOStorageHolderList->end(); aIter++ )
+ if ( (*aIter) && (*aIter)->GetStorageName().Equals( rEleName ) )
+ {
+ // the storage is already in use
+ return xResult;
+ }
+
+ if ( IsStream( rEleName ) )
+ return xResult;
+
+ if ( GetError() == ERRCODE_NONE )
+ {
+ StreamMode nMode = ( ( nUNOStorageMode & embed::ElementModes::WRITE ) == embed::ElementModes::WRITE ) ?
+ STREAM_WRITE : ( STREAM_READ | STREAM_NOCREATE );
+ if ( nUNOStorageMode & embed::ElementModes::NOCREATE )
+ nMode |= STREAM_NOCREATE;
+
+ sal_Bool bStorageReady = !IsStorage( rEleName );
+ SotStorageRef pChildStorage = OpenUCBStorage( rEleName, nMode, STORAGE_TRANSACTED );
+ if ( pChildStorage->GetError() == ERRCODE_NONE && pChildStorage->m_pOwnStg )
+ {
+ ::utl::TempFile* pTempFile = new ::utl::TempFile();
+ if ( pTempFile->GetURL().Len() )
+ {
+ if ( !bStorageReady )
+ {
+ UCBStorage* pChildUCBStg = PTR_CAST( UCBStorage, pChildStorage->m_pOwnStg );
+ if ( pChildUCBStg )
+ {
+ UCBStorage* pTempStorage = new UCBStorage( pTempFile->GetURL(), STREAM_WRITE, FALSE, TRUE );
+ if ( pTempStorage )
+ {
+ pChildUCBStg->CopyTo( pTempStorage );
+
+ // CopyTo does not transport unknown media type
+ // just workaround it
+ uno::Any aMediaType;
+
+ if ( pChildUCBStg->GetProperty(
+ ::rtl::OUString::createFromAscii( "MediaType" ), aMediaType ) )
+ pTempStorage->SetProperty( ::rtl::OUString::createFromAscii( "MediaType" ), aMediaType );
+
+ bStorageReady = !pChildUCBStg->GetError() && !pTempStorage->GetError()
+ && pTempStorage->Commit();
+
+ delete ((BaseStorage*)pTempStorage);
+ pTempStorage = NULL;
+ }
+ }
+
+ OSL_ENSURE( bStorageReady, "Problem on storage copy!\n" );
+ }
+
+ if ( bStorageReady )
+ {
+ try {
+ uno::Reference< lang::XSingleServiceFactory > xStorageFactory(
+ ::comphelper::getProcessServiceFactory()->createInstance(
+ ::rtl::OUString::createFromAscii( "com.sun.star.embed.StorageFactory" ) ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xStorageFactory.is(), "Can't create storage factory!\n" );
+ if ( xStorageFactory.is() )
+ {
+ uno::Sequence< uno::Any > aArg( 2 );
+ aArg[0] <<= ::rtl::OUString( pTempFile->GetURL() );
+ aArg[1] <<= nUNOStorageMode;
+ uno::Reference< embed::XStorage > xDuplStorage(
+ xStorageFactory->createInstanceWithArguments( aArg ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xDuplStorage.is(), "Can't open storage!\n" );
+ if ( xDuplStorage.is() )
+ {
+ UNOStorageHolder* pHolder =
+ new UNOStorageHolder( *this, *pChildStorage, xDuplStorage, pTempFile );
+ pHolder->acquire();
+ pTempFile = NULL;
+ pUNOStorageHolderList->push_back( pHolder );
+ xResult = xDuplStorage;
+ }
+ }
+ }
+ catch( uno::Exception& e )
+ {
+ (void)e;
+ OSL_ENSURE( sal_False, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ) );
+ }
+ }
+ }
+
+ if ( pTempFile != NULL )
+ delete pTempFile;
+ }
+ else
+ SetError( pChildStorage->GetError() );
+ }
+
+ return xResult;
+}
+
+/*************************************************************************
+|* SotStorage::CreateMemoryStream()
+|*
+|* Beschreibung
+*************************************************************************/
+SvMemoryStream * SotStorage::CreateMemoryStream()
+{
+ SvMemoryStream * pStm = NULL;
+ pStm = new SvMemoryStream( 0x8000, 0x8000 );
+ SotStorageRef aStg = new SotStorage( *pStm );
+ if( CopyTo( aStg ) )
+ aStg->Commit();
+ else
+ {
+ aStg.Clear(); // Storage vorher freigeben
+ delete pStm;
+ pStm = NULL;
+ }
+ return pStm;
+}
+
+/*************************************************************************
+|* SotStorage::GetStorage()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::IsStorageFile( const String & rFileName )
+{
+ String aName( rFileName );
+ INetURLObject aObj( aName );
+ if ( aObj.GetProtocol() == INET_PROT_NOT_VALID )
+ {
+ String aURL;
+ ::utl::LocalFileHelper::ConvertPhysicalNameToURL( aName, aURL );
+ aObj.SetURL( aURL );
+ aName = aObj.GetMainURL( INetURLObject::NO_DECODE );
+ }
+
+ SvStream * pStm = ::utl::UcbStreamHelper::CreateStream( aName, STREAM_STD_READ );
+ BOOL bRet = SotStorage::IsStorageFile( pStm );
+ delete pStm;
+ return bRet;
+}
+
+BOOL SotStorage::IsStorageFile( SvStream* pStream )
+{
+ /** code for new storages must come first! **/
+ if ( pStream )
+ {
+ long nPos = pStream->Tell();
+ BOOL bRet = UCBStorage::IsStorageFile( pStream );
+ if ( !bRet )
+ bRet = Storage::IsStorageFile( pStream );
+ pStream->Seek( nPos );
+ return bRet;
+ }
+ else
+ return FALSE;
+}
+/*************************************************************************
+|* SotStorage::GetStorage()
+|*
+|* Beschreibung
+*************************************************************************/
+const String & SotStorage::GetName() const
+{
+ if( !m_aName.Len() )
+ {
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ ((SotStorage *)this)->m_aName = m_pOwnStg->GetName();
+ }
+ return m_aName;
+}
+
+void SotStorage::SetName( const String& rName )
+{
+ // This method is necessary because most storages will not be opened with a FileName, but an external stream instead
+ // This stream is a stream opened by a UCP and so aName is only used as a transport for all client code of the SotStorage
+ // class that depends on the fact that a root storage has a name
+ DBG_ASSERT( !GetName().Len(), "SetName() must not be called when the storage already has a name!" );
+ m_aName = rName;
+}
+
+/*************************************************************************
+|* SotStorage::ResetError()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorage::ResetError()
+{
+ m_nError = SVSTREAM_OK;
+ if( m_pOwnStg )
+ m_pOwnStg->ResetError();
+}
+
+/*************************************************************************
+|* SotStorage::SetClass()
+|* SotStorage::SetConvertClass()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorage::SetClass( const SvGlobalName & rName,
+ ULONG nOriginalClipFormat,
+ const String & rUserTypeName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ m_pOwnStg->SetClass( rName, nOriginalClipFormat, rUserTypeName );
+ else
+ SetError( SVSTREAM_GENERALERROR );
+}
+
+void SotStorage::SetConvertClass( const SvGlobalName & rName,
+ ULONG nOriginalClipFormat,
+ const String & rUserTypeName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ m_pOwnStg->SetConvertClass( rName, nOriginalClipFormat, rUserTypeName );
+ else
+ SetError( SVSTREAM_GENERALERROR );
+}
+
+/*************************************************************************
+|* SotStorage::GetClassName()
+|* SotStorage::GetFormat()
+|* SotStorage::GetUserName()
+|* SotStorage::ShouldConvert()
+|*
+|* Beschreibung
+*************************************************************************/
+SvGlobalName SotStorage::GetClassName()
+{
+ SvGlobalName aGN;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ aGN = m_pOwnStg->GetClassName();
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return aGN;
+}
+
+ULONG SotStorage::GetFormat()
+{
+ ULONG nFormat = 0;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ nFormat = m_pOwnStg->GetFormat();
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return nFormat;
+}
+
+String SotStorage::GetUserName()
+{
+ String aName;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ aName = m_pOwnStg->GetUserName();
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return aName;
+}
+
+BOOL SotStorage::ShouldConvert()
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ return m_pOwnStg->ShouldConvert();
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return FALSE;
+}
+
+/*************************************************************************
+|* SotStorage::FillInfoList()
+|*
+|* Beschreibung
+*************************************************************************/
+void SotStorage::FillInfoList( SvStorageInfoList * pFillList ) const
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ m_pOwnStg->FillInfoList( pFillList );
+}
+
+/*************************************************************************
+|* SotStorage::CopyTo()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::CopyTo( SotStorage * pDestStg )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ DBG_ASSERT( pDestStg->Owner(), "must be owner" );
+ if( m_pOwnStg && pDestStg->m_pOwnStg )
+ {
+ m_pOwnStg->CopyTo( pDestStg->m_pOwnStg );
+ SetError( m_pOwnStg->GetError() );
+ pDestStg->m_aKey = m_aKey;
+ pDestStg->m_nVersion = m_nVersion;
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::Commit()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::Commit()
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ if( !m_pOwnStg->Commit() )
+ SetError( m_pOwnStg->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::Revert()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::Revert()
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ if( !m_pOwnStg->Revert() )
+ SetError( m_pOwnStg->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::OpenStream()
+|*
+|* Beschreibung
+*************************************************************************/
+SotStorageStream * SotStorage::OpenEncryptedSotStream( const String & rEleName, const ByteString& rKey,
+ StreamMode nMode,
+ StorageMode nStorageMode )
+{
+ DBG_ASSERT( !nStorageMode, "StorageModes ignored" );
+ SotStorageStream * pStm = NULL;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ // volle Ole-Patches einschalten
+ // egal was kommt, nur exclusiv gestattet
+ nMode |= STREAM_SHARE_DENYALL;
+ ErrCode nE = m_pOwnStg->GetError();
+ BaseStorageStream* p = m_pOwnStg->OpenStream( rEleName, nMode,
+ (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE, &rKey );
+ pStm = new SotStorageStream( p );
+
+ if( !nE )
+ m_pOwnStg->ResetError(); // kein Fehler setzen
+ if( nMode & STREAM_TRUNC )
+ pStm->SetSize( 0 );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return pStm;
+}
+
+SotStorageStream * SotStorage::OpenSotStream( const String & rEleName,
+ StreamMode nMode,
+ StorageMode nStorageMode )
+{
+ DBG_ASSERT( !nStorageMode, "StorageModes ignored" );
+ SotStorageStream * pStm = NULL;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ // volle Ole-Patches einschalten
+ // egal was kommt, nur exclusiv gestattet
+ nMode |= STREAM_SHARE_DENYALL;
+ ErrCode nE = m_pOwnStg->GetError();
+ BaseStorageStream * p = m_pOwnStg->OpenStream( rEleName, nMode,
+ (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ pStm = new SotStorageStream( p );
+
+ if( !nE )
+ m_pOwnStg->ResetError(); // kein Fehler setzen
+ if( nMode & STREAM_TRUNC )
+ pStm->SetSize( 0 );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return pStm;
+}
+
+/*************************************************************************
+|* SotStorage::OpenStorage()
+|*
+|* Beschreibung
+*************************************************************************/
+SotStorage * SotStorage::OpenSotStorage( const String & rEleName,
+ StreamMode nMode,
+ StorageMode nStorageMode )
+{
+ SotStorage * pStor = NULL;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ nMode |= STREAM_SHARE_DENYALL;
+ ErrCode nE = m_pOwnStg->GetError();
+ BaseStorage * p = m_pOwnStg->OpenStorage( rEleName, nMode,
+ (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ if( p )
+ {
+ pStor = new SotStorage( p );
+ if( !nE )
+ m_pOwnStg->ResetError(); // kein Fehler setzen
+
+ return pStor;
+ }
+ }
+
+ SetError( SVSTREAM_GENERALERROR );
+
+ return NULL;
+}
+
+SotStorage * SotStorage::OpenUCBStorage( const String & rEleName,
+ StreamMode nMode,
+ StorageMode nStorageMode )
+{
+ SotStorage * pStor = NULL;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ nMode |= STREAM_SHARE_DENYALL;
+ ErrCode nE = m_pOwnStg->GetError();
+ BaseStorage * p = m_pOwnStg->OpenUCBStorage( rEleName, nMode,
+ (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ pStor = new SotStorage( p );
+ if( !nE )
+ m_pOwnStg->ResetError(); // kein Fehler setzen
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return pStor;
+}
+
+SotStorage * SotStorage::OpenOLEStorage( const String & rEleName,
+ StreamMode nMode,
+ StorageMode nStorageMode )
+{
+ SotStorage * pStor = NULL;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ nMode |= STREAM_SHARE_DENYALL;
+ ErrCode nE = m_pOwnStg->GetError();
+ BaseStorage * p = m_pOwnStg->OpenOLEStorage( rEleName, nMode,
+ (nStorageMode & STORAGE_TRANSACTED) ? FALSE : TRUE );
+ pStor = new SotStorage( p );
+ if( !nE )
+ m_pOwnStg->ResetError(); // kein Fehler setzen
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return pStor;
+}
+
+/*************************************************************************
+|* SotStorage::IsStream()
+|* SotStorage::IsStorage()
+|* SotStorage::IsContained()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::IsStorage( const String & rEleName ) const
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ // ein bisschen schneller
+ if( m_pOwnStg )
+ return m_pOwnStg->IsStorage( rEleName );
+ return FALSE;
+}
+
+BOOL SotStorage::IsStream( const String & rEleName ) const
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ // ein bisschen schneller
+ if( m_pOwnStg )
+ return m_pOwnStg->IsStream( rEleName );
+ return FALSE;
+}
+
+BOOL SotStorage::IsContained( const String & rEleName ) const
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ // ein bisschen schneller
+ if( m_pOwnStg )
+ return m_pOwnStg->IsContained( rEleName );
+ return FALSE;
+}
+
+/*************************************************************************
+|* SotStorage::Remove()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::Remove( const String & rEleName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ m_pOwnStg->Remove( rEleName );
+ SetError( m_pOwnStg->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::Rename()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::Rename( const String & rEleName, const String & rNewName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ m_pOwnStg->Rename( rEleName, rNewName );
+ SetError( m_pOwnStg->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::CopyTo()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::CopyTo( const String & rEleName,
+ SotStorage * pNewSt, const String & rNewName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ DBG_ASSERT( pNewSt->Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ m_pOwnStg->CopyTo( rEleName, pNewSt->m_pOwnStg, rNewName );
+ SetError( m_pOwnStg->GetError() );
+ SetError( pNewSt->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+/*************************************************************************
+|* SotStorage::MoveTo()
+|*
+|* Beschreibung
+*************************************************************************/
+BOOL SotStorage::MoveTo( const String & rEleName,
+ SotStorage * pNewSt, const String & rNewName )
+{
+ DBG_ASSERT( Owner(), "must be owner" );
+ DBG_ASSERT( pNewSt->Owner(), "must be owner" );
+ if( m_pOwnStg )
+ {
+ m_pOwnStg->MoveTo( rEleName, pNewSt->m_pOwnStg, rNewName );
+ SetError( m_pOwnStg->GetError() );
+ SetError( pNewSt->GetError() );
+ }
+ else
+ SetError( SVSTREAM_GENERALERROR );
+ return SVSTREAM_OK == GetError();
+}
+
+const SvStream* SotStorage::GetSvStream()
+{
+ const SvStream* pResult = 0;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ pResult = m_pOwnStg->GetSvStream();
+ return pResult;
+}
+
+SvStream* SotStorage::GetTargetSvStream() const
+{
+ SvStream* pResult = 0;
+ DBG_ASSERT( Owner(), "must be owner" );
+ if( m_pOwnStg )
+ pResult = (SvStream*)(m_pOwnStg->GetSvStream());
+ return pResult;
+}
+
+
+BOOL SotStorage::Validate()
+{
+ DBG_ASSERT( m_bIsRoot, "Validate nur an Rootstorage" );
+ if( m_pOwnStg )
+ return m_pOwnStg->ValidateFAT();
+ else
+ return TRUE;
+}
+
+BOOL SotStorage::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ if ( pStg )
+ {
+ return pStg->SetProperty( rName, rValue );
+ }
+ else
+ {
+ DBG_WARNING("W1:Not implemented!");
+ return FALSE;
+ }
+}
+
+BOOL SotStorage::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ if ( pStg )
+ {
+ return pStg->GetProperty( rName, rValue );
+ }
+ else if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL )
+ {
+ String aStr = SotExchange::GetFormatMimeType( GetFormat() );
+ USHORT nPos = aStr.Search(';');
+ if ( nPos != STRING_NOTFOUND )
+ aStr = aStr.Copy( 0, nPos );
+ rValue <<= (::rtl::OUString) aStr;
+ return TRUE;
+ }
+ else
+ {
+ DBG_WARNING("W1:Not implemented!");
+ return FALSE;
+ }
+}
+
+BOOL SotStorage::GetProperty( const String& rEleName, const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ if ( pStg )
+ {
+ return pStg->GetProperty( rEleName, rName, rValue );
+ }
+ else
+ {
+ DBG_WARNING("W1:Not implemented!");
+ return FALSE;
+ }
+}
+
+BOOL SotStorage::IsOLEStorage() const
+{
+ UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg );
+ return !pStg;
+}
+
+BOOL SotStorage::IsOLEStorage( const String & rFileName )
+{
+ return Storage::IsStorageFile( rFileName );
+}
+
+BOOL SotStorage::IsOLEStorage( SvStream* pStream )
+{
+ return Storage::IsStorageFile( pStream );
+}
+
+void SotStorage::SetKey( const ByteString& rKey )
+{
+ m_aKey = rKey;
+ if ( !IsOLEStorage() )
+ {
+ sal_uInt8 aBuffer[RTL_DIGEST_LENGTH_SHA1];
+ rtlDigestError nError = rtl_digest_SHA1( m_aKey.GetBuffer(), m_aKey.Len(), aBuffer, RTL_DIGEST_LENGTH_SHA1 );
+ if ( nError == rtl_Digest_E_None )
+ {
+ sal_uInt8* pBuffer = aBuffer;
+ ::com::sun::star::uno::Sequence < sal_Int8 > aSequ( (sal_Int8*) pBuffer, RTL_DIGEST_LENGTH_SHA1 );
+ ::com::sun::star::uno::Any aAny;
+ aAny <<= aSequ;
+ SetProperty( ::rtl::OUString::createFromAscii("EncryptionKey"), aAny );
+ }
+ }
+}
+
+SotStorage* SotStorage::OpenOLEStorage( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >& xStorage,
+ const String& rEleName, StreamMode nMode )
+{
+ sal_Int32 nEleMode = embed::ElementModes::SEEKABLEREAD;
+ if ( nMode & STREAM_WRITE )
+ nEleMode |= embed::ElementModes::WRITE;
+ if ( nMode & STREAM_TRUNC )
+ nEleMode |= embed::ElementModes::TRUNCATE;
+ if ( nMode & STREAM_NOCREATE )
+ nEleMode |= embed::ElementModes::NOCREATE;
+
+ SvStream* pStream = NULL;
+ try
+ {
+ uno::Reference < io::XStream > xStream = xStorage->openStreamElement( rEleName, nEleMode );
+
+ // TODO/LATER: should it be done this way?
+ if ( nMode & STREAM_WRITE )
+ {
+ uno::Reference < beans::XPropertySet > xStreamProps( xStream, uno::UNO_QUERY_THROW );
+ xStreamProps->setPropertyValue(
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaType" ) ),
+ uno::makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application/vnd.sun.star.oleobject" ) ) ) );
+ }
+
+ pStream = utl::UcbStreamHelper::CreateStream( xStream );
+ }
+ catch ( uno::Exception& )
+ {
+ //TODO/LATER: ErrorHandling
+ pStream = new SvMemoryStream;
+ pStream->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ return new SotStorage( pStream, TRUE );
+}
+
+sal_Int32 SotStorage::GetFormatID( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >& xStorage )
+{
+ uno::Reference< beans::XPropertySet > xProps( xStorage, uno::UNO_QUERY );
+ if ( !xProps.is() )
+ return 0;
+
+ ::rtl::OUString aMediaType;
+ xProps->getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) ) >>= aMediaType;
+ if ( aMediaType.getLength() )
+ {
+ ::com::sun::star::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = aMediaType;
+ return SotExchange::GetFormat( aDataFlavor );
+ }
+
+ return 0;
+}
+
+sal_Int32 SotStorage::GetVersion( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >& xStorage )
+{
+ sal_Int32 nSotFormatID = SotStorage::GetFormatID( xStorage );
+ switch( nSotFormatID )
+ {
+ case SOT_FORMATSTR_ID_STARWRITER_8:
+ case SOT_FORMATSTR_ID_STARWRITER_8_TEMPLATE:
+ case SOT_FORMATSTR_ID_STARWRITERWEB_8:
+ case SOT_FORMATSTR_ID_STARWRITERGLOB_8:
+ case SOT_FORMATSTR_ID_STARDRAW_8:
+ case SOT_FORMATSTR_ID_STARDRAW_8_TEMPLATE:
+ case SOT_FORMATSTR_ID_STARIMPRESS_8:
+ case SOT_FORMATSTR_ID_STARIMPRESS_8_TEMPLATE:
+ case SOT_FORMATSTR_ID_STARCALC_8:
+ case SOT_FORMATSTR_ID_STARCALC_8_TEMPLATE:
+ case SOT_FORMATSTR_ID_STARCHART_8:
+ case SOT_FORMATSTR_ID_STARCHART_8_TEMPLATE:
+ case SOT_FORMATSTR_ID_STARMATH_8:
+ case SOT_FORMATSTR_ID_STARMATH_8_TEMPLATE:
+ return SOFFICE_FILEFORMAT_8;
+ case SOT_FORMATSTR_ID_STARWRITER_60:
+ case SOT_FORMATSTR_ID_STARWRITERWEB_60:
+ case SOT_FORMATSTR_ID_STARWRITERGLOB_60:
+ case SOT_FORMATSTR_ID_STARDRAW_60:
+ case SOT_FORMATSTR_ID_STARIMPRESS_60:
+ case SOT_FORMATSTR_ID_STARCALC_60:
+ case SOT_FORMATSTR_ID_STARCHART_60:
+ case SOT_FORMATSTR_ID_STARMATH_60:
+ return SOFFICE_FILEFORMAT_60;
+ }
+
+ return 0;
+}
+
diff --git a/sot/source/sdstor/storinfo.cxx b/sot/source/sdstor/storinfo.cxx
new file mode 100644
index 000000000000..2aaaadd5a151
--- /dev/null
+++ b/sot/source/sdstor/storinfo.cxx
@@ -0,0 +1,111 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#include <stg.hxx>
+#include <storinfo.hxx>
+#include <sot/exchange.hxx>
+
+
+/************** class SvStorageInfoList **********************************
+*************************************************************************/
+PRV_SV_IMPL_OWNER_LIST(SvStorageInfoList,SvStorageInfo)
+
+const SvStorageInfo * SvStorageInfoList::Get( const String & rEleName )
+{
+ for( ULONG i = 0; i < Count(); i++ )
+ {
+ const SvStorageInfo & rType = GetObject( i );
+ if( rType.GetName() == rEleName )
+ return &rType;
+ }
+ return NULL;
+}
+
+/************** class SvStorageInfo **************************************
+*************************************************************************/
+ULONG ReadClipboardFormat( SvStream & rStm )
+{
+ sal_uInt32 nFormat = 0;
+ INT32 nLen = 0;
+ rStm >> nLen;
+ if( rStm.IsEof() )
+ rStm.SetError( SVSTREAM_GENERALERROR );
+ if( nLen > 0 )
+ {
+ // get a string name
+ sal_Char * p = new sal_Char[ nLen ];
+ if( rStm.Read( p, nLen ) == (ULONG) nLen )
+ {
+ nFormat = SotExchange::RegisterFormatName( String::CreateFromAscii( p, short(nLen-1) ) );
+ }
+ else
+ rStm.SetError( SVSTREAM_GENERALERROR );
+ delete [] p;
+ }
+ else if( nLen == -1L )
+ // Windows clipboard format
+ // SV und Win stimmen ueberein (bis einschl. FORMAT_GDIMETAFILE)
+ rStm >> nFormat;
+ else if( nLen == -2L )
+ {
+ rStm >> nFormat;
+ // Mac clipboard format
+ // ??? not implemented
+ rStm.SetError( SVSTREAM_GENERALERROR );
+ }
+ else if( nLen != 0 )
+ {
+ // unknown identifier
+ rStm.SetError( SVSTREAM_GENERALERROR );
+ }
+ return nFormat;
+}
+
+void WriteClipboardFormat( SvStream & rStm, ULONG nFormat )
+{
+ // determine the clipboard format string
+ String aCbFmt;
+ if( nFormat > FORMAT_GDIMETAFILE )
+ aCbFmt = SotExchange::GetFormatName( nFormat );
+ if( aCbFmt.Len() )
+ {
+ ByteString aAsciiCbFmt( aCbFmt, RTL_TEXTENCODING_ASCII_US );
+ rStm << (INT32) (aAsciiCbFmt.Len() + 1);
+ rStm << (const char *)aAsciiCbFmt.GetBuffer();
+ rStm << (UINT8) 0;
+ }
+ else if( nFormat )
+ rStm << (INT32) -1 // for Windows
+ << (INT32) nFormat;
+ else
+ rStm << (INT32) 0; // no clipboard format
+}
+
+
diff --git a/sot/source/sdstor/ucbstorage.cxx b/sot/source/sdstor/ucbstorage.cxx
new file mode 100644
index 000000000000..ea3b656272db
--- /dev/null
+++ b/sot/source/sdstor/ucbstorage.cxx
@@ -0,0 +1,3600 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/ResultSetException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/sdbc/XResultSet.hdl>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/packages/manifest/XManifestWriter.hpp>
+#include <com/sun/star/packages/manifest/XManifestReader.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+
+#include <rtl/digest.h>
+#include <tools/ref.hxx>
+#include <tools/debug.hxx>
+#include <unotools/streamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <tools/list.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+
+#include "stg.hxx"
+#include "storinfo.hxx"
+#include <sot/storage.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include "clsids.hxx"
+
+#include "unostorageholder.hxx"
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::sdbc;
+using namespace ::ucbhelper;
+
+#if OSL_DEBUG_LEVEL > 1
+#include <stdio.h>
+static int nOpenFiles=0;
+static int nOpenStreams=0;
+#endif
+
+typedef ::cppu::WeakImplHelper2 < XInputStream, XSeekable > FileInputStreamWrapper_Base;
+class FileStreamWrapper_Impl : public FileInputStreamWrapper_Base
+{
+protected:
+ ::osl::Mutex m_aMutex;
+ String m_aURL;
+ SvStream* m_pSvStream;
+
+public:
+ FileStreamWrapper_Impl( const String& rName );
+ virtual ~FileStreamWrapper_Impl();
+
+ //DECLARE_UNO3_AGG_DEFAULTS( FileStreamWrapper_Impl, FileInputStreamWrapper_Base);
+
+ virtual void SAL_CALL seek( sal_Int64 _nLocation ) throw ( IllegalArgumentException, IOException, RuntimeException);
+ virtual sal_Int64 SAL_CALL getPosition( ) throw ( IOException, RuntimeException);
+ virtual sal_Int64 SAL_CALL getLength( ) throw ( IOException, RuntimeException);
+ virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException );
+ virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException );
+ virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) throw( NotConnectedException, BufferSizeExceededException, RuntimeException);
+ virtual sal_Int32 SAL_CALL available() throw( NotConnectedException, RuntimeException );
+ virtual void SAL_CALL closeInput() throw( NotConnectedException, RuntimeException );
+
+protected:
+ void checkConnected();
+ void checkError();
+};
+
+//------------------------------------------------------------------
+FileStreamWrapper_Impl::FileStreamWrapper_Impl( const String& rName )
+ : m_aURL( rName )
+ , m_pSvStream(0)
+{
+ // if no URL is provided the stream is empty
+}
+
+//------------------------------------------------------------------
+FileStreamWrapper_Impl::~FileStreamWrapper_Impl()
+{
+ if ( m_pSvStream )
+ {
+ delete m_pSvStream;
+#if OSL_DEBUG_LEVEL > 1
+ --nOpenFiles;
+#endif
+ }
+
+ if ( m_aURL.Len() )
+ ::utl::UCBContentHelper::Kill( m_aURL );
+}
+
+//------------------------------------------------------------------------------
+sal_Int32 SAL_CALL FileStreamWrapper_Impl::readBytes(Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
+ throw( NotConnectedException, BufferSizeExceededException, RuntimeException )
+{
+ if ( !m_aURL.Len() )
+ {
+ aData.realloc( 0 );
+ return 0;
+ }
+
+ checkConnected();
+
+ if (nBytesToRead < 0)
+ throw BufferSizeExceededException(::rtl::OUString(),static_cast<XWeak*>(this));
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ aData.realloc(nBytesToRead);
+
+ sal_uInt32 nRead = m_pSvStream->Read((void*)aData.getArray(), nBytesToRead);
+ checkError();
+
+ // Wenn gelesene Zeichen < MaxLength, Sequence anpassen
+ if (nRead < (sal_uInt32)nBytesToRead)
+ aData.realloc( nRead );
+
+ return nRead;
+}
+
+//------------------------------------------------------------------------------
+sal_Int32 SAL_CALL FileStreamWrapper_Impl::readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException )
+{
+ if ( !m_aURL.Len() )
+ {
+ aData.realloc( 0 );
+ return 0;
+ }
+
+ checkError();
+
+ if (nMaxBytesToRead < 0)
+ throw BufferSizeExceededException(::rtl::OUString(),static_cast<XWeak*>(this));
+
+ if (m_pSvStream->IsEof())
+ {
+ aData.realloc(0);
+ return 0;
+ }
+ else
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+//------------------------------------------------------------------------------
+void SAL_CALL FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip) throw( NotConnectedException, BufferSizeExceededException, RuntimeException )
+{
+ if ( !m_aURL.Len() )
+ return;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkError();
+
+#ifdef DBG_UTIL
+ sal_uInt32 nCurrentPos = m_pSvStream->Tell();
+#endif
+
+ m_pSvStream->SeekRel(nBytesToSkip);
+ checkError();
+
+#ifdef DBG_UTIL
+ nCurrentPos = m_pSvStream->Tell();
+#endif
+}
+
+//------------------------------------------------------------------------------
+sal_Int32 SAL_CALL FileStreamWrapper_Impl::available() throw( NotConnectedException, RuntimeException )
+{
+ if ( !m_aURL.Len() )
+ return 0;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkConnected();
+
+ sal_uInt32 nPos = m_pSvStream->Tell();
+ checkError();
+
+ m_pSvStream->Seek(STREAM_SEEK_TO_END);
+ checkError();
+
+ sal_Int32 nAvailable = (sal_Int32)m_pSvStream->Tell() - nPos;
+ m_pSvStream->Seek(nPos);
+ checkError();
+
+ return nAvailable;
+}
+
+//------------------------------------------------------------------------------
+void SAL_CALL FileStreamWrapper_Impl::closeInput() throw( NotConnectedException, RuntimeException )
+{
+ if ( !m_aURL.Len() )
+ return;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkConnected();
+ DELETEZ( m_pSvStream );
+#if OSL_DEBUG_LEVEL > 1
+ --nOpenFiles;
+#endif
+ ::utl::UCBContentHelper::Kill( m_aURL );
+ m_aURL.Erase();
+}
+
+//------------------------------------------------------------------------------
+void SAL_CALL FileStreamWrapper_Impl::seek( sal_Int64 _nLocation ) throw (IllegalArgumentException, IOException, RuntimeException)
+{
+ if ( !m_aURL.Len() )
+ return;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkConnected();
+
+ m_pSvStream->Seek((sal_uInt32)_nLocation);
+ checkError();
+}
+
+//------------------------------------------------------------------------------
+sal_Int64 SAL_CALL FileStreamWrapper_Impl::getPosition( ) throw (IOException, RuntimeException)
+{
+ if ( !m_aURL.Len() )
+ return 0;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkConnected();
+
+ sal_uInt32 nPos = m_pSvStream->Tell();
+ checkError();
+ return (sal_Int64)nPos;
+}
+
+//------------------------------------------------------------------------------
+sal_Int64 SAL_CALL FileStreamWrapper_Impl::getLength( ) throw (IOException, RuntimeException)
+{
+ if ( !m_aURL.Len() )
+ return 0;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkConnected();
+
+ sal_uInt32 nCurrentPos = m_pSvStream->Tell();
+ checkError();
+
+ m_pSvStream->Seek(STREAM_SEEK_TO_END);
+ sal_uInt32 nEndPos = m_pSvStream->Tell();
+ m_pSvStream->Seek(nCurrentPos);
+
+ checkError();
+
+ return (sal_Int64)nEndPos;
+}
+
+//------------------------------------------------------------------------------
+void FileStreamWrapper_Impl::checkConnected()
+{
+ if ( !m_aURL.Len() )
+ throw NotConnectedException(::rtl::OUString(), const_cast<XWeak*>(static_cast<const XWeak*>(this)));
+ if ( !m_pSvStream )
+ {
+ m_pSvStream = ::utl::UcbStreamHelper::CreateStream( m_aURL, STREAM_STD_READ );
+#if OSL_DEBUG_LEVEL > 1
+ ++nOpenFiles;
+#endif
+ }
+}
+
+//------------------------------------------------------------------------------
+void FileStreamWrapper_Impl::checkError()
+{
+ checkConnected();
+
+ if (m_pSvStream->SvStream::GetError() != ERRCODE_NONE)
+ // TODO: really evaluate the error
+ throw NotConnectedException(::rtl::OUString(), const_cast<XWeak*>(static_cast<const XWeak*>(this)));
+}
+
+TYPEINIT1( UCBStorageStream, BaseStorageStream );
+TYPEINIT1( UCBStorage, BaseStorage );
+
+#define COMMIT_RESULT_FAILURE 0
+#define COMMIT_RESULT_NOTHING_TO_DO 1
+#define COMMIT_RESULT_SUCCESS 2
+
+#define min( x, y ) (( x < y ) ? x : y)
+#define max( x, y ) (( x > y ) ? x : y)
+
+sal_Int32 GetFormatId_Impl( SvGlobalName aName )
+{
+// if ( aName == SvGlobalName( SO3_SW_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARWRITER_8;
+// if ( aName == SvGlobalName( SO3_SWWEB_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARWRITERWEB_8;
+// if ( aName == SvGlobalName( SO3_SWGLOB_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARWRITERGLOB_8;
+// if ( aName == SvGlobalName( SO3_SDRAW_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARDRAW_8;
+// if ( aName == SvGlobalName( SO3_SIMPRESS_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARIMPRESS_8;
+// if ( aName == SvGlobalName( SO3_SC_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARCALC_8;
+// if ( aName == SvGlobalName( SO3_SCH_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARCHART_8;
+// if ( aName == SvGlobalName( SO3_SM_CLASSID_8 ) )
+// return SOT_FORMATSTR_ID_STARMATH_8;
+ if ( aName == SvGlobalName( SO3_SW_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARWRITER_60;
+ if ( aName == SvGlobalName( SO3_SWWEB_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARWRITERWEB_60;
+ if ( aName == SvGlobalName( SO3_SWGLOB_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARWRITERGLOB_60;
+ if ( aName == SvGlobalName( SO3_SDRAW_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARDRAW_60;
+ if ( aName == SvGlobalName( SO3_SIMPRESS_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARIMPRESS_60;
+ if ( aName == SvGlobalName( SO3_SC_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARCALC_60;
+ if ( aName == SvGlobalName( SO3_SCH_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARCHART_60;
+ if ( aName == SvGlobalName( SO3_SM_CLASSID_60 ) )
+ return SOT_FORMATSTR_ID_STARMATH_60;
+ if ( aName == SvGlobalName( SO3_OUT_CLASSID ) ||
+ aName == SvGlobalName( SO3_APPLET_CLASSID ) ||
+ aName == SvGlobalName( SO3_PLUGIN_CLASSID ) ||
+ aName == SvGlobalName( SO3_IFRAME_CLASSID ) )
+ // allowed, but not supported
+ return 0;
+ else
+ {
+ DBG_ERROR( "Unknown UCB storage format!" );
+ return 0;
+ }
+}
+
+
+SvGlobalName GetClassId_Impl( sal_Int32 nFormat )
+{
+ switch ( nFormat )
+ {
+ case SOT_FORMATSTR_ID_STARWRITER_8 :
+ case SOT_FORMATSTR_ID_STARWRITER_8_TEMPLATE :
+ return SvGlobalName( SO3_SW_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARWRITERWEB_8 :
+ return SvGlobalName( SO3_SWWEB_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARWRITERGLOB_8 :
+ return SvGlobalName( SO3_SWGLOB_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARDRAW_8 :
+ case SOT_FORMATSTR_ID_STARDRAW_8_TEMPLATE :
+ return SvGlobalName( SO3_SDRAW_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARIMPRESS_8 :
+ case SOT_FORMATSTR_ID_STARIMPRESS_8_TEMPLATE :
+ return SvGlobalName( SO3_SIMPRESS_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARCALC_8 :
+ case SOT_FORMATSTR_ID_STARCALC_8_TEMPLATE :
+ return SvGlobalName( SO3_SC_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARCHART_8 :
+ case SOT_FORMATSTR_ID_STARCHART_8_TEMPLATE :
+ return SvGlobalName( SO3_SCH_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARMATH_8 :
+ case SOT_FORMATSTR_ID_STARMATH_8_TEMPLATE :
+ return SvGlobalName( SO3_SM_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARWRITER_60 :
+ return SvGlobalName( SO3_SW_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARWRITERWEB_60 :
+ return SvGlobalName( SO3_SWWEB_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARWRITERGLOB_60 :
+ return SvGlobalName( SO3_SWGLOB_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARDRAW_60 :
+ return SvGlobalName( SO3_SDRAW_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARIMPRESS_60 :
+ return SvGlobalName( SO3_SIMPRESS_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARCALC_60 :
+ return SvGlobalName( SO3_SC_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARCHART_60 :
+ return SvGlobalName( SO3_SCH_CLASSID_60 );
+ case SOT_FORMATSTR_ID_STARMATH_60 :
+ return SvGlobalName( SO3_SM_CLASSID_60 );
+ default :
+ //DBG_ERROR( "Unknown UCB storage format!" );
+ return SvGlobalName();
+ }
+}
+
+// All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle
+// class, that uses the refcounted object as impl-class.
+
+enum RepresentModes {
+ nonset,
+ svstream,
+ xinputstream
+};
+
+class UCBStorageStream_Impl : public SvRefBase, public SvStream
+{
+ ~UCBStorageStream_Impl();
+public:
+
+ virtual ULONG GetData( void* pData, ULONG nSize );
+ virtual ULONG PutData( const void* pData, ULONG nSize );
+ virtual ULONG SeekPos( ULONG nPos );
+ virtual void SetSize( ULONG nSize );
+ virtual void FlushData();
+ virtual void ResetError();
+
+ UCBStorageStream* m_pAntiImpl; // only valid if an external reference exists
+
+ String m_aOriginalName;// the original name before accessing the stream
+ String m_aName; // the actual name ( changed with a Rename command at the parent )
+ String m_aURL; // the full path name to create the content
+ String m_aContentType;
+ String m_aOriginalContentType;
+ ByteString m_aKey;
+ ::ucbhelper::Content* m_pContent; // the content that provides the data
+ Reference<XInputStream> m_rSource; // the stream covering the original data of the content
+ SvStream* m_pStream; // the stream worked on; for readonly streams it is the original stream of the content
+ // for read/write streams it's a copy into a temporary file
+ String m_aTempURL; // URL of this temporary stream
+ RepresentModes m_nRepresentMode; // should it be used as XInputStream or as SvStream
+ long m_nError;
+ StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing )
+ BOOL m_bSourceRead; // Source still contains useful information
+ BOOL m_bModified; // only modified streams will be sent to the original content
+ BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package
+ BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages
+ // this means that the root storage does an autocommit when its external
+ // reference is destroyed
+ BOOL m_bIsOLEStorage;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream
+
+ UCBStorageStream_Impl( const String&, StreamMode, UCBStorageStream*, BOOL, const ByteString* pKey=0, BOOL bRepair = FALSE, Reference< XProgressHandler > xProgress = Reference< XProgressHandler >() );
+
+ void Free();
+ BOOL Init();
+ BOOL Clear();
+ sal_Int16 Commit(); // if modified and commited: transfer an XInputStream to the content
+ BOOL Revert(); // discard all changes
+ BaseStorage* CreateStorage();// create an OLE Storage on the UCBStorageStream
+ ULONG GetSize();
+
+ ULONG ReadSourceWriteTemporary( ULONG aLength ); // read aLength from source and copy to temporary,
+ // no seeking is produced
+ ULONG ReadSourceWriteTemporary(); // read source till the end and copy to temporary,
+ // no seeking is produced
+#if 0
+ ULONG CopySourceToTemporary( ULONG aLength ); // same as ReadSourceWriteToTemporary( aLength )
+ // but the writing is done at the end of temporary
+ // pointer position is not changed
+#endif
+
+ ULONG CopySourceToTemporary(); // same as ReadSourceWriteToTemporary()
+ // but the writing is done at the end of temporary
+ // pointer position is not changed
+ Reference<XInputStream> GetXInputStream(); // return XInputStream, after that
+ // this class is close to be unusable
+ // since it can not read and write
+ using SvStream::SetError;
+ void SetError( sal_uInt32 nError );
+ void PrepareCachedForReopen( StreamMode nMode );
+};
+
+SV_DECL_IMPL_REF( UCBStorageStream_Impl );
+
+struct UCBStorageElement_Impl;
+DECLARE_LIST( UCBStorageElementList_Impl, UCBStorageElement_Impl* )
+
+class UCBStorage_Impl : public SvRefBase
+{
+ ~UCBStorage_Impl();
+public:
+ UCBStorage* m_pAntiImpl; // only valid if external references exists
+
+ String m_aOriginalName;// the original name before accessing the storage
+ String m_aName; // the actual name ( changed with a Rename command at the parent )
+ String m_aURL; // the full path name to create the content
+ String m_aContentType;
+ String m_aOriginalContentType;
+ ::ucbhelper::Content* m_pContent; // the content that provides the storage elements
+ ::utl::TempFile* m_pTempFile; // temporary file, only for storages on stream
+ SvStream* m_pSource; // original stream, only for storages on a stream
+ //SvStream* m_pStream; // the corresponding editable stream, only for storage on a stream
+ long m_nError;
+ StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing )
+ BOOL m_bModified; // only modified elements will be sent to the original content
+ BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package
+ BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages
+ // this means that the root storage does an autocommit when its external
+ // reference is destroyed
+ BOOL m_bIsRoot; // marks this storage as root storages that manages all oommits and reverts
+ BOOL m_bDirty; // ???
+ BOOL m_bIsLinked;
+ BOOL m_bListCreated;
+ ULONG m_nFormat;
+ String m_aUserTypeName;
+ SvGlobalName m_aClassId;
+
+ UCBStorageElementList_Impl m_aChildrenList;
+
+ BOOL m_bRepairPackage;
+ Reference< XProgressHandler > m_xProgressHandler;
+
+ UNOStorageHolderList* m_pUNOStorageHolderList;
+ UCBStorage_Impl( const ::ucbhelper::Content&, const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() );
+ UCBStorage_Impl( const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() );
+ UCBStorage_Impl( SvStream&, UCBStorage*, BOOL );
+ void Init();
+ sal_Int16 Commit();
+ BOOL Revert();
+ BOOL Insert( ::ucbhelper::Content *pContent );
+ UCBStorage_Impl* OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect );
+ UCBStorageStream_Impl* OpenStream( UCBStorageElement_Impl*, StreamMode, BOOL, const ByteString* pKey=0 );
+ void SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const String& );
+ void GetProps( sal_Int32&, Sequence < Sequence < PropertyValue > >& rSequence, const String& );
+ sal_Int32 GetObjectCount();
+ void ReadContent();
+ void CreateContent();
+ ::ucbhelper::Content* GetContent()
+ { if ( !m_pContent ) CreateContent(); return m_pContent; }
+ UCBStorageElementList_Impl& GetChildrenList()
+ {
+ long nError = m_nError;
+ ReadContent();
+ if ( m_nMode & STREAM_WRITE )
+ {
+ m_nError = nError;
+ if ( m_pAntiImpl )
+ {
+ m_pAntiImpl->ResetError();
+ m_pAntiImpl->SetError( nError );
+ }
+ }
+
+ return m_aChildrenList;
+ }
+
+ void SetError( long nError );
+};
+
+SV_DECL_IMPL_REF( UCBStorage_Impl );
+
+// this struct contains all neccessary information on an element inside a UCBStorage
+struct UCBStorageElement_Impl
+{
+ String m_aName; // the actual URL relative to the root "folder"
+ String m_aOriginalName;// the original name in the content
+ ULONG m_nSize;
+ BOOL m_bIsFolder; // Only TRUE when it is a UCBStorage !
+ BOOL m_bIsStorage; // Also TRUE when it is an OLEStorage !
+ BOOL m_bIsRemoved; // element will be removed on commit
+ BOOL m_bIsInserted; // element will be removed on revert
+ UCBStorage_ImplRef m_xStorage; // reference to the "real" storage
+ UCBStorageStream_ImplRef m_xStream; // reference to the "real" stream
+
+ UCBStorageElement_Impl( const ::rtl::OUString& rName,
+ BOOL bIsFolder = FALSE, ULONG nSize = 0 )
+ : m_aName( rName )
+ , m_aOriginalName( rName )
+ , m_nSize( nSize )
+ , m_bIsFolder( bIsFolder )
+ , m_bIsStorage( bIsFolder )
+ , m_bIsRemoved( FALSE )
+ , m_bIsInserted( FALSE )
+ {
+ }
+
+ ::ucbhelper::Content* GetContent();
+ BOOL IsModified();
+ String GetContentType();
+ void SetContentType( const String& );
+ String GetOriginalContentType();
+ BOOL IsLoaded()
+ { return m_xStream.Is() || m_xStorage.Is(); }
+};
+
+::ucbhelper::Content* UCBStorageElement_Impl::GetContent()
+{
+ if ( m_xStream.Is() )
+ return m_xStream->m_pContent;
+ else if ( m_xStorage.Is() )
+ return m_xStorage->GetContent();
+ else
+ return NULL;
+}
+
+String UCBStorageElement_Impl::GetContentType()
+{
+ if ( m_xStream.Is() )
+ return m_xStream->m_aContentType;
+ else if ( m_xStorage.Is() )
+ return m_xStorage->m_aContentType;
+ else
+ {
+ DBG_ERROR("Element not loaded!");
+ return String();
+ }
+}
+
+void UCBStorageElement_Impl::SetContentType( const String& rType )
+{
+ if ( m_xStream.Is() ) {
+ m_xStream->m_aContentType = m_xStream->m_aOriginalContentType = rType;
+ }
+ else if ( m_xStorage.Is() ) {
+ m_xStorage->m_aContentType = m_xStorage->m_aOriginalContentType = rType;
+ }
+ else {
+ DBG_ERROR("Element not loaded!");
+ }
+}
+
+String UCBStorageElement_Impl::GetOriginalContentType()
+{
+ if ( m_xStream.Is() )
+ return m_xStream->m_aOriginalContentType;
+ else if ( m_xStorage.Is() )
+ return m_xStorage->m_aOriginalContentType;
+ else
+ return String();
+}
+
+BOOL UCBStorageElement_Impl::IsModified()
+{
+ BOOL bModified = m_bIsRemoved || m_bIsInserted || m_aName != m_aOriginalName;
+ if ( bModified )
+ {
+ if ( m_xStream.Is() )
+ bModified = m_xStream->m_aContentType != m_xStream->m_aOriginalContentType;
+ else if ( m_xStorage.Is() )
+ bModified = m_xStorage->m_aContentType != m_xStorage->m_aOriginalContentType;
+ }
+
+ return bModified;
+}
+
+UCBStorageStream_Impl::UCBStorageStream_Impl( const String& rName, StreamMode nMode, UCBStorageStream* pStream, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress )
+ : m_pAntiImpl( pStream )
+ , m_aURL( rName )
+ , m_pContent( NULL )
+ , m_pStream( NULL )
+ , m_nRepresentMode( nonset )
+ , m_nError( 0 )
+ , m_nMode( nMode )
+ , m_bSourceRead( !( nMode & STREAM_TRUNC ) )
+ , m_bModified( FALSE )
+ , m_bCommited( FALSE )
+ , m_bDirect( bDirect )
+ , m_bIsOLEStorage( FALSE )
+{
+ // name is last segment in URL
+ INetURLObject aObj( rName );
+ m_aName = m_aOriginalName = aObj.GetLastName();
+ try
+ {
+ // create the content
+ Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv;
+
+ ::rtl::OUString aTemp( rName );
+
+ if ( bRepair )
+ {
+ xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(),
+ xProgress );
+ aTemp += rtl::OUString::createFromAscii("?repairpackage");
+ }
+
+ m_pContent = new ::ucbhelper::Content( aTemp, xComEnv );
+
+ if ( pKey )
+ {
+ m_aKey = *pKey;
+
+ // stream is encrypted and should be decrypted (without setting the key we'll get the raw data)
+ sal_uInt8 aBuffer[RTL_DIGEST_LENGTH_SHA1];
+ rtlDigestError nErr = rtl_digest_SHA1( pKey->GetBuffer(), pKey->Len(), aBuffer, RTL_DIGEST_LENGTH_SHA1 );
+ if ( nErr == rtl_Digest_E_None )
+ {
+ sal_uInt8* pBuffer = aBuffer;
+ ::com::sun::star::uno::Sequence < sal_Int8 > aSequ( (sal_Int8*) pBuffer, RTL_DIGEST_LENGTH_SHA1 );
+ ::com::sun::star::uno::Any aAny;
+ aAny <<= aSequ;
+ m_pContent->setPropertyValue( ::rtl::OUString::createFromAscii("EncryptionKey"), aAny );
+ }
+ }
+ }
+ catch ( ContentCreationException& )
+ {
+ // content could not be created
+ SetError( SVSTREAM_CANNOT_MAKE );
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+}
+
+UCBStorageStream_Impl::~UCBStorageStream_Impl()
+{
+ if( m_rSource.is() )
+ m_rSource = Reference< XInputStream >();
+
+ if( m_pStream )
+ delete m_pStream;
+
+ if ( m_aTempURL.Len() )
+ ::utl::UCBContentHelper::Kill( m_aTempURL );
+
+ if( m_pContent )
+ delete m_pContent;
+}
+
+
+Reference<XInputStream> UCBStorageStream_Impl::GetXInputStream()
+{
+ Reference< XInputStream > aResult;
+
+ if( m_pAntiImpl && m_nRepresentMode != nonset )
+ {
+ DBG_ERROR( "Misuse of the XInputstream!" );
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ }
+ else
+ {
+ if( m_bModified )
+ {
+ // use wrapper around temporary stream
+ if( Init() )
+ {
+ CopySourceToTemporary();
+
+ // owner transfer of stream to wrapper
+ aResult = new ::utl::OInputStreamWrapper( m_pStream, TRUE );
+ m_pStream->Seek(0);
+
+ if( aResult.is() )
+ {
+ // temporary stream can not be used here any more
+ // and can not be opened untill wrapper is closed
+ // stream is deleted by wrapper after use
+ m_pStream = NULL;
+ m_nRepresentMode = xinputstream;
+ }
+ }
+ }
+ else
+ {
+ Free();
+
+ // open a new instance of XInputStream
+ try
+ {
+ aResult = m_pContent->openStream();
+ }
+ catch ( Exception& )
+ {
+ // usually means that stream could not be opened
+ }
+
+ if( aResult.is() )
+ m_nRepresentMode = xinputstream;
+ else
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ }
+ }
+
+ return aResult;
+}
+
+BOOL UCBStorageStream_Impl::Init()
+{
+ if( m_nRepresentMode == xinputstream )
+ {
+ DBG_ERROR( "XInputStream misuse!" );
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ return FALSE;
+ }
+
+ if( !m_pStream )
+ {
+ // no temporary stream was created
+ // create one
+
+ m_nRepresentMode = svstream; // can not be used as XInputStream
+
+ if ( !m_aTempURL.Len() )
+ m_aTempURL = ::utl::TempFile().GetURL();
+
+ m_pStream = ::utl::UcbStreamHelper::CreateStream( m_aTempURL, STREAM_STD_READWRITE, sal_True /* bFileExists */ );
+#if OSL_DEBUG_LEVEL > 1
+ ++nOpenFiles;
+#endif
+
+ if( !m_pStream )
+ {
+ DBG_ERROR( "Suspicious temporary stream creation!" );
+ SetError( SVSTREAM_CANNOT_MAKE );
+ return FALSE;
+ }
+
+ SetError( m_pStream->GetError() );
+ }
+
+ if( m_bSourceRead && !m_rSource.is() )
+ {
+ // source file contain usefull information and is not opened
+ // open it from the point of noncopied data
+
+ try
+ {
+ m_rSource = m_pContent->openStream();
+ }
+ catch ( Exception& )
+ {
+ // usually means that stream could not be opened
+ }
+
+ if( m_rSource.is() )
+ {
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+
+ try
+ {
+ m_rSource->skipBytes( m_pStream->Tell() );
+ }
+ catch( BufferSizeExceededException& )
+ {
+ // the temporary stream already contain all the data
+ m_bSourceRead = FALSE;
+ }
+ catch( Exception& )
+ {
+ // something is really wrong
+ m_bSourceRead = FALSE;
+ DBG_ERROR( "Can not operate original stream!" );
+ SetError( SVSTREAM_CANNOT_MAKE );
+ }
+
+ m_pStream->Seek( 0 );
+ }
+ else
+ {
+ // if the new file is edited than no source exist
+ m_bSourceRead = FALSE;
+ //SetError( SVSTREAM_CANNOT_MAKE );
+ }
+ }
+
+ DBG_ASSERT( m_rSource.is() || !m_bSourceRead, "Unreadable source stream!" );
+
+ return sal_True;
+}
+
+ULONG UCBStorageStream_Impl::ReadSourceWriteTemporary()
+{
+ // read source stream till the end and copy all the data to
+ // the current position of the temporary stream
+
+ ULONG aResult = 0;
+
+ if( m_bSourceRead )
+ {
+ Sequence<sal_Int8> aData(32000);
+
+ try
+ {
+ ULONG aReaded;
+ do
+ {
+ aReaded = m_rSource->readBytes( aData, 32000 );
+ aResult += m_pStream->Write( aData.getArray(), aReaded );
+ } while( aReaded == 32000 );
+ }
+#if OSL_DEBUG_LEVEL > 1
+ catch( Exception & e )
+ {
+ OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
+#else
+ catch( Exception & )
+ {
+#endif
+ }
+ }
+
+ m_bSourceRead = FALSE;
+
+ return aResult;
+
+}
+
+ULONG UCBStorageStream_Impl::ReadSourceWriteTemporary( ULONG aLength )
+{
+ // read aLength bite from the source stream and copy them to the current
+ // position of the temporary stream
+
+ ULONG aResult = 0;
+
+ if( m_bSourceRead )
+ {
+ Sequence<sal_Int8> aData(32000);
+
+ try
+ {
+
+ ULONG aReaded = 32000;
+
+ for( ULONG pInd = 0; pInd < aLength && aReaded == 32000 ; pInd += 32000 )
+ {
+ ULONG aToCopy = min( aLength - pInd, 32000 );
+ aReaded = m_rSource->readBytes( aData, aToCopy );
+ aResult += m_pStream->Write( aData.getArray(), aReaded );
+ }
+
+ if( aResult < aLength )
+ m_bSourceRead = FALSE;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ catch( Exception & e )
+ {
+ OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
+#else
+ catch( Exception & )
+ {
+#endif
+ }
+ }
+
+ return aResult;
+}
+
+ULONG UCBStorageStream_Impl::CopySourceToTemporary()
+{
+ // current position of the temporary stream is not changed
+ ULONG aResult = 0;
+
+ if( m_bSourceRead )
+ {
+ ULONG aPos = m_pStream->Tell();
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+ aResult = ReadSourceWriteTemporary();
+ m_pStream->Seek( aPos );
+ }
+
+ return aResult;
+
+}
+
+#if 0
+ULONG UCBStorageStream_Impl::CopySourceToTemporary( ULONG aLength )
+{
+ // current position of the temporary stream is not changed
+ ULONG aResult = 0;
+
+ if( m_bSourceRead )
+ {
+ ULONG aPos = m_pStream->Tell();
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+ aResult = ReadSourceWriteTemporary( aLength );
+ m_pStream->Seek( aPos );
+ }
+
+ return aResult;
+
+}
+#endif
+
+// UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream
+// of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified
+ULONG UCBStorageStream_Impl::GetData( void* pData, ULONG nSize )
+{
+ ULONG aResult = 0;
+
+ if( !Init() )
+ return 0;
+
+
+ // read data that is in temporary stream
+ aResult = m_pStream->Read( pData, nSize );
+ if( m_bSourceRead && aResult < nSize )
+ {
+ // read the tail of the data from original stream
+ // copy this tail to the temporary stream
+
+ ULONG aToRead = nSize - aResult;
+ pData = (void*)( (char*)pData + aResult );
+
+ try
+ {
+ Sequence<sal_Int8> aData( aToRead );
+ ULONG aReaded = m_rSource->readBytes( aData, aToRead );
+ aResult += m_pStream->Write( (void*)aData.getArray(), aReaded );
+ memcpy( pData, aData.getArray(), aReaded );
+ }
+#if OSL_DEBUG_LEVEL > 1
+ catch( Exception & e )
+ {
+ OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
+#else
+ catch( Exception & )
+ {
+#endif
+ }
+
+ if( aResult < nSize )
+ m_bSourceRead = FALSE;
+ }
+
+ return aResult;
+}
+
+ULONG UCBStorageStream_Impl::PutData( const void* pData, ULONG nSize )
+{
+ if ( !(m_nMode & STREAM_WRITE) )
+ {
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ return 0; // ?mav?
+ }
+
+ if( !nSize || !Init() )
+ return 0;
+
+ ULONG aResult = m_pStream->Write( pData, nSize );
+
+ m_bModified = aResult > 0;
+
+ return aResult;
+
+}
+
+ULONG UCBStorageStream_Impl::SeekPos( ULONG nPos )
+{
+ if( !Init() )
+ return 0;
+
+ ULONG aResult;
+
+ if( nPos == STREAM_SEEK_TO_END )
+ {
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+ ReadSourceWriteTemporary();
+ aResult = m_pStream->Tell();
+ }
+ else
+ {
+ // the problem is that even if nPos is larger the the length
+ // of the stream the stream pointer will be moved to this position
+ // so we have to check if temporary stream does not contain required position
+
+ if( m_pStream->Tell() > nPos
+ || m_pStream->Seek( STREAM_SEEK_TO_END ) > nPos )
+ {
+ // no copiing is required
+ aResult = m_pStream->Seek( nPos );
+ }
+ else
+ {
+ // the temp stream pointer points to the end now
+ aResult = m_pStream->Tell();
+
+ if( aResult < nPos )
+ {
+ if( m_bSourceRead )
+ {
+ aResult += ReadSourceWriteTemporary( nPos - aResult );
+ if( aResult < nPos )
+ m_bSourceRead = FALSE;
+
+ DBG_ASSERT( aResult == m_pStream->Tell(), "Error in stream arithmetic!\n" );
+ }
+
+ if( (m_nMode & STREAM_WRITE) && !m_bSourceRead && aResult < nPos )
+ {
+ // it means that all the Source stream was copied already
+ // but the required position still was not reached
+ // for writable streams it should be done
+ m_pStream->SetStreamSize( nPos );
+ aResult = m_pStream->Seek( STREAM_SEEK_TO_END );
+ DBG_ASSERT( aResult == nPos, "Error in stream arithmetic!\n" );
+ }
+ }
+ }
+ }
+
+ return aResult;
+}
+
+void UCBStorageStream_Impl::SetSize( ULONG nSize )
+{
+ if ( !(m_nMode & STREAM_WRITE) )
+ {
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ return;
+ }
+
+ if( !Init() )
+ return;
+
+ m_bModified = TRUE;
+
+ if( m_bSourceRead )
+ {
+ ULONG aPos = m_pStream->Tell();
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+ if( m_pStream->Tell() < nSize )
+ ReadSourceWriteTemporary( nSize - m_pStream->Tell() );
+ m_pStream->Seek( aPos );
+ }
+
+ m_pStream->SetStreamSize( nSize );
+ m_bSourceRead = FALSE;
+}
+
+void UCBStorageStream_Impl::FlushData()
+{
+ if( m_pStream )
+ {
+ CopySourceToTemporary();
+ m_pStream->Flush();
+ }
+
+ m_bCommited = TRUE;
+}
+
+void UCBStorageStream_Impl::SetError( sal_uInt32 nErr )
+{
+ if ( !m_nError )
+ {
+ m_nError = nErr;
+ SvStream::SetError( nErr );
+ if ( m_pAntiImpl ) m_pAntiImpl->SetError( nErr );
+ }
+}
+
+void UCBStorageStream_Impl::ResetError()
+{
+ m_nError = 0;
+ SvStream::ResetError();
+ if ( m_pAntiImpl )
+ m_pAntiImpl->ResetError();
+}
+
+ULONG UCBStorageStream_Impl::GetSize()
+{
+ if( !Init() )
+ return 0;
+
+ ULONG nPos = m_pStream->Tell();
+ m_pStream->Seek( STREAM_SEEK_TO_END );
+ ReadSourceWriteTemporary();
+ ULONG nRet = m_pStream->Tell();
+ m_pStream->Seek( nPos );
+
+ return nRet;
+}
+
+BaseStorage* UCBStorageStream_Impl::CreateStorage()
+{
+ // create an OLEStorage on a SvStream ( = this )
+ // it gets the root attribute because otherwise it would probably not write before my root is commited
+ UCBStorageStream* pNewStorageStream = new UCBStorageStream( this );
+ Storage *pStorage = new Storage( *pNewStorageStream, m_bDirect );
+
+ // GetError() call cleares error code for OLE storages, must be changed in future
+ long nTmpErr = pStorage->GetError();
+ pStorage->SetError( nTmpErr );
+
+ m_bIsOLEStorage = !nTmpErr;
+ return static_cast< BaseStorage* > ( pStorage );
+}
+
+sal_Int16 UCBStorageStream_Impl::Commit()
+{
+ // send stream to the original content
+ // the parent storage is responsible for the correct handling of deleted contents
+ if ( m_bCommited || m_bIsOLEStorage || m_bDirect )
+ {
+ // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage
+ // was commited as well ( if not opened in direct mode )
+
+ if ( m_bModified )
+ {
+ try
+ {
+ CopySourceToTemporary();
+
+ // release all stream handles
+ Free();
+
+ // the temporary file does not exist only for truncated streams
+ DBG_ASSERT( m_aTempURL.Len() || ( m_nMode & STREAM_TRUNC ), "No temporary file to read from!");
+ if ( !m_aTempURL.Len() && !( m_nMode & STREAM_TRUNC ) )
+ throw RuntimeException();
+
+ // create wrapper to stream that is only used while reading inside package component
+ Reference < XInputStream > xStream = new FileStreamWrapper_Impl( m_aTempURL );
+
+ Any aAny;
+ InsertCommandArgument aArg;
+ aArg.Data = xStream;
+ aArg.ReplaceExisting = sal_True;
+ aAny <<= aArg;
+ m_pContent->executeCommand( ::rtl::OUString::createFromAscii("insert"), aAny );
+
+ // wrapper now controls lifetime of temporary file
+ m_aTempURL.Erase();
+
+ INetURLObject aObj( m_aURL );
+ aObj.SetName( m_aName );
+ m_aURL = aObj.GetMainURL( INetURLObject::NO_DECODE );
+ m_bModified = FALSE;
+ m_bSourceRead = TRUE;
+ }
+ catch ( CommandAbortedException& )
+ {
+ // any command wasn't executed successfully - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( Exception& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+
+ m_bCommited = FALSE;
+ return COMMIT_RESULT_SUCCESS;
+ }
+ }
+
+ return COMMIT_RESULT_NOTHING_TO_DO;
+}
+
+BOOL UCBStorageStream_Impl::Revert()
+{
+ // if an OLEStorage is created on this stream, no "revert" is neccessary because OLEStorages do nothing on "Revert" !
+ if ( m_bCommited )
+ {
+ DBG_ERROR("Revert while commit is in progress!" );
+ return FALSE; // ???
+ }
+
+ Free();
+ if ( m_aTempURL.Len() )
+ {
+ ::utl::UCBContentHelper::Kill( m_aTempURL );
+ m_aTempURL.Erase();
+ }
+
+ m_bSourceRead = FALSE;
+ try
+ {
+ m_rSource = m_pContent->openStream();
+ if( m_rSource.is() )
+ {
+ if ( m_pAntiImpl && ( m_nMode & STREAM_TRUNC ) )
+ // stream is in use and should be truncated
+ m_bSourceRead = FALSE;
+ else
+ {
+ m_nMode &= ~STREAM_TRUNC;
+ m_bSourceRead = TRUE;
+ }
+ }
+ else
+ SetError( SVSTREAM_CANNOT_MAKE );
+ }
+ catch ( ContentCreationException& )
+ {
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( RuntimeException& )
+ {
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( Exception& )
+ {
+ }
+
+ m_bModified = FALSE;
+ m_aName = m_aOriginalName;
+ m_aContentType = m_aOriginalContentType;
+ return ( GetError() == ERRCODE_NONE );
+}
+
+BOOL UCBStorageStream_Impl::Clear()
+{
+ BOOL bRet = ( m_pAntiImpl == NULL );
+ DBG_ASSERT( bRet, "Removing used stream!" );
+ if( bRet )
+ {
+ Free();
+ }
+
+ return bRet;
+}
+
+void UCBStorageStream_Impl::Free()
+{
+#if OSL_DEBUG_LEVEL > 1
+ if ( m_pStream )
+ {
+ if ( m_aTempURL.Len() )
+ --nOpenFiles;
+ else
+ --nOpenStreams;
+ }
+#endif
+
+ m_nRepresentMode = nonset;
+ m_rSource = Reference< XInputStream >();
+ DELETEZ( m_pStream );
+}
+
+void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode )
+{
+ sal_Bool isWritable = (( m_nMode & STREAM_WRITE ) != 0 );
+ if ( isWritable )
+ {
+ // once stream was writable, never reset to readonly
+ nMode |= STREAM_WRITE;
+ }
+
+ m_nMode = nMode;
+ Free();
+
+ if ( nMode & STREAM_TRUNC )
+ {
+ m_bSourceRead = 0; // usually it should be 0 already but just in case...
+
+ if ( m_aTempURL.Len() )
+ {
+ ::utl::UCBContentHelper::Kill( m_aTempURL );
+ m_aTempURL.Erase();
+ }
+ }
+}
+
+UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey )
+{
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorageStream !
+ pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey );
+ pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress )
+{
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorageStream !
+ pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey, bRepair, xProgress );
+ pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl *pImpl )
+ : pImp( pImpl )
+{
+ pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used
+ pImp->m_pAntiImpl = this;
+ SetError( pImp->m_nError );
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorageStream::~UCBStorageStream()
+{
+ if ( pImp->m_nMode & STREAM_WRITE )
+ pImp->Flush();
+ pImp->m_pAntiImpl = NULL;
+ pImp->Free();
+ pImp->ReleaseRef();
+}
+
+ULONG UCBStorageStream::Read( void * pData, ULONG nSize )
+{
+ //return pImp->m_pStream->Read( pData, nSize );
+ return pImp->GetData( pData, nSize );
+}
+
+ULONG UCBStorageStream::Write( const void* pData, ULONG nSize )
+{
+/*
+ // mba: does occur in writer !
+ if ( pImp->m_bCommited )
+ {
+ DBG_ERROR("Writing while commit is in progress!" );
+ return 0;
+ }
+*/
+ // pImp->m_bModified = TRUE;
+ //return pImp->m_pStream->Write( pData, nSize );
+ return pImp->PutData( pData, nSize );
+}
+
+ULONG UCBStorageStream::Seek( ULONG nPos )
+{
+ //return pImp->m_pStream->Seek( nPos );
+ return pImp->Seek( nPos );
+}
+
+ULONG UCBStorageStream::Tell()
+{
+ if( !pImp->Init() )
+ return 0;
+ return pImp->m_pStream->Tell();
+}
+
+void UCBStorageStream::Flush()
+{
+ // streams are never really transacted, so flush also means commit !
+ Commit();
+}
+
+BOOL UCBStorageStream::SetSize( ULONG nNewSize )
+{
+/*
+ if ( pImp->m_bCommited )
+ {
+ DBG_ERROR("Changing stream size while commit is in progress!" );
+ return FALSE;
+ }
+*/
+ // pImp->m_bModified = TRUE;
+ //return pImp->m_pStream->SetStreamSize( nNewSize );
+ pImp->SetSize( nNewSize );
+ return !pImp->GetError();
+}
+
+BOOL UCBStorageStream::Validate( BOOL bWrite ) const
+{
+ return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) );
+}
+
+BOOL UCBStorageStream::ValidateMode( StreamMode m ) const
+{
+ // ???
+ if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx
+ return TRUE;
+ USHORT nCurMode = 0xFFFF;
+ if( ( m & 3 ) == STREAM_READ )
+ {
+ // only SHARE_DENYWRITE or SHARE_DENYALL allowed
+ if( ( ( m & STREAM_SHARE_DENYWRITE )
+ && ( nCurMode & STREAM_SHARE_DENYWRITE ) )
+ || ( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) ) )
+ return TRUE;
+ }
+ else
+ {
+ // only SHARE_DENYALL allowed
+ // storages open in r/o mode are OK, since only
+ // the commit may fail
+ if( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) )
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+const SvStream* UCBStorageStream::GetSvStream() const
+{
+ if( !pImp->Init() )
+ return NULL;
+
+ pImp->CopySourceToTemporary();
+ return pImp->m_pStream; // should not live longer then pImp!!!
+}
+
+SvStream* UCBStorageStream::GetModifySvStream()
+{
+ return (SvStream*)pImp;
+}
+
+Reference< XInputStream > UCBStorageStream::GetXInputStream() const
+{
+ return pImp->GetXInputStream();
+}
+
+BOOL UCBStorageStream::Equals( const BaseStorageStream& rStream ) const
+{
+ // ???
+ return ((BaseStorageStream*) this ) == &rStream;
+}
+
+BOOL UCBStorageStream::Commit()
+{
+ // mark this stream for sending it on root commit
+ pImp->FlushData();
+ return TRUE;
+}
+
+BOOL UCBStorageStream::Revert()
+{
+ return pImp->Revert();
+}
+
+BOOL UCBStorageStream::CopyTo( BaseStorageStream* pDestStm )
+{
+ if( !pImp->Init() )
+ return FALSE;
+
+ UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pDestStm );
+ if ( pStg )
+ pStg->pImp->m_aContentType = pImp->m_aContentType;
+
+ pDestStm->SetSize( 0 );
+ Seek( STREAM_SEEK_TO_END );
+ INT32 n = Tell();
+ if( n < 0 )
+ return FALSE;
+
+ if( pDestStm->SetSize( n ) && n )
+ {
+ BYTE* p = new BYTE[ 4096 ];
+ Seek( 0L );
+ pDestStm->Seek( 0L );
+ while( n )
+ {
+ UINT32 nn = n;
+ if( nn > 4096 )
+ nn = 4096;
+ if( Read( p, nn ) != nn )
+ break;
+ if( pDestStm->Write( p, nn ) != nn )
+ break;
+ n -= nn;
+ }
+
+ delete[] p;
+ }
+
+ return TRUE;
+}
+
+BOOL UCBStorageStream::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue )
+{
+ if ( rName.CompareToAscii("Title") == COMPARE_EQUAL )
+ return FALSE;
+
+ if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL )
+ {
+ ::rtl::OUString aTmp;
+ rValue >>= aTmp;
+ pImp->m_aContentType = aTmp;
+ }
+
+ try
+ {
+ if ( pImp->m_pContent )
+ {
+ pImp->m_pContent->setPropertyValue( rName, rValue );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+
+ return FALSE;
+}
+
+BOOL UCBStorageStream::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ try
+ {
+ if ( pImp->m_pContent )
+ {
+ rValue = pImp->m_pContent->getPropertyValue( rName );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+
+ return FALSE;
+}
+
+UCBStorage::UCBStorage( SvStream& rStrm, BOOL bDirect )
+{
+ String aURL = GetLinkedFile( rStrm );
+ if ( aURL.Len() )
+ {
+ StreamMode nMode = STREAM_READ;
+ if( rStrm.IsWritable() )
+ nMode = STREAM_READ | STREAM_WRITE;
+
+ ::ucbhelper::Content aContent( aURL, Reference < XCommandEnvironment >() );
+ pImp = new UCBStorage_Impl( aContent, aURL, nMode, this, bDirect, TRUE );
+ }
+ else
+ {
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorage !
+ pImp = new UCBStorage_Impl( rStrm, this, bDirect );
+ }
+
+ pImp->AddRef();
+ pImp->Init();
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorage::UCBStorage( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot )
+{
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorage !
+ pImp = new UCBStorage_Impl( rContent, rName, nMode, this, bDirect, bIsRoot );
+ pImp->AddRef();
+ pImp->Init();
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler )
+{
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorage !
+ pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, bIsRepair, xProgressHandler );
+ pImp->AddRef();
+ pImp->Init();
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot )
+{
+ // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
+ // to class UCBStorage !
+ pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, sal_False, Reference< XProgressHandler >() );
+ pImp->AddRef();
+ pImp->Init();
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorage::UCBStorage( UCBStorage_Impl *pImpl )
+ : pImp( pImpl )
+{
+ pImp->m_pAntiImpl = this;
+ SetError( pImp->m_nError );
+ pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used
+ StorageBase::m_nMode = pImp->m_nMode;
+}
+
+UCBStorage::~UCBStorage()
+{
+ if ( pImp->m_bIsRoot && pImp->m_bDirect && ( !pImp->m_pTempFile || pImp->m_pSource ) )
+ // DirectMode is simulated with an AutoCommit
+ Commit();
+
+ pImp->m_pAntiImpl = NULL;
+ pImp->ReleaseRef();
+}
+
+UCBStorage_Impl::UCBStorage_Impl( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler )
+ : m_pAntiImpl( pStorage )
+ , m_pContent( new ::ucbhelper::Content( rContent ) )
+ , m_pTempFile( NULL )
+ , m_pSource( NULL )
+ //, m_pStream( NULL )
+ , m_nError( 0 )
+ , m_nMode( nMode )
+ , m_bModified( FALSE )
+ , m_bCommited( FALSE )
+ , m_bDirect( bDirect )
+ , m_bIsRoot( bIsRoot )
+ , m_bDirty( FALSE )
+ , m_bIsLinked( TRUE )
+ , m_bListCreated( FALSE )
+ , m_nFormat( 0 )
+ , m_aClassId( SvGlobalName() )
+ , m_bRepairPackage( bIsRepair )
+ , m_xProgressHandler( xProgressHandler )
+ , m_pUNOStorageHolderList( NULL )
+
+{
+ String aName( rName );
+ if( !aName.Len() )
+ {
+ // no name given = use temporary name!
+ DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" );
+ m_pTempFile = new ::utl::TempFile;
+ m_pTempFile->EnableKillingFile( TRUE );
+ m_aName = m_aOriginalName = aName = m_pTempFile->GetURL();
+ }
+
+ m_aURL = rName;
+}
+
+UCBStorage_Impl::UCBStorage_Impl( const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler )
+ : m_pAntiImpl( pStorage )
+ , m_pContent( NULL )
+ , m_pTempFile( NULL )
+ , m_pSource( NULL )
+ //, m_pStream( NULL )
+ , m_nError( 0 )
+ , m_nMode( nMode )
+ , m_bModified( FALSE )
+ , m_bCommited( FALSE )
+ , m_bDirect( bDirect )
+ , m_bIsRoot( bIsRoot )
+ , m_bDirty( FALSE )
+ , m_bIsLinked( FALSE )
+ , m_bListCreated( FALSE )
+ , m_nFormat( 0 )
+ , m_aClassId( SvGlobalName() )
+ , m_bRepairPackage( bIsRepair )
+ , m_xProgressHandler( xProgressHandler )
+ , m_pUNOStorageHolderList( NULL )
+{
+ String aName( rName );
+ if( !aName.Len() )
+ {
+ // no name given = use temporary name!
+ DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" );
+ m_pTempFile = new ::utl::TempFile;
+ m_pTempFile->EnableKillingFile( TRUE );
+ m_aName = m_aOriginalName = aName = m_pTempFile->GetURL();
+ }
+
+ if ( m_bIsRoot )
+ {
+ // create the special package URL for the package content
+ String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://");
+ aTemp += String(INetURLObject::encode( aName, INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL ));
+ m_aURL = aTemp;
+
+ if ( m_nMode & STREAM_WRITE )
+ {
+ // the root storage opens the package, so make sure that there is any
+ SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aName, STREAM_STD_READWRITE, m_pTempFile != 0 /* bFileExists */ );
+ delete pStream;
+ }
+ }
+ else
+ {
+ // substorages are opened like streams: the URL is a "child URL" of the root package URL
+ m_aURL = rName;
+ if ( m_aURL.CompareToAscii( "vnd.sun.star.pkg://", 19 ) != 0 )
+ m_bIsLinked = TRUE;
+ }
+}
+
+UCBStorage_Impl::UCBStorage_Impl( SvStream& rStream, UCBStorage* pStorage, BOOL bDirect )
+ : m_pAntiImpl( pStorage )
+ , m_pContent( NULL )
+ , m_pTempFile( new ::utl::TempFile )
+ , m_pSource( &rStream )
+ , m_nError( 0 )
+ , m_bModified( FALSE )
+ , m_bCommited( FALSE )
+ , m_bDirect( bDirect )
+ , m_bIsRoot( TRUE )
+ , m_bDirty( FALSE )
+ , m_bIsLinked( FALSE )
+ , m_bListCreated( FALSE )
+ , m_nFormat( 0 )
+ , m_aClassId( SvGlobalName() )
+ , m_bRepairPackage( FALSE )
+ , m_pUNOStorageHolderList( NULL )
+{
+ // opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call,
+ // which will be called in the storages' dtor
+ m_pTempFile->EnableKillingFile( TRUE );
+ DBG_ASSERT( !bDirect, "Storage on a stream must not be opened in direct mode!" );
+
+ // UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only
+ // accessed readonly
+ // the root storage opens the package; create the special package URL for the package content
+ String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://");
+ aTemp += String(INetURLObject::encode( m_pTempFile->GetURL(), INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL ));
+ m_aURL = aTemp;
+
+ // copy data into the temporary file
+ SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), STREAM_STD_READWRITE, sal_True /* bFileExists */ );
+ if ( pStream )
+ {
+ rStream.Seek(0);
+ rStream >> *pStream;
+ pStream->Flush();
+ DELETEZ( pStream );
+ }
+
+ // close stream and let content access the file
+ m_pSource->Seek(0);
+
+ // check opening mode
+ m_nMode = STREAM_READ;
+ if( rStream.IsWritable() )
+ m_nMode = STREAM_READ | STREAM_WRITE;
+}
+
+void UCBStorage_Impl::Init()
+{
+ // name is last segment in URL
+ INetURLObject aObj( m_aURL );
+ if ( !m_aName.Len() )
+ // if the name was not already set to a temp name
+ m_aName = m_aOriginalName = aObj.GetLastName();
+
+ // don't create the content for disk spanned files, avoid too early access to directory and/or manifest
+ if ( !m_pContent && !( m_nMode & STORAGE_DISKSPANNED_MODE ) )
+ CreateContent();
+
+ if ( m_nMode & STORAGE_DISKSPANNED_MODE )
+ {
+ // Hack! Avoid access to the manifest file until mediatype is not available in the first segment of a
+ // disk spanned file
+ m_aContentType = m_aOriginalContentType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.xml.impress") );
+ }
+ else if ( m_pContent )
+ {
+ if ( m_bIsLinked )
+ {
+ if( m_bIsRoot )
+ {
+ ReadContent();
+ if ( m_nError == ERRCODE_NONE )
+ {
+ // read the manifest.xml file
+ aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("META-INF") ) );
+ aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("manifest.xml") ) );
+
+ // create input stream
+ SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aObj.GetMainURL( INetURLObject::NO_DECODE ), STREAM_STD_READ );
+ // no stream means no manifest.xml
+ if ( pStream )
+ {
+ if ( !pStream->GetError() )
+ {
+ ::utl::OInputStreamWrapper* pHelper = new ::utl::OInputStreamWrapper( *pStream );
+ com::sun::star::uno::Reference < ::com::sun::star::io::XInputStream > xInputStream( pHelper );
+
+ // create a manifest reader object that will read in the manifest from the stream
+ Reference < ::com::sun::star::packages::manifest::XManifestReader > xReader =
+ Reference< ::com::sun::star::packages::manifest::XManifestReader >
+ ( ::comphelper::getProcessServiceFactory()->createInstance(
+ ::rtl::OUString::createFromAscii( "com.sun.star.packages.manifest.ManifestReader" )), UNO_QUERY) ;
+ Sequence < Sequence < PropertyValue > > aProps = xReader->readManifestSequence( xInputStream );
+
+ // cleanup
+ xReader = NULL;
+ xInputStream = NULL;
+ SetProps( aProps, String() );
+ }
+
+ delete pStream;
+ }
+ }
+ }
+ else
+ ReadContent();
+ }
+ else
+ {
+ // get the manifest information from the package
+ try {
+ Any aAny = m_pContent->getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) );
+ rtl::OUString aTmp;
+ if ( ( aAny >>= aTmp ) && aTmp.getLength() )
+ m_aContentType = m_aOriginalContentType = aTmp;
+ }
+ catch( Exception& )
+ {
+ DBG_ASSERT( sal_False,
+ "getPropertyValue has thrown an exception! Please let developers know the scenario!" );
+ }
+ }
+ }
+
+ if ( m_aContentType.Len() )
+ {
+ // get the clipboard format using the content type
+ ::com::sun::star::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = m_aContentType;
+ m_nFormat = SotExchange::GetFormat( aDataFlavor );
+
+ // get the ClassId using the clipboard format ( internal table )
+ m_aClassId = GetClassId_Impl( m_nFormat );
+
+ // get human presentable name using the clipboard format
+ SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor );
+ m_aUserTypeName = aDataFlavor.HumanPresentableName;
+
+ if( m_pContent && !m_bIsLinked && m_aClassId != SvGlobalName() )
+ ReadContent();
+ }
+}
+
+void UCBStorage_Impl::CreateContent()
+{
+ try
+ {
+ // create content; where to put StreamMode ?! ( already done when opening the file of the package ? )
+ Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv;
+
+ ::rtl::OUString aTemp( m_aURL );
+
+ if ( m_bRepairPackage )
+ {
+ xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(),
+ m_xProgressHandler );
+ aTemp += rtl::OUString::createFromAscii("?repairpackage");
+ }
+
+ m_pContent = new ::ucbhelper::Content( aTemp, xComEnv );
+ }
+ catch ( ContentCreationException& )
+ {
+ // content could not be created
+ SetError( SVSTREAM_CANNOT_MAKE );
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( SVSTREAM_CANNOT_MAKE );
+ }
+}
+
+void UCBStorage_Impl::ReadContent()
+{
+ if ( m_bListCreated )
+ return;
+
+ m_bListCreated = TRUE;
+
+ // create cursor for access to children
+ Sequence< ::rtl::OUString > aProps(4);
+ ::rtl::OUString* pProps = aProps.getArray();
+ pProps[0] = ::rtl::OUString::createFromAscii( "Title" );
+ pProps[1] = ::rtl::OUString::createFromAscii( "IsFolder" );
+ pProps[2] = ::rtl::OUString::createFromAscii( "MediaType" );
+ pProps[3] = ::rtl::OUString::createFromAscii( "Size" );
+ ::ucbhelper::ResultSetInclude eInclude = ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS;
+
+ try
+ {
+ GetContent();
+ if ( !m_pContent )
+ return;
+
+ Reference< XResultSet > xResultSet = m_pContent->createCursor( aProps, eInclude );
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ Reference< XRow > xRow( xResultSet, UNO_QUERY );
+ if ( xResultSet.is() )
+ {
+ while ( xResultSet->next() )
+ {
+ // insert all into the children list
+ ::rtl::OUString aTitle( xRow->getString(1) );
+ ::rtl::OUString aContentType;
+ if ( m_bIsLinked )
+ {
+ // unpacked storages have to deal with the meta-inf folder by themselves
+ if( aTitle.equalsAscii("META-INF") )
+ continue;
+ }
+ else
+ {
+ aContentType = xRow->getString(3);
+ }
+
+ BOOL bIsFolder( xRow->getBoolean(2) );
+ sal_Int64 nSize = xRow->getLong(4);
+ UCBStorageElement_Impl* pElement = new UCBStorageElement_Impl( aTitle, bIsFolder, (ULONG) nSize );
+ m_aChildrenList.Insert( pElement, LIST_APPEND );
+
+ sal_Bool bIsOfficeDocument = m_bIsLinked || ( m_aClassId != SvGlobalName() );
+ if ( bIsFolder )
+ {
+ if ( m_bIsLinked )
+ OpenStorage( pElement, m_nMode, m_bDirect );
+ if ( pElement->m_xStorage.Is() )
+ pElement->m_xStorage->Init();
+ }
+ else if ( bIsOfficeDocument )
+ {
+ // streams can be external OLE objects, so they are now folders, but storages!
+ String aName( m_aURL );
+ aName += '/';
+ aName += String( xRow->getString(1) );
+
+ Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv;
+ if ( m_bRepairPackage )
+ {
+ xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(),
+ m_xProgressHandler );
+ aName += String( RTL_CONSTASCII_USTRINGPARAM( "?repairpackage" ) );
+ }
+
+ ::ucbhelper::Content aContent( aName, xComEnv );
+
+ ::rtl::OUString aMediaType;
+ Any aAny = aContent.getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) );
+ if ( ( aAny >>= aMediaType ) && ( aMediaType.compareToAscii("application/vnd.sun.star.oleobject") == 0 ) )
+ pElement->m_bIsStorage = TRUE;
+ else if ( !aMediaType.getLength() )
+ {
+ // older files didn't have that special content type, so they must be detected
+ OpenStream( pElement, STREAM_STD_READ, m_bDirect );
+ if ( Storage::IsStorageFile( pElement->m_xStream ) )
+ pElement->m_bIsStorage = TRUE;
+ else
+ pElement->m_xStream->Free();
+ }
+ }
+ }
+ }
+ }
+ catch ( InteractiveIOException& r )
+ {
+ if ( r.Code != IOErrorCode_NOT_EXISTING )
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( CommandAbortedException& )
+ {
+ // any command wasn't executed successfully - not specified
+ if ( !( m_nMode & STREAM_WRITE ) )
+ // if the folder was just inserted and not already commited, this is not an error!
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( ResultSetException& )
+ {
+ // means that the package file is broken
+ SetError( ERRCODE_IO_BROKENPACKAGE );
+ }
+ catch ( SQLException& )
+ {
+ // means that the file can be broken
+ SetError( ERRCODE_IO_WRONGFORMAT );
+ }
+ catch ( Exception& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+}
+
+void UCBStorage_Impl::SetError( long nError )
+{
+ if ( !m_nError )
+ {
+ m_nError = nError;
+ if ( m_pAntiImpl ) m_pAntiImpl->SetError( nError );
+ }
+}
+
+sal_Int32 UCBStorage_Impl::GetObjectCount()
+{
+ sal_Int32 nCount = m_aChildrenList.Count();
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ while ( pElement )
+ {
+ DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" );
+ if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() )
+ nCount += pElement->m_xStorage->GetObjectCount();
+ pElement = m_aChildrenList.Next();
+ }
+
+ return nCount;
+}
+
+::rtl::OUString Find_Impl( const Sequence < Sequence < PropertyValue > >& rSequence, const ::rtl::OUString& rPath )
+{
+ BOOL bFound = FALSE;
+ for ( sal_Int32 nSeqs=0; nSeqs<rSequence.getLength(); nSeqs++ )
+ {
+ const Sequence < PropertyValue >& rMyProps = rSequence[nSeqs];
+ ::rtl::OUString aType;
+
+ for ( sal_Int32 nProps=0; nProps<rMyProps.getLength(); nProps++ )
+ {
+ const PropertyValue& rAny = rMyProps[nProps];
+ if ( rAny.Name.equalsAscii("FullPath") )
+ {
+ rtl::OUString aTmp;
+ if ( ( rAny.Value >>= aTmp ) && aTmp == rPath )
+ bFound = TRUE;
+ if ( aType.getLength() )
+ break;
+ }
+ else if ( rAny.Name.equalsAscii("MediaType") )
+ {
+ if ( ( rAny.Value >>= aType ) && aType.getLength() && bFound )
+ break;
+ }
+ }
+
+ if ( bFound )
+ return aType;
+ }
+
+ return ::rtl::OUString();
+}
+
+void UCBStorage_Impl::SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const String& rPath )
+{
+ String aPath( rPath );
+ if ( !m_bIsRoot )
+ aPath += m_aName;
+ aPath += '/';
+
+ m_aContentType = m_aOriginalContentType = Find_Impl( rSequence, aPath );
+
+ if ( m_bIsRoot )
+ // the "FullPath" of a child always starts without '/'
+ aPath.Erase();
+
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ while ( pElement )
+ {
+ DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" );
+ if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() )
+ pElement->m_xStorage->SetProps( rSequence, aPath );
+ else
+ {
+ String aElementPath( aPath );
+ aElementPath += pElement->m_aName;
+ pElement->SetContentType( Find_Impl( rSequence, aElementPath ) );
+ }
+
+ pElement = m_aChildrenList.Next();
+ }
+
+ if ( m_aContentType.Len() )
+ {
+ // get the clipboard format using the content type
+ ::com::sun::star::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = m_aContentType;
+ m_nFormat = SotExchange::GetFormat( aDataFlavor );
+
+ // get the ClassId using the clipboard format ( internal table )
+ m_aClassId = GetClassId_Impl( m_nFormat );
+
+ // get human presentable name using the clipboard format
+ SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor );
+ m_aUserTypeName = aDataFlavor.HumanPresentableName;
+ }
+}
+
+void UCBStorage_Impl::GetProps( sal_Int32& nProps, Sequence < Sequence < PropertyValue > >& rSequence, const String& rPath )
+{
+ // first my own properties
+ Sequence < PropertyValue > aProps(2);
+
+ // first property is the "FullPath" name
+ // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder
+ String aPath( rPath );
+ if ( !m_bIsRoot )
+ aPath += m_aName;
+ aPath += '/';
+ aProps[0].Name = ::rtl::OUString::createFromAscii("MediaType");
+ aProps[0].Value <<= (::rtl::OUString ) m_aContentType;
+ aProps[1].Name = ::rtl::OUString::createFromAscii("FullPath");
+ aProps[1].Value <<= (::rtl::OUString ) aPath;
+ rSequence[ nProps++ ] = aProps;
+
+ if ( m_bIsRoot )
+ // the "FullPath" of a child always starts without '/'
+ aPath.Erase();
+
+ // now the properties of my elements
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ while ( pElement )
+ {
+ DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" );
+ if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() )
+ // storages add there properties by themselves ( see above )
+ pElement->m_xStorage->GetProps( nProps, rSequence, aPath );
+ else
+ {
+ // properties of streams
+ String aElementPath( aPath );
+ aElementPath += pElement->m_aName;
+ aProps[0].Name = ::rtl::OUString::createFromAscii("MediaType");
+ aProps[0].Value <<= (::rtl::OUString ) pElement->GetContentType();
+ aProps[1].Name = ::rtl::OUString::createFromAscii("FullPath");
+ aProps[1].Value <<= (::rtl::OUString ) aElementPath;
+ rSequence[ nProps++ ] = aProps;
+ }
+
+ pElement = m_aChildrenList.Next();
+ }
+}
+
+UCBStorage_Impl::~UCBStorage_Impl()
+{
+ if ( m_pUNOStorageHolderList )
+ {
+ for ( UNOStorageHolderList::iterator aIter = m_pUNOStorageHolderList->begin();
+ aIter != m_pUNOStorageHolderList->end(); aIter++ )
+ if ( *aIter )
+ {
+ (*aIter)->InternalDispose();
+ (*aIter)->release();
+ (*aIter) = NULL;
+ }
+
+ m_pUNOStorageHolderList->clear();
+ DELETEZ( m_pUNOStorageHolderList );
+ }
+
+ // first delete elements!
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ while ( pElement )
+ {
+ delete pElement;
+ pElement = m_aChildrenList.Next();
+ }
+
+ m_aChildrenList.Clear();
+ delete m_pContent;
+ delete m_pTempFile;
+}
+
+BOOL UCBStorage_Impl::Insert( ::ucbhelper::Content *pContent )
+{
+ // a new substorage is inserted into a UCBStorage ( given by the parameter pContent )
+ // it must be inserted with a title and a type
+ BOOL bRet = FALSE;
+
+ try
+ {
+ Sequence< ContentInfo > aInfo = pContent->queryCreatableContentsInfo();
+ sal_Int32 nCount = aInfo.getLength();
+ if ( nCount == 0 )
+ return sal_False;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ // Simply look for the first KIND_FOLDER...
+ const ContentInfo & rCurr = aInfo[i];
+ if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER )
+ {
+ // Make sure the only required bootstrap property is "Title",
+ const Sequence< Property > & rProps = rCurr.Properties;
+ if ( rProps.getLength() != 1 )
+ continue;
+
+ if ( !rProps[ 0 ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) )
+ continue;
+
+ Sequence < ::rtl::OUString > aNames(1);
+ ::rtl::OUString* pNames = aNames.getArray();
+ pNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) );
+ Sequence < Any > aValues(1);
+ Any* pValues = aValues.getArray();
+ pValues[0] = makeAny( ::rtl::OUString( m_aName ) );
+
+ Content aNewFolder;
+ if ( !pContent->insertNewContent( rCurr.Type, aNames, aValues, aNewFolder ) )
+ continue;
+
+ // remove old content, create an "empty" new one and initialize it with the new inserted
+ DELETEZ( m_pContent );
+ m_pContent = new ::ucbhelper::Content( aNewFolder );
+ bRet = TRUE;
+ }
+ }
+ }
+ catch ( CommandAbortedException& )
+ {
+ // any command wasn't executed successfully - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+ catch ( Exception& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ }
+
+ return bRet;
+}
+
+sal_Int16 UCBStorage_Impl::Commit()
+{
+ // send all changes to the package
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ sal_Int16 nRet = COMMIT_RESULT_NOTHING_TO_DO;
+
+ // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no
+ // commit command has been sent
+ if ( ( m_nMode & STREAM_WRITE ) && ( m_bCommited || m_bDirect ) )
+ {
+ try
+ {
+ // all errors will be caught in the "catch" statement outside the loop
+ while ( pElement && nRet )
+ {
+ ::ucbhelper::Content* pContent = pElement->GetContent();
+ BOOL bDeleteContent = FALSE;
+ if ( !pContent && pElement->IsModified() )
+ {
+ // if the element has never been opened, no content has been created until now
+ bDeleteContent = TRUE; // remember to delete it later
+ String aName( m_aURL );
+ aName += '/';
+ aName += pElement->m_aOriginalName;
+ pContent = new ::ucbhelper::Content( aName, Reference< ::com::sun::star::ucb::XCommandEnvironment > () );
+ }
+
+ if ( pElement->m_bIsRemoved )
+ {
+ // was it inserted, then removed (so there would be nothing to do!)
+ if ( !pElement->m_bIsInserted )
+ {
+ // first remove all open stream handles
+ if( !pElement->m_xStream.Is() || pElement->m_xStream->Clear() )
+ {
+ pContent->executeCommand( ::rtl::OUString::createFromAscii("delete"), makeAny( sal_Bool( sal_True ) ) );
+ nRet = COMMIT_RESULT_SUCCESS;
+ }
+ else
+ // couldn't release stream because there are external references to it
+ nRet = COMMIT_RESULT_FAILURE;
+ }
+ }
+ else
+ {
+ sal_Int16 nLocalRet = COMMIT_RESULT_NOTHING_TO_DO;
+ if ( pElement->m_xStorage.Is() )
+ {
+ // element is a storage
+ // do a commit in the following cases:
+ // - if storage is already inserted, and changed
+ // - storage is not in a package
+ // - it's a new storage, try to insert and commit if successful inserted
+ if ( !pElement->m_bIsInserted || m_bIsLinked || pElement->m_xStorage->Insert( m_pContent ) )
+ {
+ nLocalRet = pElement->m_xStorage->Commit();
+ pContent = pElement->GetContent();
+ }
+ }
+ else if ( pElement->m_xStream.Is() )
+ {
+ // element is a stream
+ nLocalRet = pElement->m_xStream->Commit();
+ if ( pElement->m_xStream->m_bIsOLEStorage )
+ {
+ // OLE storage should be stored encrytped, if the storage uses encryption
+ pElement->m_xStream->m_aContentType = String::CreateFromAscii("application/vnd.sun.star.oleobject");
+ Any aValue;
+ aValue <<= (BOOL) TRUE;
+ pElement->m_xStream->m_pContent->setPropertyValue(String::CreateFromAscii("Encrypted"), aValue );
+ }
+
+ pContent = pElement->GetContent();
+ }
+
+ if ( pElement->m_aName != pElement->m_aOriginalName )
+ {
+ // name ( title ) of the element was changed
+ nLocalRet = COMMIT_RESULT_SUCCESS;
+ Any aAny;
+ aAny <<= (rtl::OUString) pElement->m_aName;
+ pContent->setPropertyValue( ::rtl::OUString::createFromAscii("Title"), aAny );
+ }
+
+ if ( pElement->IsLoaded() && pElement->GetContentType() != pElement->GetOriginalContentType() )
+ {
+ // mediatype of the element was changed
+ nLocalRet = COMMIT_RESULT_SUCCESS;
+ Any aAny;
+ aAny <<= (rtl::OUString) pElement->GetContentType();
+ pContent->setPropertyValue( ::rtl::OUString::createFromAscii("MediaType"), aAny );
+ }
+
+ if ( nLocalRet != COMMIT_RESULT_NOTHING_TO_DO )
+ nRet = nLocalRet;
+ }
+
+ if ( bDeleteContent )
+ // content was created inside the loop
+ delete pContent;
+
+ if ( nRet == COMMIT_RESULT_FAILURE )
+ break;
+
+ pElement = m_aChildrenList.Next();
+ }
+ }
+ catch ( ContentCreationException& )
+ {
+ // content could not be created
+ SetError( ERRCODE_IO_NOTEXISTS );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( CommandAbortedException& )
+ {
+ // any command wasn't executed successfully - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( RuntimeException& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( Exception& )
+ {
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+
+ if ( m_bIsRoot && m_pContent )
+ {
+ // the root storage must flush the root package content
+ if ( nRet == COMMIT_RESULT_SUCCESS )
+ {
+ try
+ {
+ // commit the media type to the JAR file
+ // clipboard format and ClassId will be retrieved from the media type when the file is loaded again
+ Any aType;
+ aType <<= (rtl::OUString) m_aContentType;
+ m_pContent->setPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ), aType );
+
+ if ( m_bIsLinked )
+ {
+ // write a manifest file
+ // first create a subfolder "META-inf"
+ Content aNewSubFolder;
+ BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, String::CreateFromAscii("META-INF"), aNewSubFolder );
+ if ( bRet )
+ {
+ // create a stream to write the manifest file - use a temp file
+ String aURL( aNewSubFolder.getURL() );
+ ::utl::TempFile* pTempFile = new ::utl::TempFile( &aURL );
+
+ // get the stream from the temp file and create an output stream wrapper
+ SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE );
+ ::utl::OOutputStreamWrapper* pHelper = new ::utl::OOutputStreamWrapper( *pStream );
+ com::sun::star::uno::Reference < ::com::sun::star::io::XOutputStream > xOutputStream( pHelper );
+
+ // create a manifest writer object that will fill the stream
+ Reference < ::com::sun::star::packages::manifest::XManifestWriter > xWriter =
+ Reference< ::com::sun::star::packages::manifest::XManifestWriter >
+ ( ::comphelper::getProcessServiceFactory()->createInstance(
+ ::rtl::OUString::createFromAscii( "com.sun.star.packages.manifest.ManifestWriter" )), UNO_QUERY) ;
+ sal_Int32 nCount = GetObjectCount() + 1;
+ Sequence < Sequence < PropertyValue > > aProps( nCount );
+ sal_Int32 nProps = 0;
+ GetProps( nProps, aProps, String() );
+ xWriter->writeManifestSequence( xOutputStream, aProps );
+
+ // move the stream to its desired location
+ Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >() );
+ xWriter = NULL;
+ xOutputStream = NULL;
+ DELETEZ( pTempFile );
+ aNewSubFolder.transferContent( aSource, InsertOperation_MOVE, ::rtl::OUString::createFromAscii("manifest.xml"), NameClash::OVERWRITE );
+ }
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf ( stderr, "Files: %i\n", nOpenFiles );
+ fprintf ( stderr, "Streams: %i\n", nOpenStreams );
+#endif
+ // force writing
+ Any aAny;
+ m_pContent->executeCommand( ::rtl::OUString::createFromAscii("flush"), aAny );
+ if ( m_pSource != 0 )
+ {
+ SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), STREAM_STD_READ );
+ m_pSource->SetStreamSize(0);
+ // m_pSource->Seek(0);
+ *pStream >> *m_pSource;
+ DELETEZ( pStream );
+ m_pSource->Seek(0);
+ }
+ }
+ }
+ catch ( CommandAbortedException& )
+ {
+ // how to tell the content : forget all changes ?!
+ // or should we assume that the content does it by itself because he throwed an exception ?!
+ // any command wasn't executed successfully - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( RuntimeException& )
+ {
+ // how to tell the content : forget all changes ?!
+ // or should we assume that the content does it by itself because he throwed an exception ?!
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( InteractiveIOException& r )
+ {
+ if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION )
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ else if ( r.Code == IOErrorCode_NOT_EXISTING )
+ SetError( ERRCODE_IO_NOTEXISTS );
+ else if ( r.Code == IOErrorCode_CANT_READ )
+ SetError( ERRCODE_IO_CANTREAD );
+ else if ( r.Code == IOErrorCode_CANT_WRITE )
+ SetError( ERRCODE_IO_CANTWRITE );
+ else
+ SetError( ERRCODE_IO_GENERAL );
+
+ return COMMIT_RESULT_FAILURE;
+ }
+ catch ( Exception& )
+ {
+ // how to tell the content : forget all changes ?!
+ // or should we assume that the content does it by itself because he throwed an exception ?!
+ // any other error - not specified
+ SetError( ERRCODE_IO_GENERAL );
+ return COMMIT_RESULT_FAILURE;
+ }
+ }
+ else if ( nRet != COMMIT_RESULT_NOTHING_TO_DO )
+ {
+ // how to tell the content : forget all changes ?! Should we ?!
+ SetError( ERRCODE_IO_GENERAL );
+ return nRet;
+ }
+
+ // after successfull root commit all elements names and types are adjusted and all removed elements
+ // are also removed from the lists
+ UCBStorageElement_Impl* pInnerElement = m_aChildrenList.First();
+ BOOL bRet = TRUE;
+ while ( pInnerElement && bRet )
+ {
+ UCBStorageElement_Impl* pNext = m_aChildrenList.Next();
+ if ( pInnerElement->m_bIsRemoved )
+ {
+ // is this correct use of our list class ?!
+ m_aChildrenList.Remove( pInnerElement );
+ }
+ else
+ {
+ pInnerElement->m_aOriginalName = pInnerElement->m_aName;
+ pInnerElement->m_bIsInserted = FALSE;
+ }
+
+ pInnerElement = pNext;
+ }
+ }
+
+ m_bCommited = FALSE;
+ }
+
+ return nRet;
+}
+
+BOOL UCBStorage_Impl::Revert()
+{
+ UCBStorageElement_Impl* pElement = m_aChildrenList.First();
+ BOOL bRet = TRUE;
+ while ( pElement && bRet )
+ {
+ pElement->m_bIsRemoved = FALSE;
+ if ( pElement->m_bIsInserted )
+ {
+ m_aChildrenList.Remove( pElement ); // correct usage of list ???
+ }
+ else
+ {
+ if ( pElement->m_xStream.Is() )
+ {
+ pElement->m_xStream->m_bCommited = sal_False;
+ pElement->m_xStream->Revert();
+ }
+ else if ( pElement->m_xStorage.Is() )
+ {
+ pElement->m_xStorage->m_bCommited = sal_False;
+ pElement->m_xStorage->Revert();
+ }
+
+ pElement->m_aName = pElement->m_aOriginalName;
+ pElement->m_bIsRemoved = FALSE;
+ }
+
+ pElement = m_aChildrenList.Next();
+ }
+
+ return bRet;
+}
+
+const String& UCBStorage::GetName() const
+{
+ return pImp->m_aName; // pImp->m_aURL ?!
+}
+
+BOOL UCBStorage::IsRoot() const
+{
+ return pImp->m_bIsRoot;
+}
+
+void UCBStorage::SetDirty()
+{
+ pImp->m_bDirty = TRUE;
+}
+
+void UCBStorage::SetClass( const SvGlobalName & rClass, ULONG nOriginalClipFormat, const String & rUserTypeName )
+{
+ pImp->m_aClassId = rClass;
+ pImp->m_nFormat = nOriginalClipFormat;
+ pImp->m_aUserTypeName = rUserTypeName;
+
+ // in UCB storages only the content type will be stored, all other information can be reconstructed
+ // ( see the UCBStorage_Impl::Init() method )
+ ::com::sun::star::datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor );
+ pImp->m_aContentType = aDataFlavor.MimeType;
+}
+
+void UCBStorage::SetClassId( const ClsId& rClsId )
+{
+ pImp->m_aClassId = SvGlobalName( (const CLSID&) rClsId );
+ if ( pImp->m_aClassId == SvGlobalName() )
+ return;
+
+ // in OLE storages the clipboard format an the user name will be transferred when a storage is copied because both are
+ // stored in one the substreams
+ // UCB storages store the content type information as content type in the manifest file and so this information must be
+ // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from
+ // the content type
+ pImp->m_nFormat = GetFormatId_Impl( pImp->m_aClassId );
+ if ( pImp->m_nFormat )
+ {
+ ::com::sun::star::datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor );
+ pImp->m_aUserTypeName = aDataFlavor.HumanPresentableName;
+ pImp->m_aContentType = aDataFlavor.MimeType;
+ }
+}
+
+const ClsId& UCBStorage::GetClassId() const
+{
+ return ( const ClsId& ) pImp->m_aClassId.GetCLSID();
+}
+
+void UCBStorage::SetConvertClass( const SvGlobalName & /*rConvertClass*/, ULONG /*nOriginalClipFormat*/, const String & /*rUserTypeName*/ )
+{
+ // ???
+}
+
+BOOL UCBStorage::ShouldConvert()
+{
+ // ???
+ return FALSE;
+}
+
+SvGlobalName UCBStorage::GetClassName()
+{
+ return pImp->m_aClassId;
+}
+
+ULONG UCBStorage::GetFormat()
+{
+ return pImp->m_nFormat;
+}
+
+String UCBStorage::GetUserName()
+{
+ DBG_ERROR("UserName is not implemented in UCB storages!" );
+ return pImp->m_aUserTypeName;
+}
+
+void UCBStorage::FillInfoList( SvStorageInfoList* pList ) const
+{
+ // put information in childrenlist into StorageInfoList
+ UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First();
+ while ( pElement )
+ {
+ if ( !pElement->m_bIsRemoved )
+ {
+ // problem: what about the size of a substorage ?!
+ ULONG nSize = pElement->m_nSize;
+ if ( pElement->m_xStream.Is() )
+ nSize = pElement->m_xStream->GetSize();
+ SvStorageInfo aInfo( pElement->m_aName, nSize, pElement->m_bIsStorage );
+ pList->Append( aInfo );
+ }
+
+ pElement = pImp->m_aChildrenList.Next();
+ }
+}
+
+BOOL UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl& rElement, BaseStorage* pDest, const String& rNew ) const
+{
+ // insert stream or storage into the list or stream of the destination storage
+ // not into the content, this will be done on commit !
+ // be aware of name changes !
+ if ( !rElement.m_bIsStorage )
+ {
+ // copy the streams data
+ // the destination stream must not be open
+ BaseStorageStream* pOtherStream = pDest->OpenStream( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect );
+ BaseStorageStream* pStream = NULL;
+ BOOL bDeleteStream = FALSE;
+
+ // if stream is already open, it is allowed to copy it, so be aware of this
+ if ( rElement.m_xStream.Is() )
+ pStream = rElement.m_xStream->m_pAntiImpl;
+ if ( !pStream )
+ {
+ pStream = ( const_cast < UCBStorage* > (this) )->OpenStream( rElement.m_aName, STREAM_STD_READ, pImp->m_bDirect );
+ bDeleteStream = TRUE;
+ }
+
+ pStream->CopyTo( pOtherStream );
+ SetError( pStream->GetError() );
+ if( pOtherStream->GetError() )
+ pDest->SetError( pOtherStream->GetError() );
+ else
+ pOtherStream->Commit();
+
+ if ( bDeleteStream )
+ delete pStream;
+ delete pOtherStream;
+ }
+ else
+ {
+ // copy the storage content
+ // the destination storage must not be open
+ BaseStorage* pStorage = NULL;
+
+ // if stream is already open, it is allowed to copy it, so be aware of this
+ BOOL bDeleteStorage = FALSE;
+ if ( rElement.m_xStorage.Is() )
+ pStorage = rElement.m_xStorage->m_pAntiImpl;
+ if ( !pStorage )
+ {
+ pStorage = ( const_cast < UCBStorage* > (this) )->OpenStorage( rElement.m_aName, pImp->m_nMode, pImp->m_bDirect );
+ bDeleteStorage = TRUE;
+ }
+
+ UCBStorage* pUCBDest = PTR_CAST( UCBStorage, pDest );
+ UCBStorage* pUCBCopy = PTR_CAST( UCBStorage, pStorage );
+
+ BOOL bOpenUCBStorage = pUCBDest && pUCBCopy;
+ BaseStorage* pOtherStorage = bOpenUCBStorage ?
+ pDest->OpenUCBStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ) :
+ pDest->OpenOLEStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect );
+
+ // For UCB storages, the class id and the format id may differ,
+ // do passing the class id is not sufficient.
+ if( bOpenUCBStorage )
+ pOtherStorage->SetClass( pStorage->GetClassName(),
+ pStorage->GetFormat(),
+ pUCBCopy->pImp->m_aUserTypeName );
+ else
+ pOtherStorage->SetClassId( pStorage->GetClassId() );
+ pStorage->CopyTo( pOtherStorage );
+ SetError( pStorage->GetError() );
+ if( pOtherStorage->GetError() )
+ pDest->SetError( pOtherStorage->GetError() );
+ else
+ pOtherStorage->Commit();
+
+ if ( bDeleteStorage )
+ delete pStorage;
+ delete pOtherStorage;
+ }
+
+ return BOOL( Good() && pDest->Good() );
+}
+
+UCBStorageElement_Impl* UCBStorage::FindElement_Impl( const String& rName ) const
+{
+ DBG_ASSERT( rName.Len(), "Name is empty!" );
+ UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First();
+ while ( pElement )
+ {
+ if ( pElement->m_aName == rName && !pElement->m_bIsRemoved )
+ break;
+ pElement = pImp->m_aChildrenList.Next();
+ }
+
+ return pElement;
+}
+
+BOOL UCBStorage::CopyTo( BaseStorage* pDestStg ) const
+{
+ DBG_ASSERT( pDestStg != ((BaseStorage*)this), "Self-Copying is not possible!" );
+ if ( pDestStg == ((BaseStorage*)this) )
+ return FALSE;
+
+ // perhaps it's also a problem if one storage is a parent of the other ?!
+ // or if not: could be optimized ?!
+
+ // For UCB storages, the class id and the format id may differ,
+ // do passing the class id is not sufficient.
+ if( pDestStg->ISA( UCBStorage ) )
+ pDestStg->SetClass( pImp->m_aClassId, pImp->m_nFormat,
+ pImp->m_aUserTypeName );
+ else
+ pDestStg->SetClassId( GetClassId() );
+ pDestStg->SetDirty();
+
+ BOOL bRet = TRUE;
+ UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First();
+ while ( pElement && bRet )
+ {
+ if ( !pElement->m_bIsRemoved )
+ bRet = CopyStorageElement_Impl( *pElement, pDestStg, pElement->m_aName );
+ pElement = pImp->m_aChildrenList.Next();
+ }
+
+ if( !bRet )
+ SetError( pDestStg->GetError() );
+ return BOOL( Good() && pDestStg->Good() );
+}
+
+BOOL UCBStorage::CopyTo( const String& rElemName, BaseStorage* pDest, const String& rNew )
+{
+ if( !rElemName.Len() )
+ return FALSE;
+
+ if ( pDest == ((BaseStorage*) this) )
+ {
+ // can't double an element
+ return FALSE;
+ }
+ else
+ {
+ // for copying no optimization is usefull, because in every case the stream data must be copied
+ UCBStorageElement_Impl* pElement = FindElement_Impl( rElemName );
+ if ( pElement )
+ return CopyStorageElement_Impl( *pElement, pDest, rNew );
+ else
+ {
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+ return FALSE;
+ }
+ }
+}
+
+BOOL UCBStorage::Commit()
+{
+ // mark this storage for sending it on root commit
+ pImp->m_bCommited = TRUE;
+ if ( pImp->m_bIsRoot )
+ // the root storage coordinates commiting by sending a Commit command to its content
+ return ( pImp->Commit() != COMMIT_RESULT_FAILURE );
+ else
+ return TRUE;
+}
+
+BOOL UCBStorage::Revert()
+{
+ return pImp->Revert();
+}
+
+BaseStorageStream* UCBStorage::OpenStream( const String& rEleName, StreamMode nMode, BOOL bDirect, const ByteString* pKey )
+{
+ if( !rEleName.Len() )
+ return NULL;
+
+ // try to find the storage element
+ UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ if ( !pElement )
+ {
+ // element does not exist, check if creation is allowed
+ if( ( nMode & STREAM_NOCREATE ) )
+ {
+ SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
+ String aName( pImp->m_aURL );
+ aName += '/';
+ aName += rEleName;
+ UCBStorageStream* pStream = new UCBStorageStream( aName, nMode, bDirect, pKey, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
+ pStream->SetError( GetError() );
+ pStream->pImp->m_aName = rEleName;
+ return pStream;
+ }
+ else
+ {
+ // create a new UCBStorageElement and insert it into the list
+ pElement = new UCBStorageElement_Impl( rEleName );
+ pElement->m_bIsInserted = TRUE;
+ pImp->m_aChildrenList.Insert( pElement, LIST_APPEND );
+ }
+ }
+
+ if ( pElement && !pElement->m_bIsFolder )
+ {
+ // check if stream is already created
+ if ( pElement->m_xStream.Is() )
+ {
+ // stream has already been created; if it has no external reference, it may be opened another time
+ if ( pElement->m_xStream->m_pAntiImpl )
+ {
+ DBG_ERROR("Stream is already open!" );
+ SetError( SVSTREAM_ACCESS_DENIED ); // ???
+ return NULL;
+ }
+ else
+ {
+ // check if stream is opened with the same keyword as before
+ // if not, generate a new stream because it could be encrypted vs. decrypted!
+ ByteString aKey;
+ if ( pKey )
+ aKey = *pKey;
+ if ( pElement->m_xStream->m_aKey == aKey )
+ {
+ pElement->m_xStream->PrepareCachedForReopen( nMode );
+
+ // DBG_ASSERT( bDirect == pElement->m_xStream->m_bDirect, "Wrong DirectMode!" );
+ return new UCBStorageStream( pElement->m_xStream );
+ }
+ }
+ }
+
+ // stream is opened the first time
+ pImp->OpenStream( pElement, nMode, bDirect, pKey );
+
+ // if name has been changed before creating the stream: set name!
+ pElement->m_xStream->m_aName = rEleName;
+ return new UCBStorageStream( pElement->m_xStream );
+ }
+
+ return NULL;
+}
+
+UCBStorageStream_Impl* UCBStorage_Impl::OpenStream( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect, const ByteString* pKey )
+{
+ String aName( m_aURL );
+ aName += '/';
+ aName += pElement->m_aOriginalName;
+ pElement->m_xStream = new UCBStorageStream_Impl( aName, nMode, NULL, bDirect, pKey, m_bRepairPackage, m_xProgressHandler );
+ return pElement->m_xStream;
+}
+
+BaseStorage* UCBStorage::OpenUCBStorage( const String& rEleName, StreamMode nMode, BOOL bDirect )
+{
+ if( !rEleName.Len() )
+ return NULL;
+
+ return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE );
+}
+
+BaseStorage* UCBStorage::OpenOLEStorage( const String& rEleName, StreamMode nMode, BOOL bDirect )
+{
+ if( !rEleName.Len() )
+ return NULL;
+
+ return OpenStorage_Impl( rEleName, nMode, bDirect, FALSE );
+}
+
+BaseStorage* UCBStorage::OpenStorage( const String& rEleName, StreamMode nMode, BOOL bDirect )
+{
+ if( !rEleName.Len() )
+ return NULL;
+
+ return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE );
+}
+
+BaseStorage* UCBStorage::OpenStorage_Impl( const String& rEleName, StreamMode nMode, BOOL bDirect, BOOL bForceUCBStorage )
+{
+ // try to find the storage element
+ UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ if ( !pElement )
+ {
+ // element does not exist, check if creation is allowed
+ if( ( nMode & STREAM_NOCREATE ) )
+ {
+ SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
+ String aName( pImp->m_aURL );
+ aName += '/';
+ aName += rEleName; // ???
+ UCBStorage *pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
+ pStorage->pImp->m_bIsRoot = FALSE;
+ pStorage->pImp->m_bListCreated = sal_True; // the storage is pretty new, nothing to read
+ pStorage->SetError( GetError() );
+ return pStorage;
+ }
+
+ // create a new UCBStorageElement and insert it into the list
+ // problem: perhaps an OLEStorage should be created ?!
+ // Because nothing is known about the element that should be created, an external parameter is needed !
+ pElement = new UCBStorageElement_Impl( rEleName );
+ pElement->m_bIsInserted = TRUE;
+ pImp->m_aChildrenList.Insert( pElement, LIST_APPEND );
+ }
+
+ if ( !pElement->m_bIsFolder && ( pElement->m_bIsStorage || !bForceUCBStorage ) )
+ {
+ // create OLE storages on a stream ( see ctor of SotStorage )
+ // Such a storage will be created on a UCBStorageStream; it will write into the stream
+ // if it is opened in direct mode or when it is committed. In this case the stream will be
+ // modified and then it MUST be treated as commited.
+ if ( !pElement->m_xStream.Is() )
+ {
+ BaseStorageStream* pStr = OpenStream( rEleName, nMode, bDirect );
+ UCBStorageStream* pStream = PTR_CAST( UCBStorageStream, pStr );
+ if ( !pStream )
+ {
+ SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
+ return NULL;
+ }
+
+ pElement->m_xStream = pStream->pImp;
+ delete pStream;
+ }
+
+ pElement->m_xStream->PrepareCachedForReopen( nMode );
+ pElement->m_xStream->Init();
+
+ pElement->m_bIsStorage = TRUE;
+ return pElement->m_xStream->CreateStorage(); // can only be created in transacted mode
+ }
+ else if ( pElement->m_xStorage.Is() )
+ {
+ // storage has already been opened; if it has no external reference, it may be opened another time
+ if ( pElement->m_xStorage->m_pAntiImpl )
+ {
+ DBG_ERROR("Storage is already open!" );
+ SetError( SVSTREAM_ACCESS_DENIED ); // ???
+ }
+ else
+ {
+ BOOL bIsWritable = (( pElement->m_xStorage->m_nMode & STREAM_WRITE ) != 0);
+ if ( !bIsWritable && (( nMode & STREAM_WRITE ) != 0 ))
+ {
+ String aName( pImp->m_aURL );
+ aName += '/';
+ aName += pElement->m_aOriginalName;
+ UCBStorage* pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
+ pElement->m_xStorage = pStorage->pImp;
+ return pStorage;
+ }
+ else
+ {
+// DBG_ASSERT( bDirect == pElement->m_xStorage->m_bDirect, "Wrong DirectMode!" );
+ return new UCBStorage( pElement->m_xStorage );
+ }
+ }
+ }
+ else if ( !pElement->m_xStream.Is() )
+ {
+ // storage is opened the first time
+ BOOL bIsWritable = (( pImp->m_nMode & STREAM_WRITE ) != 0 );
+ if ( pImp->m_bIsLinked && pImp->m_bIsRoot && bIsWritable )
+ {
+ // make sure that the root storage object has been created before substorages will be created
+ INetURLObject aFolderObj( pImp->m_aURL );
+ String aName = aFolderObj.GetName();
+ aFolderObj.removeSegment();
+
+ Content aFolder( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ), Reference < XCommandEnvironment >() );
+ pImp->m_pContent = new Content;
+ BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, pImp->m_aName, *pImp->m_pContent );
+ if ( !bRet )
+ {
+ SetError( SVSTREAM_CANNOT_MAKE );
+ return NULL;
+ }
+ }
+
+ UCBStorage_Impl* pStor = pImp->OpenStorage( pElement, nMode, bDirect );
+ if ( pStor )
+ {
+ if ( pElement->m_bIsInserted )
+ pStor->m_bListCreated = sal_True; // the storage is pretty new, nothing to read
+
+ return new UCBStorage( pStor );
+ }
+ }
+
+ return NULL;
+}
+
+UCBStorage_Impl* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect )
+{
+ UCBStorage_Impl* pRet = NULL;
+ String aName( m_aURL );
+ aName += '/';
+ aName += pElement->m_aOriginalName; // ???
+
+ pElement->m_bIsStorage = pElement->m_bIsFolder = TRUE;
+
+ if ( m_bIsLinked && !::utl::UCBContentHelper::Exists( aName ) )
+ {
+ Content aNewFolder;
+ BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, pElement->m_aOriginalName, aNewFolder );
+ if ( bRet )
+ pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler );
+ }
+ else
+ {
+ pRet = new UCBStorage_Impl( aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler );
+ }
+
+ if ( pRet )
+ {
+ pRet->m_bIsLinked = m_bIsLinked;
+ pRet->m_bIsRoot = FALSE;
+
+ // if name has been changed before creating the stream: set name!
+ pRet->m_aName = pElement->m_aOriginalName;
+ pElement->m_xStorage = pRet;
+ }
+
+ if ( pRet )
+ pRet->Init();
+
+ return pRet;
+}
+
+BOOL UCBStorage::IsStorage( const String& rEleName ) const
+{
+ if( !rEleName.Len() )
+ return FALSE;
+
+ const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ return ( pElement && pElement->m_bIsStorage );
+}
+
+BOOL UCBStorage::IsStream( const String& rEleName ) const
+{
+ if( !rEleName.Len() )
+ return FALSE;
+
+ const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ return ( pElement && !pElement->m_bIsStorage );
+}
+
+BOOL UCBStorage::IsContained( const String & rEleName ) const
+{
+ if( !rEleName.Len() )
+ return FALSE;
+ const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ return ( pElement != NULL );
+}
+
+BOOL UCBStorage::Remove( const String& rEleName )
+{
+ if( !rEleName.Len() )
+ return FALSE;
+
+ UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ if ( pElement )
+ {
+ pElement->m_bIsRemoved = TRUE;
+ }
+ else
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+
+ return ( pElement != NULL );
+}
+
+BOOL UCBStorage::Rename( const String& rEleName, const String& rNewName )
+{
+ if( !rEleName.Len()|| !rNewName.Len() )
+ return FALSE;
+
+ UCBStorageElement_Impl *pAlreadyExisting = FindElement_Impl( rNewName );
+ if ( pAlreadyExisting )
+ {
+ SetError( SVSTREAM_ACCESS_DENIED );
+ return FALSE; // can't change to a name that is already used
+ }
+
+ UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
+ if ( pElement )
+ {
+ pElement->m_aName = rNewName;
+ }
+ else
+ SetError( SVSTREAM_FILE_NOT_FOUND );
+
+ return pElement != NULL;
+}
+
+BOOL UCBStorage::MoveTo( const String& rEleName, BaseStorage* pNewSt, const String& rNewName )
+{
+ if( !rEleName.Len() || !rNewName.Len() )
+ return FALSE;
+
+ if ( pNewSt == ((BaseStorage*) this) && !FindElement_Impl( rNewName ) )
+ {
+ return Rename( rEleName, rNewName );
+ }
+ else
+ {
+/*
+ if ( PTR_CAST( UCBStorage, pNewSt ) )
+ {
+ // because the element is moved, not copied, a special optimization is possible :
+ // first copy the UCBStorageElement; flag old element as "Removed" and new as "Inserted",
+ // clear original name/type of the new element
+ // if moved element is open: copy content, but change absolute URL ( and those of all children of the element! ),
+ // clear original name/type of new content, keep the old original stream/storage, but forget its working streams,
+ // close original UCBContent and original stream, only the TempFile and its stream may remain unchanged, but now
+ // belong to the new content
+ // if original and editable stream are identical ( readonly element ), it has to be copied to the editable
+ // stream of the destination object
+ // Not implemented at the moment ( risky?! ), perhaps later
+ }
+*/
+ // MoveTo is done by first copying to the new destination and then removing the old element
+ BOOL bRet = CopyTo( rEleName, pNewSt, rNewName );
+ if ( bRet )
+ bRet = Remove( rEleName );
+ return bRet;
+ }
+}
+
+BOOL UCBStorage::ValidateFAT()
+{
+ // ???
+ return TRUE;
+}
+
+BOOL UCBStorage::Validate( BOOL bWrite ) const
+{
+ // ???
+ return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) );
+}
+
+BOOL UCBStorage::ValidateMode( StreamMode m ) const
+{
+ // ???
+ if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx
+ return TRUE;
+ USHORT nCurMode = 0xFFFF;
+ if( ( m & 3 ) == STREAM_READ )
+ {
+ // only SHARE_DENYWRITE or SHARE_DENYALL allowed
+ if( ( ( m & STREAM_SHARE_DENYWRITE )
+ && ( nCurMode & STREAM_SHARE_DENYWRITE ) )
+ || ( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) ) )
+ return TRUE;
+ }
+ else
+ {
+ // only SHARE_DENYALL allowed
+ // storages open in r/o mode are OK, since only
+ // the commit may fail
+ if( ( m & STREAM_SHARE_DENYALL )
+ && ( nCurMode & STREAM_SHARE_DENYALL ) )
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+const SvStream* UCBStorage::GetSvStream() const
+{
+ // this would cause a complete download of the file
+ // as it looks, this method is NOT used inside of SOT, only exported by class SotStorage - but for what ???
+ return pImp->m_pSource;
+}
+
+BOOL UCBStorage::Equals( const BaseStorage& rStorage ) const
+{
+ // ???
+ return ((BaseStorage*)this) == &rStorage;
+}
+
+BOOL UCBStorage::IsStorageFile( const String& rFileName )
+{
+ String aFileURL = rFileName;
+ INetURLObject aObj( aFileURL );
+ if ( aObj.GetProtocol() == INET_PROT_NOT_VALID )
+ {
+ ::utl::LocalFileHelper::ConvertPhysicalNameToURL( rFileName, aFileURL );
+ aObj.SetURL( aFileURL );
+ aFileURL = aObj.GetMainURL( INetURLObject::NO_DECODE );
+ }
+
+ SvStream * pStm = ::utl::UcbStreamHelper::CreateStream( aFileURL, STREAM_STD_READ );
+ BOOL bRet = UCBStorage::IsStorageFile( pStm );
+ delete pStm;
+ return bRet;
+}
+
+BOOL UCBStorage::IsStorageFile( SvStream* pFile )
+{
+ if ( !pFile )
+ return FALSE;
+
+ ULONG nPos = pFile->Tell();
+ pFile->Seek( STREAM_SEEK_TO_END );
+ if ( pFile->Tell() < 4 )
+ return FALSE;
+
+ pFile->Seek(0);
+ UINT32 nBytes;
+ *pFile >> nBytes;
+
+ // search for the magic bytes
+ BOOL bRet = ( nBytes == 0x04034b50 );
+ if ( !bRet )
+ {
+ // disk spanned file have an additional header in front of the usual one
+ bRet = ( nBytes == 0x08074b50 );
+ if ( bRet )
+ {
+ *pFile >> nBytes;
+ bRet = ( nBytes == 0x04034b50 );
+ }
+ }
+
+ pFile->Seek( nPos );
+ return bRet;
+}
+
+BOOL UCBStorage::IsDiskSpannedFile( SvStream* pFile )
+{
+ if ( !pFile )
+ return FALSE;
+
+ ULONG nPos = pFile->Tell();
+ pFile->Seek( STREAM_SEEK_TO_END );
+ if ( !pFile->Tell() )
+ return FALSE;
+
+ pFile->Seek(0);
+ UINT32 nBytes;
+ *pFile >> nBytes;
+
+ // disk spanned file have an additional header in front of the usual one
+ BOOL bRet = ( nBytes == 0x08074b50 );
+ if ( bRet )
+ {
+ *pFile >> nBytes;
+ bRet = ( nBytes == 0x04034b50 );
+ }
+
+ pFile->Seek( nPos );
+ return bRet;
+}
+
+String UCBStorage::GetLinkedFile( SvStream &rStream )
+{
+ String aString;
+ ULONG nPos = rStream.Tell();
+ rStream.Seek( STREAM_SEEK_TO_END );
+ if ( !rStream.Tell() )
+ return aString;
+
+ rStream.Seek(0);
+ UINT32 nBytes;
+ rStream >> nBytes;
+ if( nBytes == 0x04034b50 )
+ {
+ ByteString aTmp;
+ rStream.ReadByteString( aTmp );
+ if ( aTmp.CompareTo( "ContentURL=", 11 ) == COMPARE_EQUAL )
+ {
+ aTmp.Erase( 0, 11 );
+ aString = String( aTmp, RTL_TEXTENCODING_UTF8 );
+ }
+ }
+
+ rStream.Seek( nPos );
+ return aString;
+}
+
+String UCBStorage::CreateLinkFile( const String& rName )
+{
+ // create a stream to write the link file - use a temp file, because it may be no file content
+ INetURLObject aFolderObj( rName );
+ String aName = aFolderObj.GetName();
+ aFolderObj.removeSegment();
+ String aFolderURL( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) );
+ ::utl::TempFile* pTempFile = new ::utl::TempFile( &aFolderURL );
+
+ // get the stream from the temp file
+ SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE | STREAM_TRUNC );
+
+ // write header
+ *pStream << ( UINT32 ) 0x04034b50;
+
+ // assemble a new folder name in the destination folder
+ INetURLObject aObj( rName );
+ String aTmpName = aObj.GetName();
+ String aTitle = String::CreateFromAscii( "content." );
+ aTitle += aTmpName;
+
+ // create a folder and store its URL
+ Content aFolder( aFolderURL, Reference < XCommandEnvironment >() );
+ Content aNewFolder;
+ BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTitle, aNewFolder );
+ if ( !bRet )
+ {
+ aFolderObj.insertName( aTitle );
+ if ( ::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) )
+ {
+ // Hack, because already existing files give the same CommandAbortedException as any other error !
+ // append a number until the name can be used for a new folder
+ aTitle += '.';
+ for ( sal_Int32 i=0; !bRet; i++ )
+ {
+ String aTmp( aTitle );
+ aTmp += String::CreateFromInt32( i );
+ bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTmp, aNewFolder );
+ if ( bRet )
+ aTitle = aTmp;
+ else
+ {
+ aFolderObj.SetName( aTmp );
+ if ( !::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) )
+ // Hack, because already existing files give the same CommandAbortedException as any other error !
+ break;
+ }
+ }
+ }
+ }
+
+ if ( bRet )
+ {
+ // get the URL
+ aObj.SetName( aTitle );
+ String aURL = aObj.GetMainURL( INetURLObject::NO_DECODE );
+
+ // store it as key/value pair
+ String aLink = String::CreateFromAscii("ContentURL=");
+ aLink += aURL;
+ pStream->WriteByteString( aLink, RTL_TEXTENCODING_UTF8 );
+ pStream->Flush();
+
+ // move the stream to its desired location
+ Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >() );
+ DELETEZ( pTempFile );
+ aFolder.transferContent( aSource, InsertOperation_MOVE, aName, NameClash::OVERWRITE );
+ return aURL;
+ }
+
+ pTempFile->EnableKillingFile( TRUE );
+ delete pTempFile;
+ return String();
+}
+
+BOOL UCBStorage::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue )
+{
+ if ( rName.CompareToAscii("Title") == COMPARE_EQUAL )
+ return FALSE;
+
+ if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL )
+ {
+ ::rtl::OUString aTmp;
+ rValue >>= aTmp;
+ pImp->m_aContentType = aTmp;
+ }
+
+ try
+ {
+ if ( pImp->GetContent() )
+ {
+ pImp->m_pContent->setPropertyValue( rName, rValue );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+
+ return FALSE;
+}
+
+BOOL UCBStorage::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ try
+ {
+ if ( pImp->GetContent() )
+ {
+ rValue = pImp->m_pContent->getPropertyValue( rName );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+
+ return FALSE;
+}
+
+BOOL UCBStorage::GetProperty( const String& rEleName, const String& rName, ::com::sun::star::uno::Any& rValue )
+{
+ UCBStorageElement_Impl *pEle = FindElement_Impl( rEleName );
+ if ( !pEle )
+ return FALSE;
+
+ if ( !pEle->m_bIsFolder )
+ {
+ if ( !pEle->m_xStream.Is() )
+ pImp->OpenStream( pEle, pImp->m_nMode, pImp->m_bDirect );
+ if ( pEle->m_xStream->m_nError )
+ {
+ pEle->m_xStream.Clear();
+ return FALSE;
+ }
+
+ try
+ {
+ if ( pEle->m_xStream->m_pContent )
+ {
+ rValue = pEle->m_xStream->m_pContent->getPropertyValue( rName );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+ else
+ {
+ if ( !pEle->m_xStorage.Is() )
+ pImp->OpenStorage( pEle, pImp->m_nMode, pImp->m_bDirect );
+ if ( pEle->m_xStorage->m_nError )
+ {
+ pEle->m_xStorage.Clear();
+ return FALSE;
+ }
+
+ try
+ {
+ if ( pEle->m_xStorage->GetContent() )
+ {
+ rValue = pEle->m_xStorage->m_pContent->getPropertyValue( rName );
+ return TRUE;
+ }
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+
+ return FALSE;
+}
+
+UNOStorageHolderList* UCBStorage::GetUNOStorageHolderList()
+{
+ if ( !pImp->m_pUNOStorageHolderList )
+ pImp->m_pUNOStorageHolderList = new UNOStorageHolderList;
+
+ return pImp->m_pUNOStorageHolderList;
+}
+
diff --git a/sot/source/sdstor/unostorageholder.cxx b/sot/source/sdstor/unostorageholder.cxx
new file mode 100644
index 000000000000..55c205557648
--- /dev/null
+++ b/sot/source/sdstor/unostorageholder.cxx
@@ -0,0 +1,197 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/embed/XTransactionBroadcaster.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+#include "unostorageholder.hxx"
+#include <storinfo.hxx>
+
+
+using namespace ::com::sun::star;
+
+UNOStorageHolder::UNOStorageHolder( SotStorage& aParentStorage,
+ SotStorage& aStorage,
+ uno::Reference< embed::XStorage > xStorage,
+ ::utl::TempFile* pTempFile )
+: m_pParentStorage( &aParentStorage )
+, m_rSotStorage( &aStorage )
+, m_xStorage( xStorage )
+, m_pTempFile( pTempFile )
+{
+ OSL_ENSURE( m_xStorage.is() && m_pTempFile, "Wrong initialization!\n" );
+ if ( !m_xStorage.is() || !m_pTempFile )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XTransactionBroadcaster > xTrBroadcast( m_xStorage, uno::UNO_QUERY );
+ if ( !xTrBroadcast.is() )
+ throw uno::RuntimeException();
+
+ xTrBroadcast->addTransactionListener( (embed::XTransactionListener*)this );
+}
+
+void UNOStorageHolder::InternalDispose()
+{
+ uno::Reference< embed::XTransactionBroadcaster > xTrBroadcast( m_xStorage, uno::UNO_QUERY );
+ if ( xTrBroadcast.is() )
+ xTrBroadcast->removeTransactionListener( (embed::XTransactionListener*)this );
+
+ uno::Reference< lang::XComponent > xComponent( m_xStorage, uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ m_xStorage = uno::Reference< embed::XStorage >();
+
+ if ( m_pParentStorage )
+ m_pParentStorage = NULL;
+
+ if ( m_pTempFile )
+ {
+ delete m_pTempFile;
+ m_pTempFile = NULL;
+ }
+
+ if ( m_rSotStorage.Is() )
+ m_rSotStorage = NULL;
+}
+
+String UNOStorageHolder::GetStorageName()
+{
+ if ( m_rSotStorage.Is() )
+ return m_rSotStorage->GetName();
+
+ return String();
+}
+
+void SAL_CALL UNOStorageHolder::preCommit( const lang::EventObject& /*aEvent*/ )
+ throw ( uno::Exception,
+ uno::RuntimeException )
+{
+ // do nothing
+}
+
+void SAL_CALL UNOStorageHolder::commited( const lang::EventObject& /*aEvent*/ )
+ throw ( uno::RuntimeException )
+{
+ ::utl::TempFile aTmpStorFile;
+ if ( !aTmpStorFile.GetURL().Len() )
+ throw uno::RuntimeException();
+
+ uno::Reference< lang::XSingleServiceFactory > xStorageFactory(
+ ::comphelper::getProcessServiceFactory()->createInstance(
+ ::rtl::OUString::createFromAscii( "com.sun.star.embed.StorageFactory" ) ),
+ uno::UNO_QUERY );
+
+ OSL_ENSURE( xStorageFactory.is(), "Can't create storage factory!\n" );
+ if ( !xStorageFactory.is() )
+ throw uno::RuntimeException();
+
+ uno::Sequence< uno::Any > aArg( 2 );
+ aArg[0] <<= ::rtl::OUString( aTmpStorFile.GetURL() );
+ aArg[1] <<= embed::ElementModes::READWRITE;
+ uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArg ), uno::UNO_QUERY );
+
+ OSL_ENSURE( xTempStorage.is(), "Can't open storage!\n" );
+ if ( !xTempStorage.is() )
+ throw uno::RuntimeException();
+
+ m_xStorage->copyToStorage( xTempStorage );
+
+ uno::Reference< lang::XComponent > xComp( xTempStorage, uno::UNO_QUERY );
+ if ( !xComp.is() )
+ throw uno::RuntimeException();
+
+ xComp->dispose();
+
+ SotStorageRef rTempStorage = new SotStorage( TRUE, aTmpStorFile.GetURL(), STREAM_WRITE, STORAGE_TRANSACTED );
+ if ( !rTempStorage.Is() || rTempStorage->GetError() != ERRCODE_NONE )
+ throw uno::RuntimeException();
+
+ SvStorageInfoList aSubStorInfoList;
+ m_rSotStorage->FillInfoList( &aSubStorInfoList );
+ for ( sal_uInt32 nInd = 0; nInd < aSubStorInfoList.Count(); nInd++ )
+ {
+ m_rSotStorage->Remove( aSubStorInfoList[nInd].GetName() );
+ if ( m_rSotStorage->GetError() )
+ {
+ m_rSotStorage->ResetError();
+ throw uno::RuntimeException();
+ }
+ }
+
+ rTempStorage->CopyTo( m_rSotStorage );
+
+ // CopyTo does not transport unknown media type
+ // just workaround it
+ uno::Any aMediaType;
+ if ( rTempStorage->GetProperty( ::rtl::OUString::createFromAscii( "MediaType" ), aMediaType ) )
+ m_rSotStorage->SetProperty( ::rtl::OUString::createFromAscii( "MediaType" ), aMediaType );
+
+ m_rSotStorage->Commit();
+}
+
+void SAL_CALL UNOStorageHolder::preRevert( const lang::EventObject& /*aEvent*/ )
+ throw ( uno::Exception,
+ uno::RuntimeException )
+{
+ // do nothing
+}
+
+void SAL_CALL UNOStorageHolder::reverted( const lang::EventObject& /*aEvent*/ )
+ throw ( uno::RuntimeException )
+{
+ // do nothing, since reverting of the duplicate storage just means
+ // not to copy changes done for it to the original storage
+}
+
+void SAL_CALL UNOStorageHolder::disposing( const lang::EventObject& /*Source*/ )
+ throw ( uno::RuntimeException )
+{
+ if ( m_pTempFile )
+ {
+ delete m_pTempFile;
+ m_pTempFile = NULL;
+ }
+
+ if ( m_rSotStorage.Is() )
+ m_rSotStorage = NULL;
+
+ if ( m_pParentStorage )
+ {
+ SotStorage* pTmp = m_pParentStorage;
+ m_pParentStorage = NULL;
+ pTmp->RemoveUNOStorageHolder( this ); // this statement can lead to destruction of the holder
+ }
+}
+
+
diff --git a/sot/source/sdstor/unostorageholder.hxx b/sot/source/sdstor/unostorageholder.hxx
new file mode 100644
index 000000000000..5e70804745a8
--- /dev/null
+++ b/sot/source/sdstor/unostorageholder.hxx
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _UNOSTORAGEHOLDER_HXX
+#define _UNOSTORAGEHOLDER_HXX
+
+#include <com/sun/star/embed/XTransactionListener.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <unotools/tempfile.hxx>
+#include <sot/storage.hxx>
+
+class SotStorage;
+class UNOStorageHolder : public ::cppu::WeakImplHelper1<
+ ::com::sun::star::embed::XTransactionListener >
+
+{
+ SotStorage* m_pParentStorage; // parent storage
+ SotStorageRef m_rSotStorage; // original substorage
+ ::com::sun::star::uno::Reference< ::com::sun::star::embed::XStorage > m_xStorage; // duplicate storage
+ ::utl::TempFile* m_pTempFile; // temporary file used by duplicate storage
+
+public:
+ UNOStorageHolder( SotStorage& aParentStorage,
+ SotStorage& aStorage,
+ ::com::sun::star::uno::Reference< ::com::sun::star::embed::XStorage > xStorage,
+ ::utl::TempFile* pTempFile );
+
+ void InternalDispose();
+ String GetStorageName();
+
+ ::com::sun::star::uno::Reference< ::com::sun::star::embed::XStorage > GetDuplicateStorage() { return m_xStorage; }
+
+ virtual void SAL_CALL preCommit( const ::com::sun::star::lang::EventObject& aEvent )
+ throw ( ::com::sun::star::uno::Exception,
+ ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL commited( const ::com::sun::star::lang::EventObject& aEvent )
+ throw ( ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL preRevert( const ::com::sun::star::lang::EventObject& aEvent )
+ throw ( ::com::sun::star::uno::Exception,
+ ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL reverted( const ::com::sun::star::lang::EventObject& aEvent )
+ throw ( ::com::sun::star::uno::RuntimeException );
+
+ virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source )
+ throw ( ::com::sun::star::uno::RuntimeException );
+};
+
+#endif
+