diff options
Diffstat (limited to 'sot/source/sdstor')
-rw-r--r-- | sot/source/sdstor/stg.cxx | 1085 | ||||
-rw-r--r-- | sot/source/sdstor/stgavl.cxx | 422 | ||||
-rw-r--r-- | sot/source/sdstor/stgavl.hxx | 80 | ||||
-rw-r--r-- | sot/source/sdstor/stgcache.cxx | 547 | ||||
-rw-r--r-- | sot/source/sdstor/stgcache.hxx | 131 | ||||
-rw-r--r-- | sot/source/sdstor/stgdir.cxx | 1062 | ||||
-rw-r--r-- | sot/source/sdstor/stgdir.hxx | 135 | ||||
-rw-r--r-- | sot/source/sdstor/stgelem.cxx | 427 | ||||
-rw-r--r-- | sot/source/sdstor/stgelem.hxx | 167 | ||||
-rw-r--r-- | sot/source/sdstor/stgio.cxx | 391 | ||||
-rw-r--r-- | sot/source/sdstor/stgio.hxx | 83 | ||||
-rw-r--r-- | sot/source/sdstor/stgole.cxx | 204 | ||||
-rw-r--r-- | sot/source/sdstor/stgole.hxx | 80 | ||||
-rw-r--r-- | sot/source/sdstor/stgstrms.cxx | 1243 | ||||
-rw-r--r-- | sot/source/sdstor/stgstrms.hxx | 171 | ||||
-rw-r--r-- | sot/source/sdstor/storage.cxx | 1560 | ||||
-rw-r--r-- | sot/source/sdstor/storinfo.cxx | 113 | ||||
-rw-r--r-- | sot/source/sdstor/ucbstorage.cxx | 3539 | ||||
-rw-r--r-- | sot/source/sdstor/unostorageholder.cxx | 199 | ||||
-rw-r--r-- | sot/source/sdstor/unostorageholder.hxx | 79 |
20 files changed, 11718 insertions, 0 deletions
diff --git a/sot/source/sdstor/stg.cxx b/sot/source/sdstor/stg.cxx new file mode 100644 index 000000000000..c73028833790 --- /dev/null +++ b/sot/source/sdstor/stg.cxx @@ -0,0 +1,1085 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <sot/storinfo.hxx> +#include <osl/file.hxx> +#include <tools/tempfile.hxx> +#include <tools/ownlist.hxx> +#include <tools/string.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <tools/pstm.hxx> +#include <tools/debug.hxx> + +#include "sot/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( sal_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. + +sal_uLong StorageBase::GetError() const +{ + sal_uLong n = m_nError; + ((StorageBase*) this)->m_nError = SVSTREAM_OK; + return n; +} + +void StorageBase::SetError( sal_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 + +sal_Bool OLEStorageBase::Validate_Impl( sal_Bool bWrite ) const +{ + if( pEntry + && !pEntry->bInvalid + && ( !bWrite || !pEntry->bDirect || ( nStreamMode & STREAM_WRITE ) ) ) + return sal_True; + return sal_False; +} + +sal_Bool OLEStorageBase::ValidateMode_Impl( StreamMode m, StgDirEntry* p ) const +{ + if( m == INTERNAL_MODE ) + return sal_True; + sal_uInt16 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 sal_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 sal_True; + } + return sal_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(); +} + +sal_Bool StorageStream::Equals( const BaseStorageStream& rStream ) const +{ + const StorageStream* pOther = PTR_CAST( StorageStream, &rStream ); + return pOther && ( pOther->pEntry == pEntry ); +} + +sal_uLong StorageStream::Read( void* pData, sal_uLong nSize ) +{ + if( Validate() ) + { + pEntry->Seek( nPos ); + nSize = pEntry->Read( pData, (sal_Int32) nSize ); + pIo->MoveError( *this ); + nPos += nSize; + } + else + nSize = 0L; + return nSize; +} + +sal_uLong StorageStream::Write( const void* pData, sal_uLong nSize ) +{ + if( Validate( sal_True ) ) + { + pEntry->Seek( nPos ); + nSize = pEntry->Write( pData, (sal_Int32) nSize ); + pIo->MoveError( *this ); + nPos += nSize; + } + else + nSize = 0L; + return nSize; +} + +sal_uLong StorageStream::Seek( sal_uLong n ) +{ + if( Validate() ) + return nPos = pEntry->Seek( n ); + else + return n; +} + +void StorageStream::Flush() +{ + // Flushing means committing, since streams are never transacted + Commit(); +} + +sal_Bool StorageStream::SetSize( sal_uLong nNewSize ) +{ + if( Validate( sal_True ) ) + { + sal_Bool b = pEntry->SetSize( (sal_Int32) nNewSize ); + pIo->MoveError( *this ); + return b; + } + else + return sal_False; +} + +sal_Bool StorageStream::Commit() +{ + if( !Validate() ) + return sal_False; + if( !( m_nMode & STREAM_WRITE ) ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_False; + } + else + { + pEntry->Commit(); + pIo->MoveError( *this ); + return Good(); + } +} + +sal_Bool StorageStream::Revert() +{ + pEntry->Revert(); + pIo->MoveError( *this ); + return Good(); +} + +sal_Bool StorageStream::CopyTo( BaseStorageStream* pDest ) +{ + if( !Validate() || !pDest->Validate( sal_True ) || Equals( *pDest ) ) + return sal_False; + pEntry->Copy( *pDest ); + pDest->Commit(); + pIo->MoveError( *this ); + SetError( pDest->GetError() ); + return sal_Bool( Good() && pDest->Good() ); +} + +const SvStream* StorageStream::GetSvStream() const +{ + return GetSvStream_Impl(); +} + +sal_Bool StorageStream::Validate( sal_Bool bValidate ) const +{ + sal_Bool bRet = Validate_Impl( bValidate ); + if ( !bRet ) + SetError( SVSTREAM_ACCESS_DENIED ); + return bRet; +} + +sal_Bool StorageStream::ValidateMode( StreamMode nMode ) const +{ + sal_Bool bRet = ValidateMode_Impl( nMode, NULL ); + if ( !bRet ) + SetError( SVSTREAM_ACCESS_DENIED ); + return bRet; +} + +sal_Bool StorageStream::ValidateMode( StreamMode nMode, StgDirEntry* p ) const +{ + sal_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 = sal_Bool( rE.aEntry.GetType() == STG_STORAGE ); + bStream = sal_Bool( rE.aEntry.GetType() == STG_STREAM ); + nSize = bStorage ? 0 : rE.aEntry.GetSize(); +} + +/////////////////////////// class Storage //////////////////////////////// + +sal_Bool Storage::IsStorageFile( const String & rFileName ) +{ + StgIo aIo; + if( aIo.Open( rFileName, STREAM_STD_READ ) ) + return aIo.Load(); + return sal_False; +} + +sal_Bool Storage::IsStorageFile( SvStream* pStream ) +{ + StgHeader aHdr; + sal_uLong nPos = pStream->Tell(); + sal_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, sal_Bool bDirect ) + : OLEStorageBase( new StgIo, NULL, m_nMode ), aName( rFile ), bIsRoot( sal_False ) +{ + sal_Bool bTemp = sal_False; + if( !aName.Len() ) + { + // no name = temporary name! + aName = TempFile::CreateTempName(); + bTemp = sal_True; + } + // the root storage creates the I/O system + m_nMode = m; + if( pIo->Open( aName, m ) ) + { + Init( sal_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, sal_Bool bDirect ) + : OLEStorageBase( new StgIo, NULL, m_nMode ), bIsRoot( sal_False ) +{ + m_nMode = STREAM_READ; + if( r.IsWritable() ) + m_nMode = STREAM_READ | STREAM_WRITE; + if( r.GetError() == SVSTREAM_OK ) + { + pIo->SetStrm( &r, sal_False ); + sal_uLong nSize = r.Seek( STREAM_SEEK_TO_END ); + r.Seek( 0L ); + // Initializing is OK if the stream is empty + Init( sal_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, sal_Bool bDirect ) + : OLEStorageBase( new StgIo, NULL, m_nMode ), bIsRoot( sal_False ) +{ + m_nMode = STREAM_READ; + + if ( rStrm.GetError() != SVSTREAM_OK ) + { + SetError( rStrm.GetError() ); + pEntry = NULL; + return; + } + + SvStream* pStream = rStrm.GetModifySvStream(); + if ( !pStream ) + { + OSL_FAIL( "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 ); + + sal_uLong nSize = pStream->Seek( STREAM_SEEK_TO_END ); + pStream->Seek( 0L ); + // Initializing is OK if the stream is empty + Init( sal_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( sal_Bool bCreate ) +{ + pEntry = NULL; + sal_Bool bHdrLoaded = sal_False; + bIsRoot = sal_True; + if( pIo->Good() ) + { + sal_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( sal_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, sal_Bool bDirect ) +{ + OSL_FAIL("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, sal_Bool bDirect ) +{ + return OpenStorage( rName, m, bDirect ); +} + +BaseStorage* Storage::OpenStorage( const String& rName, StreamMode m, sal_Bool bDirect ) +{ + if( !Validate() || !ValidateMode( m ) ) + return new Storage( pIo, NULL, m ); + if( bDirect && !pEntry->bDirect ) + bDirect = sal_False; + + StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName ); + if( !p ) + { + if( !( m & STREAM_NOCREATE ) ) + { + sal_Bool bTemp = sal_False; + // create a new storage + String aNewName = rName; + if( !aNewName.Len() ) + { + aNewName.AssignAscii( "Temp Stg " ); + aNewName.Append( String::CreateFromInt32( ++nTmpCount ) ); + bTemp = sal_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 = sal_True; + return pStg; +} + +// Open a stream + +BaseStorageStream* Storage::OpenStream( const String& rName, StreamMode m, sal_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 ); + sal_Bool bTemp = sal_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 = sal_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( sal_True ); + pIo->MoveError( *pStm ); + return pStm; +} + +// Delete a stream or substorage by setting the temp bit. + +sal_Bool Storage::Remove( const String& rName ) +{ + if( !Validate( sal_True ) ) + return sal_False; + StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName ); + if( p ) + { + p->Invalidate( sal_True ); + return sal_True; + } + else + { + SetError( SVSTREAM_FILE_NOT_FOUND ); + return sal_False; + } +} + +// Rename a storage element + +sal_Bool Storage::Rename( const String& rOld, const String& rNew ) +{ + if( Validate( sal_True ) ) + { + sal_Bool b = pIo->pTOC->Rename( *pEntry, rOld, rNew ); + pIo->MoveError( *this ); + return b; + } + else + return sal_False; +} + +// Copy one element + +sal_Bool Storage::CopyTo( const String& rElem, BaseStorage* pDest, const String& rNew ) +{ + if( !Validate() || !pDest || !pDest->Validate( sal_True ) ) + return sal_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 sal_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 ); + + sal_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 sal_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 ); + + sal_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 sal_Bool( Good() && pDest->Good() ); + } + } + SetError( SVSTREAM_FILE_NOT_FOUND ); + return sal_False; +} + +sal_Bool Storage::CopyTo( BaseStorage* pDest ) const +{ + if( !Validate() || !pDest || !pDest->Validate( sal_True ) || Equals( *pDest ) ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_False; + } + Storage* pThis = (Storage*) this; + /* + if( !pThis->pEntry->IsContained( pDest->pEntry ) ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_False; + } + */ + pDest->SetClassId( GetClassId() ); + pDest->SetDirty(); + SvStorageInfoList aList; + FillInfoList( &aList ); + sal_Bool bRes = sal_True; + for( sal_uInt16 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 sal_Bool( Good() && pDest->Good() ); +} + +// Move one element + +sal_Bool Storage::MoveTo( const String& rElem, BaseStorage* pODest, const String& rNew ) +{ + if( !Validate() || !pODest || !pODest->Validate( sal_True ) || Equals( *pODest ) ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_False; + } + + StgDirEntry* pElem = pIo->pTOC->Find( *pEntry, rElem ); + if( pElem ) + { + // Simplest case: both storages share the same file + sal_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 sal_False; + } + bRes = pIo->pTOC->Move( *pEntry, *pDest->pEntry, rNew ); + if( !bRes ) + { + pIo->MoveError( *this ); + pDest->pIo->MoveError( *pDest ); + sal_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 sal_False; +} + +sal_Bool Storage::IsStorage( const String& rName ) const +{ + if( Validate() ) + { + StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName ); + if( p ) + return sal_Bool( p->aEntry.GetType() == STG_STORAGE ); + } + return sal_False; +} + +sal_Bool Storage::IsStream( const String& rName ) const +{ + if( Validate() ) + { + StgDirEntry* p = pIo->pTOC->Find( *pEntry, rName ); + if( p ) + return sal_Bool( p->aEntry.GetType() == STG_STREAM ); + } + return sal_False; +} + +sal_Bool Storage::IsContained( const String& rName ) const +{ + if( Validate() ) + return sal_Bool( pIo->pTOC->Find( *pEntry, rName ) != NULL ); + else + return sal_False; +} + +// Commit all sub-elements within this storage. If this is +// the root, commit the FAT, the TOC and the header as well. + +sal_Bool Storage::Commit() +{ + sal_Bool bRes = sal_True; + if( !Validate() ) + return sal_False; + if( !( m_nMode & STREAM_WRITE ) ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_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; +} + +sal_Bool Storage::Revert() +{ + return sal_True; +} + +///////////////////////////// OLE Support //////////////////////////////// + +// Set the storage type + +void Storage::SetClass( const SvGlobalName & rClass, + sal_uLong nOriginalClipFormat, + const String & rUserTypeName ) +{ + if( Validate( sal_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, sal_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, + sal_uLong nOriginalClipFormat, + const String & rUserTypeName ) +{ + if( Validate( sal_True ) ) + { + SetClass( rConvertClass, nOriginalClipFormat, rUserTypeName ); + // plus the convert flag: + StgOleStream aOle( *this, sal_True ); + aOle.GetFlags() |= 4; + if( !aOle.Store() ) + SetError( aOle.GetError() ); + } +} + +SvGlobalName Storage::GetClassName() +{ + StgCompObjStream aCompObj( *this, sal_False ); + if( aCompObj.Load() ) + return SvGlobalName( (const CLSID&) aCompObj.GetClsId() ); + pIo->ResetError(); + + if ( pEntry ) + return SvGlobalName( (const CLSID&) pEntry->aEntry.GetClassId() ); + + return SvGlobalName(); +} + +sal_uLong Storage::GetFormat() +{ + StgCompObjStream aCompObj( *this, sal_False ); + if( aCompObj.Load() ) + return aCompObj.GetCbFormat(); + pIo->ResetError(); + return 0; +} + +String Storage::GetUserName() +{ + StgCompObjStream aCompObj( *this, sal_False ); + if( aCompObj.Load() ) + return aCompObj.GetUserName(); + pIo->ResetError(); + return String(); +} + +sal_Bool Storage::ShouldConvert() +{ + StgOleStream aOle( *this, sal_False ); + if( aOle.Load() ) + return sal_Bool( ( aOle.GetFlags() & 4 ) != 0 ); + else + { + pIo->ResetError(); + return sal_False; + } +} + +sal_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(); +} + +sal_Bool Storage::Validate( sal_Bool bValidate ) const +{ + sal_Bool bRet = Validate_Impl( bValidate ); + if ( !bRet ) + SetError( SVSTREAM_ACCESS_DENIED ); + return bRet; +} + +sal_Bool Storage::ValidateMode( StreamMode nMode ) const +{ + sal_Bool bRet = ValidateMode_Impl( nMode ); + if ( !bRet ) + SetError( SVSTREAM_ACCESS_DENIED ); + return bRet; +} + +sal_Bool Storage::ValidateMode( StreamMode nMode, StgDirEntry* p ) const +{ + sal_Bool bRet = ValidateMode_Impl( nMode, p ); + if ( !bRet ) + SetError( SVSTREAM_ACCESS_DENIED ); + return bRet; +} + +sal_Bool Storage::Equals( const BaseStorage& rStorage ) const +{ + const Storage* pOther = PTR_CAST( Storage, &rStorage ); + return pOther && ( pOther->pEntry == pEntry ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgavl.cxx b/sot/source/sdstor/stgavl.cxx new file mode 100644 index 000000000000..c411b845e0a2 --- /dev/null +++ b/sot/source/sdstor/stgavl.cxx @@ -0,0 +1,422 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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, sal_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 sal_False if the element already exists. + +sal_Bool StgAvlNode::Insert( StgAvlNode** pRoot, StgAvlNode* pIns ) +{ + StgAvlNode* pPivot, *pHeavy, *pNewRoot, *pParent, *pPrev; + // special case - empty tree + if( *pRoot == NULL ) + { + *pRoot = pIns; + return sal_True; + } + // find insertion point and return if already present + short nRes = (*pRoot)->Locate( pIns, &pPivot, &pParent, &pPrev ); + if( !nRes ) + return sal_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 sal_True; +} + +// Remove node from tree. Returns sal_True is found and removed. +// Actually delete if bDel + +sal_Bool StgAvlNode::Remove( StgAvlNode** pRoot, StgAvlNode* pDel, sal_Bool bDel ) +{ + // special case - empty tree + if( *pRoot == NULL ) + return sal_False; + // delete the element + pDel = Rem( pRoot, pDel, sal_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, sal_False ); + Insert( &pNew, p ); + } + *pRoot = pNew;*/ + return sal_True; + } + else + return sal_False; +} + +// Move node to a different tree. Returns sal_True is found and moved. This routine +// may be called when the key has changed. + +sal_Bool StgAvlNode::Move + ( StgAvlNode** pRoot1, StgAvlNode** pRoot2, StgAvlNode* pMove ) +{ + // special case - empty tree + if( *pRoot1 == NULL ) + return sal_False; + pMove = Rem( pRoot1, pMove, sal_False ); + if( pMove ) + return Insert( pRoot2, pMove ); + else + return sal_False; +} + +////////////////////////// class AvlIterator ///////////////////////// + +// The iterator walks through a tree one entry by one. + +StgAvlIterator::StgAvlIterator( StgAvlNode* p ) +{ + pRoot = p; + nCount = 0; + nCur = 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 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgavl.hxx b/sot/source/sdstor/stgavl.hxx new file mode 100644 index 000000000000..8bd48ef8b62e --- /dev/null +++ b/sot/source/sdstor/stgavl.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + +#include <tools/solar.h> + +// 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*, sal_Bool ); +protected: + short nId; // iterator ID + short nBalance; // indicates tree balance + StgAvlNode* pLeft, *pRight; // leaves + StgAvlNode(); +public: + virtual ~StgAvlNode(); + StgAvlNode* Find( StgAvlNode* ); + static sal_Bool Insert( StgAvlNode**, StgAvlNode* ); + static sal_Bool Remove( StgAvlNode**, StgAvlNode*, sal_Bool bDel = sal_True ); + static sal_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 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgcache.cxx b/sot/source/sdstor/stgcache.cxx new file mode 100644 index 000000000000..d9b5884473b0 --- /dev/null +++ b/sot/source/sdstor/stgcache.cxx @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <boost/unordered_map.hpp> +#if defined(_MSC_VER) && (_MSC_VER<1200) +#include <tools/postsys.h> +#endif + +#include <string.h> +#include <osl/endian.h> +#include <tools/string.hxx> + +#include "sot/stg.hxx" +#include "stgelem.hxx" +#include "stgcache.hxx" +#include "stgstrms.hxx" +#include "stgdir.hxx" +#include "stgio.hxx" + +/*************************************************************************/ +//----------------------------------------------------------------------------- +typedef boost::unordered_map +< + sal_Int32, + StgPage *, + boost::hash< sal_Int32 >, + std::equal_to< sal_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 = sal_False; + nPage = 0; + pData = new sal_uInt8[ nData ]; + pNext1 = + pNext2 = + pLast1 = + pLast2 = NULL; + pOwner = NULL; +} + +StgPage::~StgPage() +{ + delete [] pData; +} + +void StgPage::SetPage( short nOff, sal_Int32 nVal ) +{ + if( ( nOff < (short) ( nData / sizeof( sal_Int32 ) ) ) && nOff >= 0 ) + { +#ifdef OSL_BIGENDIAN + nVal = SWAPLONG(nVal); +#endif + ((sal_Int32*) pData )[ nOff ] = nVal; + bDirty = sal_True; + } +} + +//////////////////////////////// class StgCache //////////////////////////// + +// The disk cache holds the cached sectors. The sector type differ according +// to their purpose. + +sal_Int32 lcl_GetPageCount( sal_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 = sal_False; + bFile = sal_False; + pLRUCache = NULL; + pStorageStream = NULL; +} + +StgCache::~StgCache() +{ + Clear(); + SetStrm( NULL, sal_False ); + delete (UsrStgPagePtr_Impl*)pLRUCache; +} + +void StgCache::SetPhysPageSize( short n ) +{ + nPageSize = n; + sal_uLong nPos = pStrm->Tell(); + sal_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( sal_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( sal_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( sal_Int32 nPage, sal_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( sal_Int32 nNew, sal_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, sal_True ); + if( q ) + memcpy( p->pData, q->pData, p->nData ); + } + p->SetDirty(); + return p; +} + +// Flush the cache whose owner is given. NULL flushes all. + +sal_Bool StgCache::Commit( StgDirEntry* ) +{ + StgPage* p = pElem1; + if( p ) do + { + if( p->bDirty ) + { + sal_Bool b = Write( p->nPage, p->pData, 1 ); + if( !b ) + return sal_False; + p->bDirty = sal_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(); + sal_Bool b = Write( p->nPage, p->pData, 1 ); + if( !b ) + return sal_False; + p->bDirty = sal_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(); + sal_Bool b = Write( p->nPage, p->pData, 1 ); + if( !b ) + return sal_False; + p->bDirty = sal_False; + } + p = p->pNext1; + } while( p != pElem1 ); +#endif + return sal_True; +} + +void StgCache::Revert( StgDirEntry* ) +{} + +// Set a stream + +void StgCache::SetStrm( SvStream* p, sal_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 = sal_False; +} + +// Open/close the disk file + +sal_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 + sal_Bool bAccessDenied = sal_False; + if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() ) + { + pFileStrm->Close(); + bAccessDenied = sal_True; + } + SetStrm( pFileStrm, sal_True ); + if( pFileStrm->IsOpen() ) + { + sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END ); + nPages = lcl_GetPageCount( nFileSize, nPageSize ); + pStrm->Seek( 0L ); + } + else + nPages = 0; + bFile = sal_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 + +sal_Bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_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 ) + { + sal_uLong nPos = Page2Pos( nPage ); + sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg; + sal_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(); +} + +sal_Bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg ) +{ + if( Good() ) + { + sal_uLong nPos = Page2Pos( nPage ); + sal_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 + } + } + sal_uLong nRes = pStrm->Write( pBuf, nBytes ); + if( nRes != nBytes ) + SetError( SVSTREAM_WRITE_ERROR ); + else + SetError( pStrm->GetError() ); +#ifdef READ_AFTER_WRITE + sal_uInt8 cBuf[ 512 ]; + pStrm->Flush(); + pStrm->Seek( nPos ); + sal_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 + +sal_Bool StgCache::SetSize( sal_Int32 n ) +{ + // Add the file header + sal_Int32 nSize = n * nPageSize + 512; + pStrm->SetStreamSize( nSize ); + SetError( pStrm->GetError() ); + if( !nError ) + nPages = n; + return Good(); +} + +void StgCache::SetError( sal_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 + +sal_Int32 StgCache::Page2Pos( sal_Int32 nPage ) +{ + if( nPage < 0 ) nPage = 0; + return( nPage * nPageSize ) + nPageSize; +} + +sal_Int32 StgCache::Pos2Page( sal_Int32 nPos ) +{ + return ( ( nPos + nPageSize - 1 ) / nPageSize ) * nPageSize - 1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgcache.hxx b/sot/source/sdstor/stgcache.hxx new file mode 100644 index 000000000000..9b47cffed6c8 --- /dev/null +++ b/sot/source/sdstor/stgcache.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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> +#include <tools/solar.h> +#include <tools/stream.hxx> +#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 + sal_uLong nError; // error code + sal_Int32 nPages; // size of data area in pages + sal_uInt16 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( sal_Int32 ); // create a cached page +protected: + SvStream* pStrm; // physical stream + sal_Bool bMyStream; // sal_True: delete stream in dtor + sal_Bool bFile; // sal_True: file stream + sal_Int32 Page2Pos( sal_Int32 ); // page address --> file position + sal_Int32 Pos2Page( sal_Int32 ); // file position --> page address +public: + StgCache(); + ~StgCache(); + void IncRef() { nRef++; } + sal_uInt16 DecRef() { return --nRef; } + void SetPhysPageSize( short ); + sal_Int32 GetPhysPages() { return nPages; } + short GetPhysPageSize() { return nPageSize; } + SvStream* GetStrm() { return pStrm; } + void SetStrm( SvStream*, sal_Bool ); + void SetStrm( UCBStorageStream* ); + sal_Bool IsWritable() { return pStrm->IsWritable(); } + sal_Bool Good() { return sal_Bool( nError == SVSTREAM_OK ); } + sal_Bool Bad() { return sal_Bool( nError != SVSTREAM_OK ); } + sal_uLong GetError() { return nError; } + void MoveError( StorageBase& ); + void SetError( sal_uLong ); + void ResetError(); + sal_Bool Open( const String& rName, StreamMode ); + void Close(); + sal_Bool Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPages ); + sal_Bool Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPages ); + sal_Bool SetSize( sal_Int32 nPages ); + StgPage* Find( sal_Int32 ); // find a cached page + StgPage* Get( sal_Int32, sal_Bool ); // get a cached page + StgPage* Copy( sal_Int32, sal_Int32=STG_FREE ); // copy a page + sal_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 + sal_Int32 nPage; // page # + sal_uInt8* pData; // nPageSize characters + short nData; // size of this page + sal_Bool bDirty; // dirty flag + StgPage( StgCache*, short ); + ~StgPage(); +public: + void SetDirty() { bDirty = sal_True; } + sal_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. + sal_Int32 GetPage( short nOff ) + { + if( ( nOff >= (short) ( nData / sizeof( sal_Int32 ) ) ) || nOff < 0 ) + return -1; + sal_Int32 n = ((sal_Int32*) pData )[ nOff ]; +#ifdef OSL_BIGENDIAN + return SWAPLONG(n); +#else + return n; +#endif + } + void SetPage( short, sal_Int32 ); // put an element +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgdir.cxx b/sot/source/sdstor/stgdir.cxx new file mode 100644 index 000000000000..fc9610a6af3b --- /dev/null +++ b/sot/source/sdstor/stgdir.cxx @@ -0,0 +1,1062 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/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, sal_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 = sal_True; + bInvalid = + bCreated = + bRenamed = + bRemoved = + bTemp = + bDirty = + bZombie = sal_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( sal_Int32& n ) +{ + sal_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( sal_Bool bForce ) +{ + if( pLeft ) + ((StgDirEntry*) pLeft)->DelTemp( sal_False ); + if( pRight ) + ((StgDirEntry*) pRight)->DelTemp( sal_False ); + if( pDown ) + { + // If the storage is dead, of course all elements are dead, too + if( bInvalid && aEntry.GetType() == STG_STORAGE ) + bForce = sal_True; + pDown->DelTemp( bForce ); + } + if( ( bForce || bInvalid ) + && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ ) + { + Close(); + if( pUp ) + { + // this deletes the element if refcnt == 0! + sal_Bool bDel = nRefCnt == 0; + StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel ); + if( !bDel ) + { + pLeft = pRight = pDown = 0; + bInvalid = bZombie = sal_True; + } + } + } +} + +// Save the tree into the given dir stream + +sal_Bool StgDirEntry::Store( StgDirStrm& rStrm ) +{ + void* pEntry = rStrm.GetEntry( nEntry, sal_True ); + if( !pEntry ) + return sal_False; + // Do not store the current (maybe not commited) entry + aSave.Store( pEntry ); + if( pLeft ) + if( !((StgDirEntry*) pLeft)->Store( rStrm ) ) + return sal_False; + if( pRight ) + if( !((StgDirEntry*) pRight)->Store( rStrm ) ) + return sal_False; + if( pDown ) + if( !pDown->Store( rStrm ) ) + return sal_False; + return sal_True; +} + +sal_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 sal_False; + } + return sal_True; +} + +// Save all dirty streams + +sal_Bool StgDirEntry::StoreStreams( StgIo& rIo ) +{ + if( !StoreStream( rIo ) ) + return sal_False; + if( pLeft ) + if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) ) + return sal_False; + if( pRight ) + if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) ) + return sal_False; + if( pDown ) + if( !pDown->StoreStreams( rIo ) ) + return sal_False; + return sal_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 + +sal_Bool StgDirEntry::IsDirty() +{ + if( bDirty || bInvalid ) + return sal_True; + if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() ) + return sal_True; + if( pRight && ((StgDirEntry*) pRight)->IsDirty() ) + return sal_True; + if( pDown && pDown->IsDirty() ) + return sal_True; + return sal_False; +} + +// Set up a stream. + +void StgDirEntry::OpenStream( StgIo& rIo, sal_Bool bForceBig ) +{ + sal_Int32 nThreshold = (sal_uInt16) 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 = sal_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 + +sal_Int32 StgDirEntry::GetSize() +{ + sal_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. + +sal_Bool StgDirEntry::SetSize( sal_Int32 nNewSize ) +{ + if ( + !( nMode & STREAM_WRITE ) || + (!bDirect && !pTmpStrm && !Strm2Tmp()) + ) + { + return sal_False; + } + + if( nNewSize < nPos ) + nPos = nNewSize; + if( pTmpStrm ) + { + pTmpStrm->SetSize( nNewSize ); + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + return sal_Bool( pTmpStrm->GetError() == SVSTREAM_OK ); + } + else + { + sal_Bool bRes = sal_False; + StgIo& rIo = pStgStrm->GetIo(); + sal_Int32 nThreshold = rIo.aHdr.GetThreshold(); + // ensure the correct storage stream! + StgStrm* pOld = NULL; + sal_uInt16 nOldSize = 0; + if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() ) + { + pOld = pStgStrm; + nOldSize = (sal_uInt16) pOld->GetSize(); + pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 ); + } + else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() ) + { + pOld = pStgStrm; + nOldSize = (sal_uInt16) 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 sal_uInt8[ nOldSize ]; + pOld->Pos2Page( 0L ); + pStgStrm->Pos2Page( 0L ); + if( pOld->Read( pBuf, nOldSize ) + && pStgStrm->Write( pBuf, nOldSize ) ) + bRes = sal_True; + delete[] static_cast<sal_uInt8*>(pBuf); + } + else + bRes = sal_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 = sal_True; + } + } + return bRes; + } +} + +// Seek. On negative values, seek to EOF. + +sal_Int32 StgDirEntry::Seek( sal_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 + { + sal_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 + +sal_Int32 StgDirEntry::Read( void* p, sal_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 + +sal_Int32 StgDirEntry::Write( const void* p, sal_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 + { + sal_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 ) +{ + sal_Int32 n = GetSize(); + if( rDest.SetSize( n ) && n ) + { + sal_uInt8 aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + Seek( 0L ); + rDest.Seek( 0L ); + while( n ) + { + sal_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 ) +{ + sal_Int32 n = GetSize(); + if( rDest.SetSize( n ) && n ) + { + sal_uLong Pos = rDest.Tell(); + sal_uInt8 aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + Seek( 0L ); + rDest.Seek( 0L ); + while( n ) + { + sal_Int32 nn = n; + if( nn > 4096 ) + nn = 4096; + if( Read( p, nn ) != nn ) + break; + if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn ) + break; + n -= nn; + } + rDest.Seek( Pos ); // ?! Seems to be undocumented ! + } +} + +// Commit this entry + +sal_Bool StgDirEntry::Commit() +{ + // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" ); + + aSave = aEntry; + sal_Bool bRes = sal_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 + +sal_Bool StgDirEntry::Revert() +{ + aEntry = aSave; + switch( aEntry.GetType() ) + { + case STG_STREAM: + if( pCurStrm ) + delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL; + break; + case STG_STORAGE: + { + sal_Bool bSomeRenamed = sal_False; + StgIterator aOIter( *this ); + StgDirEntry* op = aOIter.First(); + while( op ) + { + op->aEntry = op->aSave; + op->bDirty = sal_False; + bSomeRenamed = sal_Bool( bSomeRenamed | op->bRenamed ); + // Remove any new entries + if( op->bCreated ) + { + op->bCreated = sal_False; + op->Close(); + op->bInvalid = sal_True; + } + // Reactivate any removed entries + else if( op->bRemoved ) + op->bRemoved = op->bInvalid = op->bTemp = sal_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 = sal_False; + } + p = aIter.Next(); + } + } + DelTemp( sal_False ); + break; + } + case STG_EMPTY: + case STG_LOCKBYTES: + case STG_PROPERTY: + case STG_ROOT: + break; + } + return sal_True; +} + +// Copy the stg stream to the temp stream + +sal_Bool StgDirEntry::Strm2Tmp() +{ + if( !pTmpStrm ) + { + sal_uLong n = 0; + if( pCurStrm ) + { + // It was already commited once + pTmpStrm = new StgTmpStrm; + if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) ) + return sal_True; + n = 1; // indicates error + } + else + { + n = aEntry.GetSize(); + pTmpStrm = new StgTmpStrm( n ); + if( pTmpStrm->GetError() == SVSTREAM_OK ) + { + if( n ) + { + sal_uInt8 aTempBytes[ 4096 ]; + void* p = static_cast<void*>( aTempBytes ); + pStgStrm->Pos2Page( 0L ); + while( n ) + { + sal_uLong nn = n; + if( nn > 4096 ) + nn = 4096; + if( (sal_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 sal_False; + } + } + return sal_True; +} + +// Copy the temp stream to the stg stream during the final commit + +sal_Bool StgDirEntry::Tmp2Strm() +{ + // We did commit once, but have not written since then + if( !pTmpStrm ) + pTmpStrm = pCurStrm, pCurStrm = NULL; + if( pTmpStrm ) + { + sal_uLong n = pTmpStrm->GetSize(); + StgStrm* pNewStrm; + StgIo& rIo = pStgStrm->GetIo(); + sal_uLong nThreshold = (sal_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 ) ) + { + sal_uInt8 p[ 4096 ]; + pTmpStrm->Seek( 0L ); + while( n ) + { + sal_uLong nn = n; + if( nn > 4096 ) + nn = 4096; + if( pTmpStrm->Read( p, nn ) != nn ) + break; + if( (sal_uLong) pNewStrm->Write( p, nn ) != nn ) + break; + n -= nn; + } + if( n ) + { + pTmpStrm->Seek( nPos ); + pStgStrm->GetIo().SetError( pTmpStrm->GetError() ); + delete pNewStrm; + return sal_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 sal_True; +} + +// Check if the given entry is contained in this entry + +sal_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 sal_False; + if( p->aEntry.GetType() == STG_STORAGE ) + if( !p->IsContained( pStg ) ) + return sal_False; + p = aIter.Next(); + } + } + return sal_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( sal_Bool bDel ) +{ +// nRefCnt = 0; + if( bDel ) + bRemoved = bInvalid = sal_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( sal_Int32 n, StgDirEntry* pUpper ) +{ + void* p = ( n == STG_FREE ) ? NULL : GetEntry( n ); + if( p ) + { + sal_Bool bOk(sal_False); + StgDirEntry* pCur = new StgDirEntry( p, &bOk ); + + if( !bOk ) + { + delete pCur; + rIo.SetError( SVSTREAM_GENERALERROR ); + // an error occurred + return; + } + + // better it is + if( !pUpper ) + pCur->aEntry.SetType( STG_ROOT ); + + sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT ); + sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT ); + // substorage? + sal_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 + { + // bnc#682484: There are some really broken docs out there + // that contain duplicate entries in 'Directory' section + // so don't set the error flag here and just skip those + // (was: 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. + +sal_Bool StgDirStrm::SetSize( sal_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 + +sal_Bool StgDirStrm::Store() +{ + if( !pRoot->IsDirty() ) + return sal_True; + if( !pRoot->StoreStreams( rIo ) ) + return sal_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 + sal_Int32 nOldStart = nStart; // save for later deletion + sal_Int32 nOldSize = nSize; + nStart = nPage = STG_EOF; + nSize = nPos = 0; + nOffset = 0; + // Delete all temporary entries + pRoot->DelTemp( sal_False ); + // set the entry numbers + sal_Int32 n = 0; + pRoot->Enum( n ); + if( !SetSize( n * STGENTRY_SIZE ) ) + { + nStart = nOldStart; nSize = nOldSize; + pRoot->RevertAll(); + return sal_False; + } + // set up the cache elements for the new stream + if( !Copy( STG_FREE, nSize ) ) + { + pRoot->RevertAll(); + return sal_False; + } + // Write the data to the new stream + if( !pRoot->Store( *this ) ) + { + pRoot->RevertAll(); + return sal_False; + } + // fill any remaining entries with empty data + sal_Int32 ne = nSize / STGENTRY_SIZE; + StgEntry aEmpty; + aEmpty.Init(); + while( n < ne ) + { + void* p = GetEntry( n++, sal_True ); + if( !p ) + { + pRoot->RevertAll(); + return sal_False; + } + aEmpty.Store( p ); + } + // Now we can release the old stream + pFat->FreePages( nOldStart, sal_True ); + rIo.aHdr.SetTOCStart( nStart ); + return sal_True; +} + +// Get a dir entry. + +void* StgDirStrm::GetEntry( sal_Int32 n, sal_Bool bDirty ) +{ + if( n < 0 ) + return NULL; + + n *= STGENTRY_SIZE; + if( n < 0 && n >= nSize ) + return NULL; + return GetPtr( n, sal_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 = sal_False; + pRes->bCreated = + pRes->bDirty = sal_True; + } + else + { + pRes = new StgDirEntry( aEntry ); + if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) ) + { + pRes->pUp = &rStg; + pRes->ppRoot = &pRoot; + pRes->bCreated = + pRes->bDirty = sal_True; + } + else + { + rIo.SetError( SVSTREAM_CANNOT_MAKE ); + delete pRes; pRes = NULL; + } + } + return pRes; +} + +// Rename the given entry. + +sal_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, sal_False ) ) + return sal_False; + p->aEntry.SetName( rNew ); + if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) ) + return sal_False; + p->bRenamed = p->bDirty = sal_True; + return sal_True; + } + else + { + rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); + return sal_False; + } +} + +// Move the given entry to a different storage. + +sal_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 sal_False; + p->bDirty = sal_True; + return sal_True; + } + else + { + rIo.SetError( SVSTREAM_FILE_NOT_FOUND ); + return sal_False; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgdir.hxx b/sot/source/sdstor/stgdir.hxx new file mode 100644 index 000000000000..ccb12c4762d7 --- /dev/null +++ b/sot/source/sdstor/stgdir.hxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + sal_Int32 nEntry; // entry # in TOC stream (temp) + sal_Int32 nPos; // current position + sal_Bool bDirty; // dirty directory entry + sal_Bool bCreated; // newly created entry + sal_Bool bRemoved; // removed per Invalidate() + sal_Bool bRenamed; // renamed + void InitMembers(); // ctor helper + virtual short Compare( const StgAvlNode* ) const; + sal_Bool StoreStream( StgIo& ); // store the stream + sal_Bool StoreStreams( StgIo& ); // store all streams + void RevertAll(); // revert the whole tree + sal_Bool Strm2Tmp(); // copy stgstream to temp file + sal_Bool Tmp2Strm(); // copy temp file to stgstream +public: + StgEntry aEntry; // entry data + sal_Int32 nRefCnt; // reference count + StreamMode nMode; // open mode + sal_Bool bTemp; // sal_True: delete on dir flush + sal_Bool bDirect; // sal_True: direct mode + sal_Bool bZombie; // sal_True: Removed From StgIo + sal_Bool bInvalid; // sal_True: invalid entry + StgDirEntry( const void*, sal_Bool * pbOk ); + StgDirEntry( const StgEntry& ); + ~StgDirEntry(); + + void Invalidate( sal_Bool=sal_False ); // invalidate all open entries + void Enum( sal_Int32& ); // enumerate entries for iteration + void DelTemp( sal_Bool ); // delete temporary entries + sal_Bool Store( StgDirStrm& ); // save entry into dir strm + sal_Bool IsContained( StgDirEntry* ); // check if subentry + + void SetDirty() { bDirty = sal_True; } + sal_Bool IsDirty(); + void ClearDirty(); + + sal_Bool Commit(); + sal_Bool Revert(); + + void OpenStream( StgIo&, sal_Bool=sal_False ); // set up an approbiate stream + void Close(); + sal_Int32 GetSize(); + sal_Bool SetSize( sal_Int32 ); + sal_Int32 Seek( sal_Int32 ); + sal_Int32 Tell() { return nPos; } + sal_Int32 Read( void*, sal_Int32 ); + sal_Int32 Write( const void*, sal_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( sal_Int32, StgDirEntry* ); +public: + StgDirStrm( StgIo& ); + ~StgDirStrm(); + virtual sal_Bool SetSize( sal_Int32 ); // change the size + sal_Bool Store(); + void* GetEntry( sal_Int32 n, sal_Bool=sal_False );// get an entry + StgDirEntry* GetRoot() { return pRoot; } + StgDirEntry* Find( StgDirEntry&, const String& ); + StgDirEntry* Create( StgDirEntry&, const String&, StgEntryType ); + sal_Bool Remove( StgDirEntry&, const String& ); + sal_Bool Rename( StgDirEntry&, const String&, const String& ); + sal_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 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgelem.cxx b/sot/source/sdstor/stgelem.cxx new file mode 100644 index 000000000000..bfde32ade4c0 --- /dev/null +++ b/sot/source/sdstor/stgelem.cxx @@ -0,0 +1,427 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/stg.hxx" +#include "stgelem.hxx" +#include "stgcache.hxx" +#include "stgstrms.hxx" +#include "stgdir.hxx" +#include "stgio.hxx" + +static sal_uInt8 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 << (sal_Int32) rId.n1 + << (sal_Int16) rId.n2 + << (sal_Int16) rId.n3 + << (sal_uInt8) rId.n4 + << (sal_uInt8) rId.n5 + << (sal_uInt8) rId.n6 + << (sal_uInt8) rId.n7 + << (sal_uInt8) rId.n8 + << (sal_uInt8) rId.n9 + << (sal_uInt8) rId.n10 + << (sal_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 ); +} + +sal_Bool StgHeader::Load( StgIo& rIo ) +{ + SvStream& r = *rIo.GetStrm(); + Load( r ); + return rIo.Good(); +} + +sal_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; +} + +sal_Bool StgHeader::Store( StgIo& rIo ) +{ + if( !bDirty ) + return sal_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 + << (sal_Int32) 0 << (sal_Int32) 0 << (sal_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 sal_Bool( !bDirty ); +} + +static bool lcl_wontoverflow(short shift) +{ + return shift >= 0 && shift < (short)sizeof(short) * 8 - 1; +} + +// Perform thorough checks also on unknown variables +sal_Bool StgHeader::Check() +{ + return sal_Bool( memcmp( cSignature, cStgSignature, 8 ) == 0 + && (short) ( nVersion >> 16 ) == 3 ) + && lcl_wontoverflow(nPageSize) + && lcl_wontoverflow(nDataPageSize); +} + +sal_Int32 StgHeader::GetFATPage( short n ) const +{ + if( n >= 0 && n < 109 ) + return nMasterFAT[ n ]; + else + return STG_EOF; +} + +void StgHeader::SetFATPage( short n, sal_Int32 nb ) +{ + if( n >= 0 && n < 109 ) + { + if( nMasterFAT[ n ] != nb ) + bDirty = sal_True, nMasterFAT[ n ] = nb; + } +} + +void StgHeader::SetClassId( const ClsId& r ) +{ + if( memcmp( &aClsId, &r, sizeof( ClsId ) ) ) + bDirty = sal_True, memcpy( &aClsId, &r, sizeof( ClsId ) ); +} + +void StgHeader::SetTOCStart( sal_Int32 n ) +{ + if( n != nTOCstrm ) bDirty = sal_True, nTOCstrm = n; +} + +void StgHeader::SetDataFATStart( sal_Int32 n ) +{ + if( n != nDataFAT ) bDirty = sal_True, nDataFAT = n; +} + +void StgHeader::SetDataFATSize( sal_Int32 n ) +{ + if( n != nDataFATSize ) bDirty = sal_True, nDataFATSize = n; +} + +void StgHeader::SetFATSize( sal_Int32 n ) +{ + if( n != nFATSize ) bDirty = sal_True, nFATSize = n; +} + +void StgHeader::SetFATChain( sal_Int32 n ) +{ + if( n != nMasterChain ) + bDirty = sal_True, nMasterChain = n; +} + +void StgHeader::SetMasters( sal_Int32 n ) +{ + if( n != nMaster ) bDirty = sal_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 + sal_uInt8* p = (sal_uInt8*) rName.GetCharStr(); + for( sal_uInt16 i = 0; i < rName.Len(); i++, p++ ) + { + // check each character and substitute blanks for illegal ones + sal_uInt8 cChar = *p; + if( cChar == '!' || cChar == ':' || cChar == '\\' || cChar == '/' ) + *p = ' '; + } +} +*/ +/* +static void FromUnicode( String& rName ) +{ + rName.Convert( CHARSET_ANSI, ::GetSystemCharSet() ); +} +*/ +sal_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 sal_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(RTL_CONSTASCII_USTRINGPARAM("en")); + aLocale.Country = OUString(RTL_CONSTASCII_USTRINGPARAM("US")); + */ + static rtl::OUString aEN(RTL_CONSTASCII_USTRINGPARAM("en")); + static rtl::OUString aUS(RTL_CONSTASCII_USTRINGPARAM("US")); + static CharClass aCC( com::sun::star::lang::Locale( aEN, aUS, rtl::OUString() ) ); + return aCC.toUpper( rStr, 0, rStr.Len() ); +} + + +sal_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 sal_True; +} + +sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const +{ + sal_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, sal_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 sal_Int32* StgEntry::GetTime( StgEntryTime eTime ) const +{ + return( eTime == STG_MODIFIED ) ? nMtime : nAtime; +} + +void StgEntry::SetTime( StgEntryTime eTime, sal_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 +{ + sal_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. + +sal_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 + + sal_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 sal_False; + } + + aName = String( nName, n ); + // I don't know the locale, so en_US is hopefully fine + aName = ToUpperUnicode( aName ); + aName.Erase( 31 ); + + return sal_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 +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgelem.hxx b/sot/source/sdstor/stgelem.hxx new file mode 100644 index 000000000000..7ef8c4772f36 --- /dev/null +++ b/sot/source/sdstor/stgelem.hxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + +#include <tools/solar.h> + +#include <sot/stg.hxx> + +class StgIo; +class SvStream; +class String; + +SvStream& operator>>( SvStream&, ClsId& ); +SvStream& operator<<( SvStream&, const ClsId& ); + +class StgHeader +{ + sal_uInt8 cSignature[ 8 ]; // 00 signature (see below) + ClsId aClsId; // 08 Class ID + sal_Int32 nVersion; // 18 version number + sal_uInt16 nByteOrder; // 1C Unicode byte order indicator + sal_Int16 nPageSize; // 1E 1 << nPageSize = block size + sal_Int16 nDataPageSize; // 20 1 << this size == data block size + sal_uInt8 bDirty; // 22 internal dirty flag + sal_uInt8 cReserved[ 9 ]; // 23 + sal_Int32 nFATSize; // 2C total number of FAT pages + sal_Int32 nTOCstrm; // 30 starting page for the TOC stream + sal_Int32 nReserved; // 34 + sal_Int32 nThreshold; // 38 minimum file size for big data + sal_Int32 nDataFAT; // 3C page # of 1st data FAT block + sal_Int32 nDataFATSize; // 40 # of data fat blocks + sal_Int32 nMasterChain; // 44 chain to the next master block + sal_Int32 nMaster; // 48 # of additional master blocks + sal_Int32 nMasterFAT[ 109 ]; // 4C first 109 master FAT pages +public: + StgHeader(); + void Init(); // initialize the header + sal_Bool Load( StgIo& ); + sal_Bool Load( SvStream& ); + sal_Bool Store( StgIo& ); + sal_Bool Check(); // check the signature and version + short GetByteOrder() const { return nByteOrder; } + sal_Int32 GetTOCStart() const { return nTOCstrm; } + void SetTOCStart( sal_Int32 n ); + sal_Int32 GetDataFATStart() const { return nDataFAT; } + void SetDataFATStart( sal_Int32 n ); + sal_Int32 GetDataFATSize() const { return nDataFATSize; } + void SetDataFATSize( sal_Int32 n ); + sal_Int32 GetThreshold() const { return nThreshold; } + short GetPageSize() const { return nPageSize; } + short GetDataPageSize() const { return nDataPageSize; } + sal_Int32 GetFATSize() const { return nFATSize; } + void SetFATSize( sal_Int32 n ); + sal_Int32 GetFATChain() const { return nMasterChain; } + void SetFATChain( sal_Int32 n ); + sal_Int32 GetMasters() const { return nMaster; } + void SetMasters( sal_Int32 n ); + short GetFAT1Size() const { return 109; } + const ClsId& GetClassId() const { return aClsId; } + void SetClassId( const ClsId& ); + sal_Int32 GetFATPage( short ) const; + void SetFATPage( short, sal_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 + sal_uInt16 nName[ 32 ]; // 00 name as WCHAR + sal_Int16 nNameLen; // 40 size of name in bytes including 00H + sal_uInt8 cType; // 42 entry type + sal_uInt8 cFlags; // 43 0 or 1 (tree balance?) + sal_Int32 nLeft; // 44 left node entry + sal_Int32 nRight; // 48 right node entry + sal_Int32 nChild; // 4C 1st child entry if storage + ClsId aClsId; // 50 class ID (optional) + sal_Int32 nFlags; // 60 state flags(?) + sal_Int32 nMtime[ 2 ]; // 64 modification time + sal_Int32 nAtime[ 2 ]; // 6C creation and access time + sal_Int32 nPage1; // 74 starting block (either direct or translated) + sal_Int32 nSize; // 78 file size + sal_Int32 nUnknown; // 7C unknown + String aName; // Name as Compare String (ascii, upper) +public: + sal_Bool Init(); // initialize the data + sal_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 + sal_Bool Load( const void* ); + void Store( void* ); + StgEntryType GetType() const { return (StgEntryType) cType; } + sal_Int32 GetStartPage() const { return nPage1; } + void SetType( StgEntryType t ) { cType = (sal_uInt8) t; } + sal_uInt8 GetFlags() const { return cFlags; } + void SetFlags( sal_uInt8 c ) { cFlags = c; } + sal_Int32 GetSize() const { return nSize; } + void SetSize( sal_Int32 n ) { nSize = n; } + const ClsId& GetClassId() const { return aClsId; } + void SetClassId( const ClsId& ); + sal_Int32 GetLeaf( StgEntryRef ) const; + void SetLeaf( StgEntryRef, sal_Int32 ); + const sal_Int32* GetTime( StgEntryTime ) const; + void SetTime( StgEntryTime, sal_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 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgio.cxx b/sot/source/sdstor/stgio.cxx new file mode 100644 index 000000000000..db861fb93911 --- /dev/null +++ b/sot/source/sdstor/stgio.cxx @@ -0,0 +1,391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/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 = sal_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. + +sal_Bool StgIo::Load() +{ + if( pStrm ) + { + if( aHdr.Load( *this ) ) + { + if( aHdr.Check() ) + SetupStreams(); + else + return sal_False; + } + } + return Good(); +} + +// Set up an initial, empty storage + +sal_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 + +sal_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(); + sal_uLong n = pStrm->GetError(); + SetError( n ); +#ifdef DBG_UTIL + if( n==0 ) ValidateFATs(); +#endif + return sal_Bool( n == 0 ); + } + } + } + SetError( SVSTREAM_WRITE_ERROR ); + return sal_False; +} + + +class EasyFat +{ + sal_Int32 *pFat; + sal_Bool *pFree; + sal_Int32 nPages; + sal_Int32 nPageSize; + +public: + EasyFat( StgIo & rIo, StgStrm *pFatStream, sal_Int32 nPSize ); + ~EasyFat() { delete[] pFat; delete[] pFree; } + + sal_Int32 GetPageSize() { return nPageSize; } + sal_Int32 Count() { return nPages; } + sal_Int32 operator[]( sal_Int32 nOffset ) { return pFat[ nOffset ]; } + + sal_uLong Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect ); + sal_Bool HasUnrefChains(); +}; + +EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize ) +{ + nPages = pFatStream->GetSize() >> 2; + nPageSize = nPSize; + pFat = new sal_Int32[ nPages ]; + pFree = new sal_Bool[ nPages ]; + + StgPage *pPage = NULL; + sal_Int32 nFatPageSize = (1 << rIo.aHdr.GetPageSize()) - 2; + + for( sal_Int32 nPage = 0; nPage < nPages; nPage++ ) + { + if( ! (nPage % nFatPageSize) ) + { + pFatStream->Pos2Page( nPage << 2 ); + sal_Int32 nPhysPage = pFatStream->GetPage(); + pPage = rIo.Get( nPhysPage, sal_True ); + } + + pFat[ nPage ] = pPage->GetPage( short( nPage % nFatPageSize ) ); + pFree[ nPage ] = sal_True; + } +} + +sal_Bool EasyFat::HasUnrefChains() +{ + for( sal_Int32 nPage = 0; nPage < nPages; nPage++ ) + { + if( pFree[ nPage ] && pFat[ nPage ] != -1 ) + return sal_True; + } + return sal_False; +} + +sal_uLong EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect ) +{ + if( nCount > 0 ) + --nCount /= GetPageSize(), nCount++; + + sal_Int32 nCurPage = nPage; + while( nCount != 0 ) + { + pFree[ nCurPage ] = sal_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 +{ + sal_uLong nError; + + EasyFat aSmallFat; + EasyFat aFat; + + StgIo &rIo; + + sal_uLong ValidateMasterFATs(); + sal_uLong ValidateDirectoryEntries(); + sal_uLong FindUnrefedChains(); + sal_uLong MarkAll( StgDirEntry *pEntry ); + +public: + + Validator( StgIo &rIo ); + sal_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 ) +{ + sal_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; +} + +sal_uLong Validator::ValidateMasterFATs() +{ + sal_Int32 nCount = rIo.aHdr.GetFATSize(); + sal_uLong nErr; + for( sal_Int32 i = 0; i < nCount; i++ ) + { + if( ( nErr = aFat.Mark(rIo.pFAT->GetPage( short(i), sal_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; +} + +sal_uLong Validator::MarkAll( StgDirEntry *pEntry ) +{ + StgIterator aIter( *pEntry ); + sal_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 + { + sal_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; +} + +sal_uLong Validator::ValidateDirectoryEntries() +{ + // Normale DirEntries + sal_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; +} + +sal_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(); +} + +sal_uLong StgIo::ValidateFATs() +{ + if( bFile ) + { + Validator *pV = new Validator( *this ); + sal_Bool bRet1 = !pV->IsError(), bRet2 = sal_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; + } + + sal_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 = sal_True; + } +// DBG_ASSERT( nErr == FAT_OK ,"Storage kaputt"); + return nErr; + } +// OSL_FAIL("Validiere nicht (kein FileStorage)"); + return FAT_OK; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/sot/source/sdstor/stgio.hxx b/sot/source/sdstor/stgio.hxx new file mode 100644 index 000000000000..91c32c7f0e1b --- /dev/null +++ b/sot/source/sdstor/stgio.hxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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; + sal_uLong nErr; +}; + +class StgIo : public StgCache { + void SetupStreams(); // load all internal streams + sal_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 + sal_Bool Load(); // load a storage file + sal_Bool Init(); // set up an empty file + sal_Bool CommitAll(); // commit everything (root commit) + + static void SetErrorLink( const Link& ); + static const Link& GetErrorLink(); + sal_uLong ValidateFATs( ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgole.cxx b/sot/source/sdstor/stgole.cxx new file mode 100644 index 000000000000..8ae6af18f7b7 --- /dev/null +++ b/sot/source/sdstor/stgole.cxx @@ -0,0 +1,204 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/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, sal_Bool bWr ) +{ + bIsWritable = sal_True; + sal_uInt16 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; +} + +sal_uLong StgInternalStream::GetData( void* pData, sal_uLong nSize ) +{ + if( pStrm ) + { + nSize = pStrm->Read( pData, nSize ); + SetError( pStrm->GetError() ); + return nSize; + } + else + return 0; +} + +sal_uLong StgInternalStream::PutData( const void* pData, sal_uLong nSize ) +{ + if( pStrm ) + { + nSize = pStrm->Write( pData, nSize ); + SetError( pStrm->GetError() ); + return nSize; + } + else + return 0; +} + +sal_uLong StgInternalStream::SeekPos( sal_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, sal_Bool bWr ) + : StgInternalStream( rStg, String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "\1CompObj" ) ), bWr ) +{ + memset( &aClsId, 0, sizeof( ClsId ) ); + nCbFormat = 0; +} + +sal_Bool StgCompObjStream::Load() +{ + memset( &aClsId, 0, sizeof( ClsId ) ); + nCbFormat = 0; + aUserName.Erase(); + if( GetError() != SVSTREAM_OK ) + return sal_False; + Seek( 8L ); // skip the first part + sal_Int32 nMarker = 0; + *this >> nMarker; + if( nMarker == -1L ) + { + *this >> aClsId; + sal_Int32 nLen1 = 0; + *this >> nLen1; + // higher bits are ignored + nLen1 &= 0xFFFF; + sal_Char* p = new sal_Char[ (sal_uInt16) nLen1 ]; + if( Read( p, nLen1 ) == (sal_uLong) nLen1 ) + { + //The encoding here is "ANSI", which is pretty useless seeing as + //the actual codepage used doesn't seem to be specified/stored + //anywhere :-(. Might as well pick 1252 and be consistent on + //all platforms and envs + //http://www.openoffice.org/nonav/issues/showattachment.cgi/68668/Orginal%20Document.doc + //for a good edge-case example + aUserName = nLen1 ? String( p, RTL_TEXTENCODING_MS_1252 ) : String(); + nCbFormat = ReadClipboardFormat( *this ); + } + else + SetError( SVSTREAM_GENERALERROR ); + delete [] p; + } + return sal_Bool( GetError() == SVSTREAM_OK ); +} + +sal_Bool StgCompObjStream::Store() +{ + if( GetError() != SVSTREAM_OK ) + return sal_False; + Seek( 0L ); + ByteString aAsciiUserName( aUserName, RTL_TEXTENCODING_MS_1252 ); + *this << (sal_Int16) 1 // Version? + << (sal_Int16) -2 // 0xFFFE = Byte Order Indicator + << (sal_Int32) 0x0A03 // Windows 3.10 + << (sal_Int32) -1L + << aClsId // Class ID + << (sal_Int32) (aAsciiUserName.Len() + 1) + << (const char *)aAsciiUserName.GetBuffer() + << (sal_uInt8) 0; // string terminator + WriteClipboardFormat( *this, nCbFormat ); + *this << (sal_Int32) 0; // terminator + Commit(); + return sal_Bool( GetError() == SVSTREAM_OK ); +} + +/////////////////////////// class StgOleStream /////////////////////////// + +StgOleStream::StgOleStream( BaseStorage& rStg, sal_Bool bWr ) + : StgInternalStream( rStg, String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "\1Ole" ) ), bWr ) +{ + nFlags = 0; +} + +sal_Bool StgOleStream::Load() +{ + nFlags = 0; + if( GetError() != SVSTREAM_OK ) + return sal_False; + sal_Int32 version = 0; + Seek( 0L ); + *this >> version >> nFlags; + return sal_Bool( GetError() == SVSTREAM_OK ); +} + +sal_Bool StgOleStream::Store() +{ + if( GetError() != SVSTREAM_OK ) + return sal_False; + Seek( 0L ); + *this << (sal_Int32) 0x02000001 // OLE version, format + << (sal_Int32) nFlags // Object flags + << (sal_Int32) 0 // Update Options + << (sal_Int32) 0 // reserved + << (sal_Int32) 0; // Moniker 1 + Commit(); + return sal_Bool( GetError() == SVSTREAM_OK ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgole.hxx b/sot/source/sdstor/stgole.hxx new file mode 100644 index 000000000000..bf936b6ad734 --- /dev/null +++ b/sot/source/sdstor/stgole.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/stg.hxx" +#include "stgelem.hxx" + +class StgInternalStream : public SvStream +{ + BaseStorageStream* pStrm; + virtual sal_uLong GetData( void* pData, sal_uLong nSize ); + virtual sal_uLong PutData( const void* pData, sal_uLong nSize ); + virtual sal_uLong SeekPos( sal_uLong nPos ); + virtual void FlushData(); +public: + StgInternalStream( BaseStorage&, const String&, sal_Bool ); + ~StgInternalStream(); + void Commit(); +}; + +// standard stream "\1CompObj" + +class StgCompObjStream : public StgInternalStream +{ + ClsId aClsId; + String aUserName; + sal_uLong nCbFormat; +public: + StgCompObjStream( BaseStorage&, sal_Bool ); + ClsId& GetClsId() { return aClsId; } + String& GetUserName() { return aUserName; } + sal_uLong& GetCbFormat() { return nCbFormat; } + sal_Bool Load(); + sal_Bool Store(); +}; + +// standard stream "\1Ole" + +class StgOleStream : public StgInternalStream +{ + sal_uInt32 nFlags; +public: + StgOleStream( BaseStorage&, sal_Bool ); + sal_uInt32& GetFlags() { return nFlags; } + sal_Bool Load(); + sal_Bool Store(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgstrms.cxx b/sot/source/sdstor/stgstrms.cxx new file mode 100644 index 000000000000..48e8de3ede6d --- /dev/null +++ b/sot/source/sdstor/stgstrms.cxx @@ -0,0 +1,1243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "sot/stg.hxx" +#include "stgelem.hxx" +#include "stgcache.hxx" +#include "stgstrms.hxx" +#include "stgdir.hxx" +#include "stgio.hxx" + +#define __HUGE + +///////////////////////////// class StgFAT /////////////////////////////// + +// The FAT class performs FAT operations on an underlying storage stream. +// This stream is either the master FAT stream (m == sal_True ) or a normal +// storage stream, which then holds the FAT for small data allocations. + +StgFAT::StgFAT( StgStrm& r, sal_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( sal_Int32 nByteOff ) +{ + StgPage* pPg = NULL; + // Position within the underlying stream + // use the Pos2Page() method of the stream + if( rStrm.Pos2Page( nByteOff ) ) + { + nOffset = rStrm.GetOffset(); + sal_Int32 nPhysPage = rStrm.GetPage(); + // get the physical page (must be present) + pPg = rStrm.GetIo().Get( nPhysPage, sal_True ); + } + return pPg; +} + +// Get the follow page for a certain FAT page. + +sal_Int32 StgFAT::GetNextPage( sal_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. + +sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs ) +{ + sal_Int32 nMinStart = STG_EOF, nMinLen = 0; + sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL; + sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0; + sal_Int32 nPages = rStrm.GetSize() >> 2; + sal_Bool bFound = sal_False; + StgPage* pPg = NULL; + short nEntry = 0; + for( sal_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; + } + sal_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 = sal_True; + else if( nTmpLen >= nMinLen ) + { + // block < requested size + nMinLen = nTmpLen, nMinStart = nTmpStart; + bFound = sal_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 + sal_Int32 nMinDiff = nPgs - nMinLen; + sal_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. + +sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs ) +{ + sal_Int32 nPos = nStart << 2; + StgPage* pPg = GetPhysPage( nPos ); + if( !pPg || !nPgs ) + return sal_False; + while( --nPgs ) + { + if( nOffset >= nPageSize ) + { + pPg = GetPhysPage( nPos ); + if( !pPg ) + return sal_False; + } + pPg->SetPage( nOffset >> 2, ++nStart ); + nOffset += 4; + nPos += 4; + } + if( nOffset >= nPageSize ) + { + pPg = GetPhysPage( nPos ); + if( !pPg ) + return sal_False; + } + pPg->SetPage( nOffset >> 2, STG_EOF ); + return sal_True; +} + +// Allocate a block of data from the given page number on. +// It the page number is != STG_EOF, chain the block. + +sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs ) +{ + sal_Int32 nOrig = nBgn; + sal_Int32 nLast = nBgn; + sal_Int32 nBegin = STG_EOF; + sal_Int32 nAlloc; + sal_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 sal_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 + +sal_Bool StgFAT::InitNew( sal_Int32 nPage1 ) +{ + sal_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 sal_False; + for( short i = 0; i < nEntries; i++ ) + pPg->SetPage( i, STG_FREE ); + nPage1++; + } + return sal_True; +} + +// Release a chain + +sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll ) +{ + while( nStart >= 0 ) + { + StgPage* pPg = GetPhysPage( nStart << 2 ); + if( !pPg ) + return sal_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 = sal_True; + } + return sal_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. + +sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos ) +{ + sal_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; + sal_Int32 nMask = ~( nPageSize - 1 ); + sal_Int32 nOld = nPos & nMask; + sal_Int32 nNew = nBytePos & nMask; + nOffset = (short) ( nBytePos & ~nMask ); + nPos = nBytePos; + if( nOld == nNew ) + return sal_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; + sal_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 sal_Bool( nRel == 0 && nPage >= 0 ); +} + +// Retrieve the physical page for a given byte offset. + +StgPage* StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_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. + +sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes ) +{ + sal_Int32 nTo = nStart; + sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize; + while( nPgs-- ) + { + if( nTo < 0 ) + { + rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return sal_False; + } + rIo.Copy( nTo, nFrom ); + if( nFrom >= 0 ) + { + nFrom = pFat->GetNextPage( nFrom ); + if( nFrom < 0 ) + { + rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return sal_False; + } + } + nTo = pFat->GetNextPage( nTo ); + } + return sal_True; +} + +sal_Bool StgStrm::SetSize( sal_Int32 nBytes ) +{ + // round up to page size + sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize; + sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize; + if( nNew > nOld ) + { + if( !Pos2Page( nSize ) ) + return sal_False; + sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize ); + if( nBgn == STG_EOF ) + return sal_False; + if( nStart == STG_EOF ) + nStart = nPage = nBgn; + } + else if( nNew < nOld ) + { + sal_Bool bAll = sal_Bool( nBytes == 0 ); + if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) ) + return sal_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 sal_True; +} + +// Return the # of allocated pages + +sal_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, sal_True ); + nSize = rIo.aHdr.GetFATSize() * nPageSize; +} + +sal_Bool StgFATStrm::Pos2Page( sal_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, sal_False ); + return sal_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( sal_Int32 nBytePos, sal_Bool bForce ) +{ + return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce ); +} + +// Get the page number entry for the given page offset. + +sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc ) +{ + if( pnMasterAlloc ) *pnMasterAlloc = 0; + if( nOff < rIo.aHdr.GetFAT1Size() ) + return rIo.aHdr.GetFATPage( nOff ); + sal_Int32 nMaxPage = nSize >> 2; + nOff = nOff - rIo.aHdr.GetFAT1Size(); + // Anzahl der Masterpages, durch die wir iterieren muessen + sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1; + sal_uInt16 nBlocks = nOff / nMasterCount; + // Offset in letzter Masterpage + nOff = nOff % nMasterCount; + + StgPage* pOldPage = 0; + StgPage* pMaster = 0; + sal_Int32 nFAT = rIo.aHdr.GetFATChain(); + for( sal_uInt16 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, sal_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, sal_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. + +sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage ) +{ + sal_Bool bRes = sal_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 + sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1; + sal_uInt16 nBlocks = nOff / nMasterCount; + // Offset in letzter Masterpage + nOff = nOff % nMasterCount; + + StgPage* pMaster = 0; + sal_Int32 nFAT = rIo.aHdr.GetFATChain(); + for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ ) + { + if( nFAT == STG_EOF || nFAT == STG_FREE ) + { + pMaster = 0; + break; + } + pMaster = rIo.Get( nFAT, sal_True ); + if ( pMaster ) + nFAT = pMaster->GetPage( nMasterCount ); + } + if( pMaster ) + pMaster->SetPage( nOff, nNewPage ); + else + { + rIo.SetError( SVSTREAM_GENERALERROR ); + bRes = sal_False; + } + } + + // lock the page against access + if( bRes ) + { + Pos2Page( nNewPage << 2 ); + StgPage* pPg = rIo.Get( nPage, sal_True ); + if( pPg ) + pPg->SetPage( nOffset >> 2, STG_FAT ); + else + bRes = sal_False; + } + return bRes; +} + +sal_Bool StgFATStrm::SetSize( sal_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 + sal_Int32 nPg = 0; + sal_uInt16 nMasterAlloc = 0; + nPg = GetPage( nOld, sal_True, &nMasterAlloc ); + if( nPg == STG_EOF ) + return sal_False; + // 4 Bytes have been used for Allocation of each MegaMasterPage + nBytes += nMasterAlloc << 2; + + // find a free page using the FAT allocator + sal_Int32 n = 1; + sal_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 sal_False; + } + // Set up the page with empty entries + StgPage* pPg = rIo.Copy( nNewPage, STG_FREE ); + if ( !pPg ) + return sal_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 + + sal_uInt32 nMax = rIo.aHdr.GetMasters( ); + sal_uInt32 nFAT = rIo.aHdr.GetFATChain(); + if( nMasterAlloc ) + for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ ) + { + if( !Pos2Page( nFAT << 2 ) ) + return sal_False; + if( nMax - nCount <= nMasterAlloc ) + { + StgPage* piPg = rIo.Get( nPage, sal_True ); + if( !piPg ) + return sal_False; + piPg->SetPage( nOffset >> 2, STG_MASTER ); + } + StgPage* pPage = rIo.Get( nFAT, sal_True ); + if( !pPage ) return sal_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 sal_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, sal_Int32 nBgn, sal_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( sal_Int32 nBgn, sal_Int32 nLen ) +{ + pFat = new StgFAT( *rIo.pFAT, sal_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; + sal_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. + +sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes ) +{ + nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr; + sal_Int32 nOldSz = nSize; + if( ( nOldSz != nBytes ) ) + { + if( !StgStrm::SetSize( nBytes ) ) + return sal_False; + sal_Int32 nMaxPage = pFat->GetMaxPage(); + if( nMaxPage > rIo.GetPhysPages() ) + if( !rIo.SetSize( nMaxPage ) ) + return sal_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 sal_True; +} + +// Get the address of the data byte at a specified offset. +// If bForce = sal_True, a read of non-existent data causes +// a read fault. + +void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty ) +{ + if( Pos2Page( Pos ) ) + { + StgPage* pPg = rIo.Get( nPage, bForce ); + if (pPg && nOffset < pPg->GetSize()) + { + pPg->SetOwner( pEntry ); + if( bDirty ) + pPg->SetDirty(); + return ((sal_uInt8 *)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. + +sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n ) +{ + if ( n < 0 ) + return 0; + + if( ( nPos + n ) > nSize ) + n = nSize - nPos; + sal_Int32 nDone = 0; + while( n ) + { + short nBytes = nPageSize - nOffset; + StgPage* pPg; + if( (sal_Int32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + short nRes; + void *p = (sal_uInt8 *) 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, sal_False ); + if( !pPg ) + break; + pPg->SetOwner( pEntry ); + memcpy( p, (sal_uInt8*)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; +} + +sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n ) +{ + sal_Int32 nDone = 0; + if( ( nPos + n ) > nSize ) + { + sal_Int32 nOld = nPos; + if( !SetSize( nPos + n ) ) + return sal_False; + Pos2Page( nOld ); + } + while( n ) + { + short nBytes = nPageSize - nOffset; + StgPage* pPg; + if( (sal_Int32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + short nRes; + const void *p = (const sal_uInt8 *) 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, sal_False ); + if( !pPg ) + break; + pPg->SetOwner( pEntry ); + memcpy( (sal_uInt8*)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, sal_Int32 nBgn, sal_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( sal_Int32 nBgn, sal_Int32 nLen ) +{ + pFat = new StgFAT( *rIo.pDataFAT, sal_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. + +sal_Int32 StgSmallStrm::Read( void* pBuf, sal_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( (sal_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( (sal_uInt8*)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; +} + +sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_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 ) + { + sal_Int32 nOld = nPos; + if( !SetSize( nPos + n ) ) + return sal_False; + Pos2Page( nOld ); + } + while( n ) + { + short nBytes = nPageSize - nOffset; + if( (sal_Int32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + // all writing goes thru the stream + sal_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( (sal_uInt8*)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( sal_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 ); +} + +sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc ) +{ + sal_uLong n = rSrc.GetSize(); + sal_uLong nCur = rSrc.Tell(); + SetSize( n ); + if( GetError() == SVSTREAM_OK ) + { + sal_uInt8* p = new sal_uInt8[ 4096 ]; + rSrc.Seek( 0L ); + Seek( 0L ); + while( n ) + { + sal_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 sal_Bool( n == 0 ); + } + else + return sal_False; +} + +StgTmpStrm::~StgTmpStrm() +{ + if( pStrm ) + { + pStrm->Close(); + osl::File::remove( aName ); + delete pStrm; + } +} + +sal_uLong StgTmpStrm::GetSize() const +{ + sal_uLong n; + if( pStrm ) + { + sal_uLong old = pStrm->Tell(); + n = pStrm->Seek( STREAM_SEEK_TO_END ); + pStrm->Seek( old ); + } + else + n = nEndOfData; + return n; +} + +void StgTmpStrm::SetSize( sal_uLong n ) +{ + if( pStrm ) + pStrm->SetStreamSize( n ); + else + { + if( n > THRESHOLD ) + { + aName = TempFile::CreateTempName(); + SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE ); + sal_uLong nCur = Tell(); + sal_uLong i = nEndOfData; + if( i ) + { + sal_uInt8* p = new sal_uInt8[ 4096 ]; + Seek( 0L ); + while( i ) + { + sal_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 ) + { + sal_uLong nCur = Tell(); + Seek( nEndOfData - 1 ); + *this << (sal_uInt8) 0; + Seek( nCur ); + } + else + nEndOfData = n; + } + } +} + +sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n ) +{ + if( pStrm ) + { + n = pStrm->Read( pData, n ); + SetError( pStrm->GetError() ); + return n; + } + else + return SvMemoryStream::GetData( (sal_Char *)pData, n ); +} + +sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n ) +{ + sal_uInt32 nCur = Tell(); + sal_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; +} + +sal_uLong StgTmpStrm::SeekPos( sal_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(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/stgstrms.hxx b/sot/source/sdstor/stgstrms.hxx new file mode 100644 index 000000000000..a6a0ad108da9 --- /dev/null +++ b/sot/source/sdstor/stgstrms.hxx @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + +#include <tools/stream.hxx> + +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 == sal_True ) or a normal +// storage stream, which then holds the FAT for small data allocations. + +class StgFAT +{ // FAT allocator + StgStrm& rStrm; // underlying stream + sal_Int32 nMaxPage; // highest page allocated so far + short nPageSize; // physical page size + short nEntries; // FAT entries per page + short nOffset; // current offset within page + sal_Int32 nLimit; // search limit recommendation + sal_Bool bPhys; // sal_True: physical FAT + StgPage* GetPhysPage( sal_Int32 nPage ); + sal_Bool MakeChain( sal_Int32 nStart, sal_Int32 nPages ); + sal_Bool InitNew( sal_Int32 nPage1 ); +public: + StgFAT( StgStrm& rStrm, sal_Bool bMark ); + sal_Int32 FindBlock( sal_Int32& nPages ); + sal_Int32 GetNextPage( sal_Int32 nPg ); + sal_Int32 AllocPages( sal_Int32 nStart, sal_Int32 nPages ); + sal_Bool FreePages( sal_Int32 nStart, sal_Bool bAll ); + sal_Int32 GetMaxPage() { return nMaxPage; } + void SetLimit( sal_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) + sal_Int32 nStart; // 1st data page + sal_Int32 nSize; // stream size in bytes + sal_Int32 nPos; // current byte position + sal_Int32 nPage; // current logical page + short nOffset; // offset into current page + short nPageSize; // logical page size + sal_Bool Copy( sal_Int32 nFrom, sal_Int32 nBytes ); + StgStrm( StgIo& ); +public: + virtual ~StgStrm(); + StgIo& GetIo() { return rIo; } + sal_Int32 GetPos() { return nPos; } + sal_Int32 GetStart() { return nStart; } + sal_Int32 GetSize() { return nSize; } + sal_Int32 GetPage() { return nPage; } + short GetPageSize() { return nPageSize; } + sal_Int32 GetPages(); + short GetOffset() { return nOffset;} + void SetEntry( StgDirEntry& ); + virtual sal_Bool SetSize( sal_Int32 ); + virtual sal_Bool Pos2Page( sal_Int32 nBytePos ); + virtual sal_Int32 Read( void*, sal_Int32 ) { return 0; } + virtual sal_Int32 Write( const void*, sal_Int32 ) { return 0; } + virtual StgPage* GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce = sal_False ); + virtual sal_Bool IsSmallStrm() { return sal_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 sal_Bool Pos2Page( sal_Int32 nBytePos ); + sal_Bool SetPage( short, sal_Int32 ); +public: + StgFATStrm( StgIo& ); + virtual ~StgFATStrm() {} + using StgStrm::GetPage; + sal_Int32 GetPage( short, sal_Bool, sal_uInt16 *pnMasterAlloc = 0); + virtual sal_Bool SetSize( sal_Int32 ); + virtual StgPage* GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce = sal_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( sal_Int32 nBgn, sal_Int32 nLen ); +public: + StgDataStrm( StgIo&, sal_Int32 nBgn, sal_Int32 nLen=-1 ); + StgDataStrm( StgIo&, StgDirEntry* ); + void* GetPtr( sal_Int32 nPos, sal_Bool bForce, sal_Bool bDirty ); + void SetIncrement( short n ) { nIncr = n ; } + virtual sal_Bool SetSize( sal_Int32 ); + virtual sal_Int32 Read( void*, sal_Int32 ); + virtual sal_Int32 Write( const void*, sal_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( sal_Int32 nBgn, sal_Int32 nLen ); +public: + StgSmallStrm( StgIo&, sal_Int32 nBgn, sal_Int32 nLen ); + StgSmallStrm( StgIo&, StgDirEntry* ); + virtual sal_Int32 Read( void*, sal_Int32 ); + virtual sal_Int32 Write( const void*, sal_Int32 ); + virtual sal_Bool IsSmallStrm() { return sal_True; } +}; + +class StgTmpStrm : public SvMemoryStream +{ + String aName; + SvFileStream* pStrm; + using SvMemoryStream::GetData; + virtual sal_uLong GetData( void* pData, sal_uLong nSize ); + virtual sal_uLong PutData( const void* pData, sal_uLong nSize ); + virtual sal_uLong SeekPos( sal_uLong nPos ); + virtual void FlushData(); + +public: + StgTmpStrm( sal_uLong=16 ); + ~StgTmpStrm(); + sal_Bool Copy( StgTmpStrm& ); + void SetSize( sal_uLong ); + sal_uLong GetSize() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/storage.cxx b/sot/source/sdstor/storage.cxx new file mode 100644 index 000000000000..ef398d081904 --- /dev/null +++ b/sot/source/sdstor/storage.cxx @@ -0,0 +1,1560 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <sot/stg.hxx> +#include <sot/storinfo.hxx> +#include <sot/storage.hxx> +#include <sot/formats.hxx> +#include <sot/exchange.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <tools/fsys.hxx> +#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( sal_Bool /*bFree*/ ) +{ +} + +#ifdef TEST_INVARIANT +void SotStorageStream::TestMemberInvariant( sal_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, sal_True ); + } + else + { + SvStream * pCacheStm = new SvCacheStream(); + xLB = new SvLockBytes( pCacheStm, sal_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 = sal_True; + else + bIsWritable = sal_False; + + DBG_ASSERT( !nStorageMode,"StorageModes ignored" ); +} + +SotStorageStream::SotStorageStream( BaseStorageStream * pStm ) +{ + if( pStm ) + { + if( STREAM_WRITE & pStm->GetMode() ) + bIsWritable = sal_True; + else + bIsWritable = sal_False; + + pOwnStm = pStm; + SetError( pStm->GetError() ); + pStm->ResetError(); + } + else + { + pOwnStm = NULL; + bIsWritable = sal_True; + SetError( SVSTREAM_INVALID_PARAMETER ); + } +} + +SotStorageStream::SotStorageStream() + : pOwnStm( NULL ) +{ + // ??? wenn Init virtuell ist, entsprechen setzen + bIsWritable = sal_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() +{ + if( pOwnStm ) + { + pOwnStm->Flush(); + sal_uLong nPos = pOwnStm->Tell(); + SetError( pOwnStm->GetError() ); + SvStream::SyncSvStream( nPos ); + } +} + +/************************************************************************* +|* SotStorageStream::ResetError() +|* +|* Beschreibung +*************************************************************************/ +void SotStorageStream::ResetError() +{ + SvStream::ResetError(); + if( pOwnStm ) + pOwnStm->ResetError(); +} + +/************************************************************************* +|* SotStorageStream::GetData() +|* +|* Beschreibung +*************************************************************************/ +sal_uLong SotStorageStream::GetData( void* pData, sal_uLong nSize ) +{ + sal_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 +*************************************************************************/ +sal_uLong SotStorageStream::PutData( const void* pData, sal_uLong nSize ) +{ + sal_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 +*************************************************************************/ +sal_uLong SotStorageStream::SeekPos( sal_uLong nPos ) +{ + sal_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( sal_uLong nNewSize ) +{ + sal_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 +|* +*************************************************************************/ +sal_uInt32 SotStorageStream::GetSize() const +{ + sal_uLong nPos = Tell(); + ((SotStorageStream *)this)->Seek( STREAM_SEEK_TO_END ); + sal_uLong nSize = Tell(); + ((SotStorageStream *)this)->Seek( nPos ); + return nSize; +} + +/************************************************************************* +|* SotStorageStream::CopyTo() +|* +|* Beschreibung +*************************************************************************/ +sal_Bool SotStorageStream::CopyTo( SotStorageStream * pDestStm ) +{ + Flush(); // alle Daten schreiben + pDestStm->ClearBuffer(); + if( !pOwnStm || !pDestStm->pOwnStm ) + { // Wenn Ole2 oder nicht nur eigene StorageStreams + + sal_uLong nPos = Tell(); // Position merken + Seek( 0L ); + pDestStm->SetSize( 0 ); // Ziel-Stream leeren + + void * pMem = new sal_uInt8[ 8192 ]; + sal_uLong nRead; + while( 0 != (nRead = Read( pMem, 8192 )) ) + { + if( nRead != pDestStm->Write( pMem, nRead ) ) + { + SetError( SVSTREAM_GENERALERROR ); + break; + } + } + delete [] static_cast<sal_uInt8*>(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 +*************************************************************************/ +sal_Bool SotStorageStream::Commit() +{ + if( pOwnStm ) + { + pOwnStm->Flush(); + if( pOwnStm->GetError() == SVSTREAM_OK ) + pOwnStm->Commit(); + SetError( pOwnStm->GetError() ); + } + return GetError() == SVSTREAM_OK; +} + +sal_Bool SotStorageStream::Revert() +{ + if( !pOwnStm ) + { + pOwnStm->Revert(); + SetError( pOwnStm->GetError() ); + } + return GetError() == SVSTREAM_OK; +} + +sal_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 + { + OSL_FAIL("Not implemented!"); + return sal_False; + } +} + +sal_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 + { + OSL_FAIL("Not implemented!"); + return sal_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 + { + OSL_FAIL("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( sal_Bool /*bFree*/ ) +{ +} + +#ifdef TEST_INVARIANT +void SotStorage::TestMemberInvariant( sal_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() == sal_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. +|* +*************************************************************************/ +#define INIT_SotStorage() \ + : m_pOwnStg( NULL ) \ + , m_pStorStm( NULL ) \ + , m_nError( SVSTREAM_OK ) \ + , m_bIsRoot( sal_False ) \ + , m_bDelStm( sal_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) ? sal_False : sal_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( sal_True, nMode, nStorageMode ); + if ( IsOLEStorage() ) + m_nVersion = SOFFICE_FILEFORMAT_50; +} + +void SotStorage::CreateStorage( sal_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, sal_False ); + } + else + { + m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_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 + sal_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) ? sal_False : sal_True ); + m_bDelStm = sal_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) ? sal_False : sal_True ); + } + } + else + { + // OLEStorage can be opened with a stream + m_pOwnStg = new Storage( *m_pStorStm, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_True ); + m_bDelStm = sal_True; + } + } + else if ( bForceUCBStorage ) + { + m_pOwnStg = new UCBStorage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_True ); + SetError( ERRCODE_IO_NOTSUPPORTED ); + } + else + { + m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_True ); + SetError( ERRCODE_IO_NOTSUPPORTED ); + } + } + } + else + { + // temporary storage + if ( bForceUCBStorage ) + m_pOwnStg = new UCBStorage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_True ); + else + m_pOwnStg = new Storage( m_aName, nMode, (nStorageMode & STORAGE_TRANSACTED) ? sal_False : sal_True ); + m_aName = m_pOwnStg->GetName(); + } + + SetError( m_pOwnStg->GetError() ); + + SignAsRoot( m_pOwnStg->IsRoot() ); +} + +SotStorage::SotStorage( sal_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; + sal_uLong nErr = m_pOwnStg ? m_pOwnStg->GetError() : SVSTREAM_CANNOT_MAKE; + SetError( nErr ); + if ( IsOLEStorage() ) + m_nVersion = SOFFICE_FILEFORMAT_50; +} + +SotStorage::SotStorage( sal_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, sal_False ); + else + m_pOwnStg = new Storage( rStm, sal_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, sal_False ); + else + m_pOwnStg = new Storage( rStm, sal_False ); + + SetError( m_pOwnStg->GetError() ); + + if ( IsOLEStorage() ) + m_nVersion = SOFFICE_FILEFORMAT_50; + + SignAsRoot( m_pOwnStg->IsRoot() ); +} + +SotStorage::SotStorage( SvStream * pStm, sal_Bool bDelete ) + INIT_SotStorage() +{ + SetError( pStm->GetError() ); + + // try as UCBStorage, next try as OLEStorage + if ( UCBStorage::IsStorageFile( pStm ) ) + m_pOwnStg = new UCBStorage( *pStm, sal_False ); + else + m_pOwnStg = new Storage( *pStm, sal_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 + { + OSL_FAIL("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, sal_False, sal_True ); + if ( pTempStorage ) + { + pChildUCBStg->CopyTo( pTempStorage ); + + // CopyTo does not transport unknown media type + // just workaround it + uno::Any aMediaType; + + if ( pChildUCBStg->GetProperty( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")), aMediaType ) ) + pTempStorage->SetProperty( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("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_FAIL( ::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 +*************************************************************************/ +sal_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 ); + sal_Bool bRet = SotStorage::IsStorageFile( pStm ); + delete pStm; + return bRet; +} + +sal_Bool SotStorage::IsStorageFile( SvStream* pStream ) +{ + /** code for new storages must come first! **/ + if ( pStream ) + { + long nPos = pStream->Tell(); + sal_Bool bRet = UCBStorage::IsStorageFile( pStream ); + if ( !bRet ) + bRet = Storage::IsStorageFile( pStream ); + pStream->Seek( nPos ); + return bRet; + } + else + return sal_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, + sal_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, + sal_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; +} + +sal_uLong SotStorage::GetFormat() +{ + sal_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; +} + +sal_Bool SotStorage::ShouldConvert() +{ + DBG_ASSERT( Owner(), "must be owner" ); + if( m_pOwnStg ) + return m_pOwnStg->ShouldConvert(); + else + SetError( SVSTREAM_GENERALERROR ); + return sal_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 +*************************************************************************/ +sal_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 +*************************************************************************/ +sal_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 +*************************************************************************/ +sal_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) ? sal_False : sal_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) ? sal_False : sal_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) ? sal_False : sal_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) ? sal_False : sal_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) ? sal_False : sal_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 +*************************************************************************/ +sal_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 sal_False; +} + +sal_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 sal_False; +} + +sal_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 sal_False; +} + +/************************************************************************* +|* SotStorage::Remove() +|* +|* Beschreibung +*************************************************************************/ +sal_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 +*************************************************************************/ +sal_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 +*************************************************************************/ +sal_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 +*************************************************************************/ +sal_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; +} + + +sal_Bool SotStorage::Validate() +{ + DBG_ASSERT( m_bIsRoot, "Validate nur an Rootstorage" ); + if( m_pOwnStg ) + return m_pOwnStg->ValidateFAT(); + else + return sal_True; +} + +sal_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 sal_False; + } +} + +sal_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() ); + sal_uInt16 nPos = aStr.Search(';'); + if ( nPos != STRING_NOTFOUND ) + aStr = aStr.Copy( 0, nPos ); + rValue <<= (::rtl::OUString) aStr; + return sal_True; + } + else + { + DBG_WARNING("W1:Not implemented!"); + return sal_False; + } +} + +sal_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 sal_False; + } +} + +sal_Bool SotStorage::IsOLEStorage() const +{ + UCBStorage* pStg = PTR_CAST( UCBStorage, m_pOwnStg ); + return !pStg; +} + +sal_Bool SotStorage::IsOLEStorage( const String & rFileName ) +{ + return Storage::IsStorageFile( rFileName ); +} + +sal_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(RTL_CONSTASCII_USTRINGPARAM("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, sal_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(RTL_CONSTASCII_USTRINGPARAM("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; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/storinfo.cxx b/sot/source/sdstor/storinfo.cxx new file mode 100644 index 000000000000..1cdde1a45104 --- /dev/null +++ b/sot/source/sdstor/storinfo.cxx @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <sot/stg.hxx> +#include <sot/storinfo.hxx> +#include <sot/exchange.hxx> + + +/************** class SvStorageInfoList ********************************** +*************************************************************************/ +PRV_SV_IMPL_OWNER_LIST(SvStorageInfoList,SvStorageInfo) + +const SvStorageInfo * SvStorageInfoList::Get( const String & rEleName ) +{ + for( sal_uLong i = 0; i < Count(); i++ ) + { + const SvStorageInfo & rType = GetObject( i ); + if( rType.GetName() == rEleName ) + return &rType; + } + return NULL; +} + +/************** class SvStorageInfo ************************************** +*************************************************************************/ +sal_uLong ReadClipboardFormat( SvStream & rStm ) +{ + sal_uInt32 nFormat = 0; + sal_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 ) == (sal_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, sal_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 << (sal_Int32) (aAsciiCbFmt.Len() + 1); + rStm << (const char *)aAsciiCbFmt.GetBuffer(); + rStm << (sal_uInt8) 0; + } + else if( nFormat ) + rStm << (sal_Int32) -1 // for Windows + << (sal_Int32) nFormat; + else + rStm << (sal_Int32) 0; // no clipboard format +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/ucbstorage.cxx b/sot/source/sdstor/ucbstorage.cxx new file mode 100644 index 000000000000..3fcf8b07cf9d --- /dev/null +++ b/sot/source/sdstor/ucbstorage.cxx @@ -0,0 +1,3539 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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/urlobj.hxx> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase2.hxx> +#include <ucbhelper/commandenvironment.hxx> + +#include "sot/stg.hxx" +#include "sot/storinfo.hxx" +#include <sot/storage.hxx> +#include <sot/exchange.hxx> +#include <sot/formats.hxx> +#include "sot/clsids.hxx" + +#include "unostorageholder.hxx" + +#include <vector> + +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(); + + 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_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 + { + OSL_FAIL( "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 : + 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 sal_uLong GetData( void* pData, sal_uLong nSize ); + virtual sal_uLong PutData( const void* pData, sal_uLong nSize ); + virtual sal_uLong SeekPos( sal_uLong nPos ); + virtual void SetSize( sal_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 ) + sal_Bool m_bSourceRead; // Source still contains useful information + sal_Bool m_bModified; // only modified streams will be sent to the original content + sal_Bool m_bCommited; // sending the streams is coordinated by the root storage of the package + sal_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 + sal_Bool m_bIsOLEStorage;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream + + UCBStorageStream_Impl( const String&, StreamMode, UCBStorageStream*, sal_Bool, const ByteString* pKey=0, sal_Bool bRepair = sal_False, Reference< XProgressHandler > xProgress = Reference< XProgressHandler >() ); + + void Free(); + sal_Bool Init(); + sal_Bool Clear(); + sal_Int16 Commit(); // if modified and commited: transfer an XInputStream to the content + sal_Bool Revert(); // discard all changes + BaseStorage* CreateStorage();// create an OLE Storage on the UCBStorageStream + sal_uLong GetSize(); + + sal_uLong ReadSourceWriteTemporary( sal_uLong aLength ); // read aLength from source and copy to temporary, + // no seeking is produced + sal_uLong ReadSourceWriteTemporary(); // read source till the end and copy to temporary, + + sal_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; +typedef ::std::vector< UCBStorageElement_Impl* > UCBStorageElementList_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 + long m_nError; + StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing ) + sal_Bool m_bModified; // only modified elements will be sent to the original content + sal_Bool m_bCommited; // sending the streams is coordinated by the root storage of the package + sal_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 + sal_Bool m_bIsRoot; // marks this storage as root storages that manages all oommits and reverts + sal_Bool m_bDirty; // ??? + sal_Bool m_bIsLinked; + sal_Bool m_bListCreated; + sal_uLong m_nFormat; + String m_aUserTypeName; + SvGlobalName m_aClassId; + + UCBStorageElementList_Impl m_aChildrenList; + + sal_Bool m_bRepairPackage; + Reference< XProgressHandler > m_xProgressHandler; + + UNOStorageHolderList* m_pUNOStorageHolderList; + UCBStorage_Impl( const ::ucbhelper::Content&, const String&, StreamMode, UCBStorage*, sal_Bool, sal_Bool, sal_Bool = sal_False, Reference< XProgressHandler > = Reference< XProgressHandler >() ); + UCBStorage_Impl( const String&, StreamMode, UCBStorage*, sal_Bool, sal_Bool, sal_Bool = sal_False, Reference< XProgressHandler > = Reference< XProgressHandler >() ); + UCBStorage_Impl( SvStream&, UCBStorage*, sal_Bool ); + void Init(); + sal_Int16 Commit(); + sal_Bool Revert(); + sal_Bool Insert( ::ucbhelper::Content *pContent ); + UCBStorage_Impl* OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, sal_Bool bDirect ); + UCBStorageStream_Impl* OpenStream( UCBStorageElement_Impl*, StreamMode, sal_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 + sal_uLong m_nSize; + sal_Bool m_bIsFolder; // Only sal_True when it is a UCBStorage ! + sal_Bool m_bIsStorage; // Also sal_True when it is an OLEStorage ! + sal_Bool m_bIsRemoved; // element will be removed on commit + sal_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, + sal_Bool bIsFolder = sal_False, sal_uLong nSize = 0 ) + : m_aName( rName ) + , m_aOriginalName( rName ) + , m_nSize( nSize ) + , m_bIsFolder( bIsFolder ) + , m_bIsStorage( bIsFolder ) + , m_bIsRemoved( sal_False ) + , m_bIsInserted( sal_False ) + { + } + + ::ucbhelper::Content* GetContent(); + sal_Bool IsModified(); + String GetContentType(); + void SetContentType( const String& ); + String GetOriginalContentType(); + sal_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 + { + OSL_FAIL("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 { + OSL_FAIL("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(); +} + +sal_Bool UCBStorageElement_Impl::IsModified() +{ + sal_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, sal_Bool bDirect, const ByteString* pKey, sal_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( sal_False ) + , m_bCommited( sal_False ) + , m_bDirect( bDirect ) + , m_bIsOLEStorage( sal_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(RTL_CONSTASCII_USTRINGPARAM("?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(RTL_CONSTASCII_USTRINGPARAM("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 ) + { + OSL_FAIL( "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, sal_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; +} + +sal_Bool UCBStorageStream_Impl::Init() +{ + if( m_nRepresentMode == xinputstream ) + { + OSL_FAIL( "XInputStream misuse!" ); + SetError( ERRCODE_IO_ACCESSDENIED ); + return sal_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 ) + { + OSL_FAIL( "Suspicious temporary stream creation!" ); + SetError( SVSTREAM_CANNOT_MAKE ); + return sal_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 = sal_False; + } + catch( Exception& ) + { + // something is really wrong + m_bSourceRead = sal_False; + OSL_FAIL( "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 = sal_False; + //SetError( SVSTREAM_CANNOT_MAKE ); + } + } + + DBG_ASSERT( m_rSource.is() || !m_bSourceRead, "Unreadable source stream!" ); + + return sal_True; +} + +sal_uLong UCBStorageStream_Impl::ReadSourceWriteTemporary() +{ + // read source stream till the end and copy all the data to + // the current position of the temporary stream + + sal_uLong aResult = 0; + + if( m_bSourceRead ) + { + Sequence<sal_Int8> aData(32000); + + try + { + sal_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_FAIL( ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + } + + m_bSourceRead = sal_False; + + return aResult; + +} + +sal_uLong UCBStorageStream_Impl::ReadSourceWriteTemporary( sal_uLong aLength ) +{ + // read aLength bite from the source stream and copy them to the current + // position of the temporary stream + + sal_uLong aResult = 0; + + if( m_bSourceRead ) + { + Sequence<sal_Int8> aData(32000); + + try + { + + sal_uLong aReaded = 32000; + + for( sal_uLong pInd = 0; pInd < aLength && aReaded == 32000 ; pInd += 32000 ) + { + sal_uLong aToCopy = min( aLength - pInd, 32000 ); + aReaded = m_rSource->readBytes( aData, aToCopy ); + aResult += m_pStream->Write( aData.getArray(), aReaded ); + } + + if( aResult < aLength ) + m_bSourceRead = sal_False; + } +#if OSL_DEBUG_LEVEL > 1 + catch( Exception & e ) + { + OSL_FAIL( ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + } + + return aResult; +} + +sal_uLong UCBStorageStream_Impl::CopySourceToTemporary() +{ + // current position of the temporary stream is not changed + sal_uLong aResult = 0; + + if( m_bSourceRead ) + { + sal_uLong aPos = m_pStream->Tell(); + m_pStream->Seek( STREAM_SEEK_TO_END ); + aResult = ReadSourceWriteTemporary(); + m_pStream->Seek( aPos ); + } + + return aResult; + +} + +// 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 +sal_uLong UCBStorageStream_Impl::GetData( void* pData, sal_uLong nSize ) +{ + sal_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 + + sal_uLong aToRead = nSize - aResult; + pData = (void*)( (char*)pData + aResult ); + + try + { + Sequence<sal_Int8> aData( aToRead ); + sal_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_FAIL( ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + + if( aResult < nSize ) + m_bSourceRead = sal_False; + } + + return aResult; +} + +sal_uLong UCBStorageStream_Impl::PutData( const void* pData, sal_uLong nSize ) +{ + if ( !(m_nMode & STREAM_WRITE) ) + { + SetError( ERRCODE_IO_ACCESSDENIED ); + return 0; // ?mav? + } + + if( !nSize || !Init() ) + return 0; + + sal_uLong aResult = m_pStream->Write( pData, nSize ); + + m_bModified = aResult > 0; + + return aResult; + +} + +sal_uLong UCBStorageStream_Impl::SeekPos( sal_uLong nPos ) +{ + if( !Init() ) + return 0; + + sal_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 = sal_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( sal_uLong nSize ) +{ + if ( !(m_nMode & STREAM_WRITE) ) + { + SetError( ERRCODE_IO_ACCESSDENIED ); + return; + } + + if( !Init() ) + return; + + m_bModified = sal_True; + + if( m_bSourceRead ) + { + sal_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 = sal_False; +} + +void UCBStorageStream_Impl::FlushData() +{ + if( m_pStream ) + { + CopySourceToTemporary(); + m_pStream->Flush(); + } + + m_bCommited = sal_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(); +} + +sal_uLong UCBStorageStream_Impl::GetSize() +{ + if( !Init() ) + return 0; + + sal_uLong nPos = m_pStream->Tell(); + m_pStream->Seek( STREAM_SEEK_TO_END ); + ReadSourceWriteTemporary(); + sal_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(RTL_CONSTASCII_USTRINGPARAM("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 = sal_False; + m_bSourceRead = sal_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 = sal_False; + return COMMIT_RESULT_SUCCESS; + } + } + + return COMMIT_RESULT_NOTHING_TO_DO; +} + +sal_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 ) + { + OSL_FAIL("Revert while commit is in progress!" ); + return sal_False; // ??? + } + + Free(); + if ( m_aTempURL.Len() ) + { + ::utl::UCBContentHelper::Kill( m_aTempURL ); + m_aTempURL.Erase(); + } + + m_bSourceRead = sal_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 = sal_False; + else + { + m_nMode &= ~STREAM_TRUNC; + m_bSourceRead = sal_True; + } + } + else + SetError( SVSTREAM_CANNOT_MAKE ); + } + catch ( ContentCreationException& ) + { + SetError( ERRCODE_IO_GENERAL ); + } + catch ( RuntimeException& ) + { + SetError( ERRCODE_IO_GENERAL ); + } + catch ( Exception& ) + { + } + + m_bModified = sal_False; + m_aName = m_aOriginalName; + m_aContentType = m_aOriginalContentType; + return ( GetError() == ERRCODE_NONE ); +} + +sal_Bool UCBStorageStream_Impl::Clear() +{ + sal_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, sal_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, sal_Bool bDirect, const ByteString* pKey, sal_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(); +} + +sal_uLong UCBStorageStream::Read( void * pData, sal_uLong nSize ) +{ + //return pImp->m_pStream->Read( pData, nSize ); + return pImp->GetData( pData, nSize ); +} + +sal_uLong UCBStorageStream::Write( const void* pData, sal_uLong nSize ) +{ +/* + // mba: does occur in writer ! + if ( pImp->m_bCommited ) + { + OSL_FAIL("Writing while commit is in progress!" ); + return 0; + } +*/ + // pImp->m_bModified = sal_True; + //return pImp->m_pStream->Write( pData, nSize ); + return pImp->PutData( pData, nSize ); +} + +sal_uLong UCBStorageStream::Seek( sal_uLong nPos ) +{ + //return pImp->m_pStream->Seek( nPos ); + return pImp->Seek( nPos ); +} + +sal_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(); +} + +sal_Bool UCBStorageStream::SetSize( sal_uLong nNewSize ) +{ +/* + if ( pImp->m_bCommited ) + { + OSL_FAIL("Changing stream size while commit is in progress!" ); + return sal_False; + } +*/ + // pImp->m_bModified = sal_True; + //return pImp->m_pStream->SetStreamSize( nNewSize ); + pImp->SetSize( nNewSize ); + return !pImp->GetError(); +} + +sal_Bool UCBStorageStream::Validate( sal_Bool bWrite ) const +{ + return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); +} + +sal_Bool UCBStorageStream::ValidateMode( StreamMode m ) const +{ + // ??? + if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx + return sal_True; + sal_uInt16 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 sal_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 sal_True; + } + + return sal_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(); +} + +sal_Bool UCBStorageStream::Equals( const BaseStorageStream& rStream ) const +{ + // ??? + return ((BaseStorageStream*) this ) == &rStream; +} + +sal_Bool UCBStorageStream::Commit() +{ + // mark this stream for sending it on root commit + pImp->FlushData(); + return sal_True; +} + +sal_Bool UCBStorageStream::Revert() +{ + return pImp->Revert(); +} + +sal_Bool UCBStorageStream::CopyTo( BaseStorageStream* pDestStm ) +{ + if( !pImp->Init() ) + return sal_False; + + UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pDestStm ); + if ( pStg ) + pStg->pImp->m_aContentType = pImp->m_aContentType; + + pDestStm->SetSize( 0 ); + Seek( STREAM_SEEK_TO_END ); + sal_Int32 n = Tell(); + if( n < 0 ) + return sal_False; + + if( pDestStm->SetSize( n ) && n ) + { + sal_uInt8* p = new sal_uInt8[ 4096 ]; + Seek( 0L ); + pDestStm->Seek( 0L ); + while( n ) + { + sal_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 sal_True; +} + +sal_Bool UCBStorageStream::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) +{ + if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) + return sal_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 sal_True; + } + } + catch ( Exception& ) + { + } + + return sal_False; +} + +sal_Bool UCBStorageStream::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + try + { + if ( pImp->m_pContent ) + { + rValue = pImp->m_pContent->getPropertyValue( rName ); + return sal_True; + } + } + catch ( Exception& ) + { + } + + return sal_False; +} + +UCBStorage::UCBStorage( SvStream& rStrm, sal_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, sal_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, sal_Bool bDirect, sal_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, sal_Bool bDirect, sal_Bool bIsRoot, sal_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, sal_Bool bDirect, sal_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, sal_Bool bDirect, sal_Bool bIsRoot, sal_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( sal_False ) + , m_bCommited( sal_False ) + , m_bDirect( bDirect ) + , m_bIsRoot( bIsRoot ) + , m_bDirty( sal_False ) + , m_bIsLinked( sal_True ) + , m_bListCreated( sal_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( sal_True ); + m_aName = m_aOriginalName = aName = m_pTempFile->GetURL(); + } + + m_aURL = rName; +} + +UCBStorage_Impl::UCBStorage_Impl( const String& rName, StreamMode nMode, UCBStorage* pStorage, sal_Bool bDirect, sal_Bool bIsRoot, sal_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( sal_False ) + , m_bCommited( sal_False ) + , m_bDirect( bDirect ) + , m_bIsRoot( bIsRoot ) + , m_bDirty( sal_False ) + , m_bIsLinked( sal_False ) + , m_bListCreated( sal_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( sal_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 = sal_True; + } +} + +UCBStorage_Impl::UCBStorage_Impl( SvStream& rStream, UCBStorage* pStorage, sal_Bool bDirect ) + : m_pAntiImpl( pStorage ) + , m_pContent( NULL ) + , m_pTempFile( new ::utl::TempFile ) + , m_pSource( &rStream ) + , m_nError( 0 ) + , m_bModified( sal_False ) + , m_bCommited( sal_False ) + , m_bDirect( bDirect ) + , m_bIsRoot( sal_True ) + , m_bDirty( sal_False ) + , m_bIsLinked( sal_False ) + , m_bListCreated( sal_False ) + , m_nFormat( 0 ) + , m_aClassId( SvGlobalName() ) + , m_bRepairPackage( sal_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( sal_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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("?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 = sal_True; + + // create cursor for access to children + Sequence< ::rtl::OUString > aProps(4); + ::rtl::OUString* pProps = aProps.getArray(); + pProps[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Title")); + pProps[1] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("IsFolder")); + pProps[2] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")); + pProps[3] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("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.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("META-INF")) ) + continue; + } + else + { + aContentType = xRow->getString(3); + } + + sal_Bool bIsFolder( xRow->getBoolean(2) ); + sal_Int64 nSize = xRow->getLong(4); + UCBStorageElement_Impl* pElement = new UCBStorageElement_Impl( aTitle, bIsFolder, (sal_uLong) nSize ); + m_aChildrenList.push_back( pElement ); + + 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(RTL_CONSTASCII_USTRINGPARAM("MediaType")) ); + if ( ( aAny >>= aMediaType ) && ( aMediaType.compareToAscii("application/vnd.sun.star.oleobject") == 0 ) ) + pElement->m_bIsStorage = sal_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 = sal_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.size(); + for ( size_t i = 0; i < m_aChildrenList.size(); ++i ) + { + UCBStorageElement_Impl* pElement = m_aChildrenList[ i ]; + 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(); + } + + return nCount; +} + +::rtl::OUString Find_Impl( const Sequence < Sequence < PropertyValue > >& rSequence, const ::rtl::OUString& rPath ) +{ + sal_Bool bFound = sal_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.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("FullPath")) ) + { + rtl::OUString aTmp; + if ( ( rAny.Value >>= aTmp ) && aTmp == rPath ) + bFound = sal_True; + if ( aType.getLength() ) + break; + } + else if ( rAny.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("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(); + + for ( size_t i = 0; i < m_aChildrenList.size(); ++i ) + { + UCBStorageElement_Impl* pElement = m_aChildrenList[ i ]; + 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 ) ); + } + } + + 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(RTL_CONSTASCII_USTRINGPARAM("MediaType")); + aProps[0].Value <<= (::rtl::OUString ) m_aContentType; + aProps[1].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("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 + for ( size_t i = 0; i < m_aChildrenList.size(); ++i ) + { + UCBStorageElement_Impl* pElement = m_aChildrenList[ i ]; + 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(RTL_CONSTASCII_USTRINGPARAM("MediaType")); + aProps[0].Value <<= (::rtl::OUString ) pElement->GetContentType(); + aProps[1].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FullPath")); + aProps[1].Value <<= (::rtl::OUString ) aElementPath; + rSequence[ nProps++ ] = aProps; + } + } +} + +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! + for ( size_t i = 0, n = m_aChildrenList.size(); i < n; ++i ) + delete m_aChildrenList[ i ]; + m_aChildrenList.clear(); + + delete m_pContent; + delete m_pTempFile; +} + +sal_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 + sal_Bool bRet = sal_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 = sal_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 + 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 + for ( size_t i = 0; i < m_aChildrenList.size() && nRet; ++i ) + { + UCBStorageElement_Impl* pElement = m_aChildrenList[ i ]; + ::ucbhelper::Content* pContent = pElement->GetContent(); + sal_Bool bDeleteContent = sal_False; + if ( !pContent && pElement->IsModified() ) + { + // if the element has never been opened, no content has been created until now + bDeleteContent = sal_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(RTL_CONSTASCII_USTRINGPARAM("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 <<= (sal_Bool) sal_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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("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; + } + } + 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(RTL_CONSTASCII_USTRINGPARAM("MediaType")), aType ); + + if ( m_bIsLinked ) + { + // write a manifest file + // first create a subfolder "META-inf" + Content aNewSubFolder; + sal_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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM("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 + for ( size_t i = 0; i < m_aChildrenList.size(); ) + { + UCBStorageElement_Impl* pInnerElement = m_aChildrenList[ i ]; + if ( pInnerElement->m_bIsRemoved ) + { + UCBStorageElementList_Impl::iterator it = m_aChildrenList.begin(); + ::std::advance( it, i ); + delete *it; + m_aChildrenList.erase( it ); + } + else + { + pInnerElement->m_aOriginalName = pInnerElement->m_aName; + pInnerElement->m_bIsInserted = sal_False; + ++i; + } + } + } + + m_bCommited = sal_False; + } + + return nRet; +} + +sal_Bool UCBStorage_Impl::Revert() +{ + for ( size_t i = 0; i < m_aChildrenList.size(); ) + { + UCBStorageElement_Impl* pElement = m_aChildrenList[ i ]; + pElement->m_bIsRemoved = sal_False; + if ( pElement->m_bIsInserted ) + { + UCBStorageElementList_Impl::iterator it = m_aChildrenList.begin(); + ::std::advance( it, i ); + delete *it; + m_aChildrenList.erase( it ); + } + 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 = sal_False; + ++i; + } + } + return sal_True; +} + +const String& UCBStorage::GetName() const +{ + return pImp->m_aName; // pImp->m_aURL ?! +} + +sal_Bool UCBStorage::IsRoot() const +{ + return pImp->m_bIsRoot; +} + +void UCBStorage::SetDirty() +{ + pImp->m_bDirty = sal_True; +} + +void UCBStorage::SetClass( const SvGlobalName & rClass, sal_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*/, sal_uLong /*nOriginalClipFormat*/, const String & /*rUserTypeName*/ ) +{ + // ??? +} + +sal_Bool UCBStorage::ShouldConvert() +{ + // ??? + return sal_False; +} + +SvGlobalName UCBStorage::GetClassName() +{ + return pImp->m_aClassId; +} + +sal_uLong UCBStorage::GetFormat() +{ + return pImp->m_nFormat; +} + +String UCBStorage::GetUserName() +{ + OSL_FAIL("UserName is not implemented in UCB storages!" ); + return pImp->m_aUserTypeName; +} + +void UCBStorage::FillInfoList( SvStorageInfoList* pList ) const +{ + // put information in childrenlist into StorageInfoList + for ( size_t i = 0; i < pImp->GetChildrenList().size(); ++i ) + { + UCBStorageElement_Impl* pElement = pImp->GetChildrenList()[ i ]; + if ( !pElement->m_bIsRemoved ) + { + // problem: what about the size of a substorage ?! + sal_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 ); + } + } +} + +sal_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; + sal_Bool bDeleteStream = sal_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 = sal_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 + sal_Bool bDeleteStorage = sal_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 = sal_True; + } + + UCBStorage* pUCBDest = PTR_CAST( UCBStorage, pDest ); + UCBStorage* pUCBCopy = PTR_CAST( UCBStorage, pStorage ); + + sal_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 sal_Bool( Good() && pDest->Good() ); +} + +UCBStorageElement_Impl* UCBStorage::FindElement_Impl( const String& rName ) const +{ + DBG_ASSERT( rName.Len(), "Name is empty!" ); + for ( size_t i = 0, n = pImp->GetChildrenList().size(); i < n; ++i ) + { + UCBStorageElement_Impl* pElement = pImp->GetChildrenList()[ i ]; + if ( pElement->m_aName == rName && !pElement->m_bIsRemoved ) + return pElement; + } + return NULL; +} + +sal_Bool UCBStorage::CopyTo( BaseStorage* pDestStg ) const +{ + DBG_ASSERT( pDestStg != ((BaseStorage*)this), "Self-Copying is not possible!" ); + if ( pDestStg == ((BaseStorage*)this) ) + return sal_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(); + + sal_Bool bRet = sal_True; + for ( size_t i = 0; i < pImp->GetChildrenList().size() && bRet; ++i ) + { + UCBStorageElement_Impl* pElement = pImp->GetChildrenList()[ i ]; + if ( !pElement->m_bIsRemoved ) + bRet = CopyStorageElement_Impl( *pElement, pDestStg, pElement->m_aName ); + } + + if( !bRet ) + SetError( pDestStg->GetError() ); + return sal_Bool( Good() && pDestStg->Good() ); +} + +sal_Bool UCBStorage::CopyTo( const String& rElemName, BaseStorage* pDest, const String& rNew ) +{ + if( !rElemName.Len() ) + return sal_False; + + if ( pDest == ((BaseStorage*) this) ) + { + // can't double an element + return sal_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 sal_False; + } + } +} + +sal_Bool UCBStorage::Commit() +{ + // mark this storage for sending it on root commit + pImp->m_bCommited = sal_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 sal_True; +} + +sal_Bool UCBStorage::Revert() +{ + return pImp->Revert(); +} + +BaseStorageStream* UCBStorage::OpenStream( const String& rEleName, StreamMode nMode, sal_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 = sal_True; + pImp->m_aChildrenList.push_back( pElement ); + } + } + + 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 ) + { + OSL_FAIL("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 ); + + 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, sal_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, sal_Bool bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, sal_True ); +} + +BaseStorage* UCBStorage::OpenOLEStorage( const String& rEleName, StreamMode nMode, sal_Bool bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, sal_False ); +} + +BaseStorage* UCBStorage::OpenStorage( const String& rEleName, StreamMode nMode, sal_Bool bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, sal_True ); +} + +BaseStorage* UCBStorage::OpenStorage_Impl( const String& rEleName, StreamMode nMode, sal_Bool bDirect, sal_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, sal_False, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); + pStorage->pImp->m_bIsRoot = sal_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 = sal_True; + pImp->m_aChildrenList.push_back( pElement ); + } + + 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 = sal_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 ) + { + OSL_FAIL("Storage is already open!" ); + SetError( SVSTREAM_ACCESS_DENIED ); // ??? + } + else + { + sal_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, sal_False, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); + pElement->m_xStorage = pStorage->pImp; + return pStorage; + } + else + { + return new UCBStorage( pElement->m_xStorage ); + } + } + } + else if ( !pElement->m_xStream.Is() ) + { + // storage is opened the first time + sal_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; + sal_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, sal_Bool bDirect ) +{ + UCBStorage_Impl* pRet = NULL; + String aName( m_aURL ); + aName += '/'; + aName += pElement->m_aOriginalName; // ??? + + pElement->m_bIsStorage = pElement->m_bIsFolder = sal_True; + + if ( m_bIsLinked && !::utl::UCBContentHelper::Exists( aName ) ) + { + Content aNewFolder; + sal_Bool bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, pElement->m_aOriginalName, aNewFolder ); + if ( bRet ) + pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, NULL, bDirect, sal_False, m_bRepairPackage, m_xProgressHandler ); + } + else + { + pRet = new UCBStorage_Impl( aName, nMode, NULL, bDirect, sal_False, m_bRepairPackage, m_xProgressHandler ); + } + + if ( pRet ) + { + pRet->m_bIsLinked = m_bIsLinked; + pRet->m_bIsRoot = sal_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; +} + +sal_Bool UCBStorage::IsStorage( const String& rEleName ) const +{ + if( !rEleName.Len() ) + return sal_False; + + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement && pElement->m_bIsStorage ); +} + +sal_Bool UCBStorage::IsStream( const String& rEleName ) const +{ + if( !rEleName.Len() ) + return sal_False; + + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement && !pElement->m_bIsStorage ); +} + +sal_Bool UCBStorage::IsContained( const String & rEleName ) const +{ + if( !rEleName.Len() ) + return sal_False; + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement != NULL ); +} + +sal_Bool UCBStorage::Remove( const String& rEleName ) +{ + if( !rEleName.Len() ) + return sal_False; + + UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + if ( pElement ) + { + pElement->m_bIsRemoved = sal_True; + } + else + SetError( SVSTREAM_FILE_NOT_FOUND ); + + return ( pElement != NULL ); +} + +sal_Bool UCBStorage::Rename( const String& rEleName, const String& rNewName ) +{ + if( !rEleName.Len()|| !rNewName.Len() ) + return sal_False; + + UCBStorageElement_Impl *pAlreadyExisting = FindElement_Impl( rNewName ); + if ( pAlreadyExisting ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return sal_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; +} + +sal_Bool UCBStorage::MoveTo( const String& rEleName, BaseStorage* pNewSt, const String& rNewName ) +{ + if( !rEleName.Len() || !rNewName.Len() ) + return sal_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 + sal_Bool bRet = CopyTo( rEleName, pNewSt, rNewName ); + if ( bRet ) + bRet = Remove( rEleName ); + return bRet; + } +} + +sal_Bool UCBStorage::ValidateFAT() +{ + // ??? + return sal_True; +} + +sal_Bool UCBStorage::Validate( sal_Bool bWrite ) const +{ + // ??? + return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); +} + +sal_Bool UCBStorage::ValidateMode( StreamMode m ) const +{ + // ??? + if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx + return sal_True; + sal_uInt16 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 sal_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 sal_True; + } + + return sal_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; +} + +sal_Bool UCBStorage::Equals( const BaseStorage& rStorage ) const +{ + // ??? + return ((BaseStorage*)this) == &rStorage; +} + +sal_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 ); + sal_Bool bRet = UCBStorage::IsStorageFile( pStm ); + delete pStm; + return bRet; +} + +sal_Bool UCBStorage::IsStorageFile( SvStream* pFile ) +{ + if ( !pFile ) + return sal_False; + + sal_uLong nPos = pFile->Tell(); + pFile->Seek( STREAM_SEEK_TO_END ); + if ( pFile->Tell() < 4 ) + return sal_False; + + pFile->Seek(0); + sal_uInt32 nBytes; + *pFile >> nBytes; + + // search for the magic bytes + sal_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; +} + +sal_Bool UCBStorage::IsDiskSpannedFile( SvStream* pFile ) +{ + if ( !pFile ) + return sal_False; + + sal_uLong nPos = pFile->Tell(); + pFile->Seek( STREAM_SEEK_TO_END ); + if ( !pFile->Tell() ) + return sal_False; + + pFile->Seek(0); + sal_uInt32 nBytes; + *pFile >> nBytes; + + // disk spanned file have an additional header in front of the usual one + sal_Bool bRet = ( nBytes == 0x08074b50 ); + if ( bRet ) + { + *pFile >> nBytes; + bRet = ( nBytes == 0x04034b50 ); + } + + pFile->Seek( nPos ); + return bRet; +} + +String UCBStorage::GetLinkedFile( SvStream &rStream ) +{ + String aString; + sal_uLong nPos = rStream.Tell(); + rStream.Seek( STREAM_SEEK_TO_END ); + if ( !rStream.Tell() ) + return aString; + + rStream.Seek(0); + sal_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 << ( sal_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; + sal_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( sal_True ); + delete pTempFile; + return String(); +} + +sal_Bool UCBStorage::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) +{ + if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) + return sal_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 sal_True; + } + } + catch ( Exception& ) + { + } + + return sal_False; +} + +sal_Bool UCBStorage::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + try + { + if ( pImp->GetContent() ) + { + rValue = pImp->m_pContent->getPropertyValue( rName ); + return sal_True; + } + } + catch ( Exception& ) + { + } + + return sal_False; +} + +sal_Bool UCBStorage::GetProperty( const String& rEleName, const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + UCBStorageElement_Impl *pEle = FindElement_Impl( rEleName ); + if ( !pEle ) + return sal_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 sal_False; + } + + try + { + if ( pEle->m_xStream->m_pContent ) + { + rValue = pEle->m_xStream->m_pContent->getPropertyValue( rName ); + return sal_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 sal_False; + } + + try + { + if ( pEle->m_xStorage->GetContent() ) + { + rValue = pEle->m_xStorage->m_pContent->getPropertyValue( rName ); + return sal_True; + } + } + catch ( Exception& ) + { + } + } + + return sal_False; +} + +UNOStorageHolderList* UCBStorage::GetUNOStorageHolderList() +{ + if ( !pImp->m_pUNOStorageHolderList ) + pImp->m_pUNOStorageHolderList = new UNOStorageHolderList; + + return pImp->m_pUNOStorageHolderList; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/unostorageholder.cxx b/sot/source/sdstor/unostorageholder.cxx new file mode 100644 index 000000000000..dcfdfa134480 --- /dev/null +++ b/sot/source/sdstor/unostorageholder.cxx @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <sot/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(RTL_CONSTASCII_USTRINGPARAM("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( sal_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(RTL_CONSTASCII_USTRINGPARAM("MediaType")), aMediaType ) ) + m_rSotStorage->SetProperty( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("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 + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sot/source/sdstor/unostorageholder.hxx b/sot/source/sdstor/unostorageholder.hxx new file mode 100644 index 000000000000..13743c64447e --- /dev/null +++ b/sot/source/sdstor/unostorageholder.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |