diff options
Diffstat (limited to 'sot/source/sdstor/ucbstorage.cxx')
-rw-r--r-- | sot/source/sdstor/ucbstorage.cxx | 3577 |
1 files changed, 3577 insertions, 0 deletions
diff --git a/sot/source/sdstor/ucbstorage.cxx b/sot/source/sdstor/ucbstorage.cxx new file mode 100644 index 000000000000..51d297dd4e31 --- /dev/null +++ b/sot/source/sdstor/ucbstorage.cxx @@ -0,0 +1,3577 @@ +/* -*- 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/list.hxx> +#include <tools/urlobj.hxx> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase2.hxx> +#include <ucbhelper/commandenvironment.hxx> + +#include "stg.hxx" +#include "storinfo.hxx" +#include <sot/storage.hxx> +#include <sot/exchange.hxx> +#include <sot/formats.hxx> +#include "clsids.hxx" + +#include "unostorageholder.hxx" + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::sdbc; +using namespace ::ucbhelper; + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +static int nOpenFiles=0; +static int nOpenStreams=0; +#endif + +typedef ::cppu::WeakImplHelper2 < XInputStream, XSeekable > FileInputStreamWrapper_Base; +class FileStreamWrapper_Impl : public FileInputStreamWrapper_Base +{ +protected: + ::osl::Mutex m_aMutex; + String m_aURL; + SvStream* m_pSvStream; + +public: + FileStreamWrapper_Impl( const String& rName ); + virtual ~FileStreamWrapper_Impl(); + + //DECLARE_UNO3_AGG_DEFAULTS( FileStreamWrapper_Impl, FileInputStreamWrapper_Base); + + virtual void SAL_CALL seek( sal_Int64 _nLocation ) throw ( IllegalArgumentException, IOException, RuntimeException); + virtual sal_Int64 SAL_CALL getPosition( ) throw ( IOException, RuntimeException); + virtual sal_Int64 SAL_CALL getLength( ) throw ( IOException, RuntimeException); + virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException ); + virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException ); + virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) throw( NotConnectedException, BufferSizeExceededException, RuntimeException); + virtual sal_Int32 SAL_CALL available() throw( NotConnectedException, RuntimeException ); + virtual void SAL_CALL closeInput() throw( NotConnectedException, RuntimeException ); + +protected: + void checkConnected(); + void checkError(); +}; + +//------------------------------------------------------------------ +FileStreamWrapper_Impl::FileStreamWrapper_Impl( const String& rName ) + : m_aURL( rName ) + , m_pSvStream(0) +{ + // if no URL is provided the stream is empty +} + +//------------------------------------------------------------------ +FileStreamWrapper_Impl::~FileStreamWrapper_Impl() +{ + if ( m_pSvStream ) + { + delete m_pSvStream; +#if OSL_DEBUG_LEVEL > 1 + --nOpenFiles; +#endif + } + + if ( m_aURL.Len() ) + ::utl::UCBContentHelper::Kill( m_aURL ); +} + +//------------------------------------------------------------------------------ +sal_Int32 SAL_CALL FileStreamWrapper_Impl::readBytes(Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead) + throw( NotConnectedException, BufferSizeExceededException, RuntimeException ) +{ + if ( !m_aURL.Len() ) + { + aData.realloc( 0 ); + return 0; + } + + checkConnected(); + + if (nBytesToRead < 0) + throw BufferSizeExceededException(::rtl::OUString(),static_cast<XWeak*>(this)); + + ::osl::MutexGuard aGuard( m_aMutex ); + + aData.realloc(nBytesToRead); + + sal_uInt32 nRead = m_pSvStream->Read((void*)aData.getArray(), nBytesToRead); + checkError(); + + // Wenn gelesene Zeichen < MaxLength, Sequence anpassen + if (nRead < (sal_uInt32)nBytesToRead) + aData.realloc( nRead ); + + return nRead; +} + +//------------------------------------------------------------------------------ +sal_Int32 SAL_CALL FileStreamWrapper_Impl::readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead) throw( NotConnectedException, BufferSizeExceededException, RuntimeException ) +{ + if ( !m_aURL.Len() ) + { + aData.realloc( 0 ); + return 0; + } + + checkError(); + + if (nMaxBytesToRead < 0) + throw BufferSizeExceededException(::rtl::OUString(),static_cast<XWeak*>(this)); + + if (m_pSvStream->IsEof()) + { + aData.realloc(0); + return 0; + } + else + return readBytes(aData, nMaxBytesToRead); +} + +//------------------------------------------------------------------------------ +void SAL_CALL FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip) throw( NotConnectedException, BufferSizeExceededException, RuntimeException ) +{ + if ( !m_aURL.Len() ) + return; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkError(); + +#ifdef DBG_UTIL + sal_uInt32 nCurrentPos = m_pSvStream->Tell(); +#endif + + m_pSvStream->SeekRel(nBytesToSkip); + checkError(); + +#ifdef DBG_UTIL + nCurrentPos = m_pSvStream->Tell(); +#endif +} + +//------------------------------------------------------------------------------ +sal_Int32 SAL_CALL FileStreamWrapper_Impl::available() throw( NotConnectedException, RuntimeException ) +{ + if ( !m_aURL.Len() ) + return 0; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkConnected(); + + sal_uInt32 nPos = m_pSvStream->Tell(); + checkError(); + + m_pSvStream->Seek(STREAM_SEEK_TO_END); + checkError(); + + sal_Int32 nAvailable = (sal_Int32)m_pSvStream->Tell() - nPos; + m_pSvStream->Seek(nPos); + checkError(); + + return nAvailable; +} + +//------------------------------------------------------------------------------ +void SAL_CALL FileStreamWrapper_Impl::closeInput() throw( NotConnectedException, RuntimeException ) +{ + if ( !m_aURL.Len() ) + return; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkConnected(); + DELETEZ( m_pSvStream ); +#if OSL_DEBUG_LEVEL > 1 + --nOpenFiles; +#endif + ::utl::UCBContentHelper::Kill( m_aURL ); + m_aURL.Erase(); +} + +//------------------------------------------------------------------------------ +void SAL_CALL FileStreamWrapper_Impl::seek( sal_Int64 _nLocation ) throw (IllegalArgumentException, IOException, RuntimeException) +{ + if ( !m_aURL.Len() ) + return; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkConnected(); + + m_pSvStream->Seek((sal_uInt32)_nLocation); + checkError(); +} + +//------------------------------------------------------------------------------ +sal_Int64 SAL_CALL FileStreamWrapper_Impl::getPosition( ) throw (IOException, RuntimeException) +{ + if ( !m_aURL.Len() ) + return 0; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkConnected(); + + sal_uInt32 nPos = m_pSvStream->Tell(); + checkError(); + return (sal_Int64)nPos; +} + +//------------------------------------------------------------------------------ +sal_Int64 SAL_CALL FileStreamWrapper_Impl::getLength( ) throw (IOException, RuntimeException) +{ + if ( !m_aURL.Len() ) + return 0; + + ::osl::MutexGuard aGuard( m_aMutex ); + checkConnected(); + + sal_uInt32 nCurrentPos = m_pSvStream->Tell(); + checkError(); + + m_pSvStream->Seek(STREAM_SEEK_TO_END); + sal_uInt32 nEndPos = m_pSvStream->Tell(); + m_pSvStream->Seek(nCurrentPos); + + checkError(); + + return (sal_Int64)nEndPos; +} + +//------------------------------------------------------------------------------ +void FileStreamWrapper_Impl::checkConnected() +{ + if ( !m_aURL.Len() ) + throw NotConnectedException(::rtl::OUString(), const_cast<XWeak*>(static_cast<const XWeak*>(this))); + if ( !m_pSvStream ) + { + m_pSvStream = ::utl::UcbStreamHelper::CreateStream( m_aURL, STREAM_STD_READ ); +#if OSL_DEBUG_LEVEL > 1 + ++nOpenFiles; +#endif + } +} + +//------------------------------------------------------------------------------ +void FileStreamWrapper_Impl::checkError() +{ + checkConnected(); + + if (m_pSvStream->SvStream::GetError() != ERRCODE_NONE) + // TODO: really evaluate the error + throw NotConnectedException(::rtl::OUString(), const_cast<XWeak*>(static_cast<const XWeak*>(this))); +} + +TYPEINIT1( UCBStorageStream, BaseStorageStream ); +TYPEINIT1( UCBStorage, BaseStorage ); + +#define COMMIT_RESULT_FAILURE 0 +#define COMMIT_RESULT_NOTHING_TO_DO 1 +#define COMMIT_RESULT_SUCCESS 2 + +#define min( x, y ) (( x < y ) ? x : y) +#define max( x, y ) (( x > y ) ? x : y) + +sal_Int32 GetFormatId_Impl( SvGlobalName aName ) +{ +// if ( aName == SvGlobalName( SO3_SW_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARWRITER_8; +// if ( aName == SvGlobalName( SO3_SWWEB_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARWRITERWEB_8; +// if ( aName == SvGlobalName( SO3_SWGLOB_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARWRITERGLOB_8; +// if ( aName == SvGlobalName( SO3_SDRAW_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARDRAW_8; +// if ( aName == SvGlobalName( SO3_SIMPRESS_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARIMPRESS_8; +// if ( aName == SvGlobalName( SO3_SC_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARCALC_8; +// if ( aName == SvGlobalName( SO3_SCH_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARCHART_8; +// if ( aName == SvGlobalName( SO3_SM_CLASSID_8 ) ) +// return SOT_FORMATSTR_ID_STARMATH_8; + if ( aName == SvGlobalName( SO3_SW_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARWRITER_60; + if ( aName == SvGlobalName( SO3_SWWEB_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARWRITERWEB_60; + if ( aName == SvGlobalName( SO3_SWGLOB_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARWRITERGLOB_60; + if ( aName == SvGlobalName( SO3_SDRAW_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARDRAW_60; + if ( aName == SvGlobalName( SO3_SIMPRESS_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARIMPRESS_60; + if ( aName == SvGlobalName( SO3_SC_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARCALC_60; + if ( aName == SvGlobalName( SO3_SCH_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARCHART_60; + if ( aName == SvGlobalName( SO3_SM_CLASSID_60 ) ) + return SOT_FORMATSTR_ID_STARMATH_60; + if ( aName == SvGlobalName( SO3_OUT_CLASSID ) || + aName == SvGlobalName( SO3_APPLET_CLASSID ) || + aName == SvGlobalName( SO3_PLUGIN_CLASSID ) || + aName == SvGlobalName( SO3_IFRAME_CLASSID ) ) + // allowed, but not supported + return 0; + else + { + DBG_ERROR( "Unknown UCB storage format!" ); + return 0; + } +} + + +SvGlobalName GetClassId_Impl( sal_Int32 nFormat ) +{ + switch ( nFormat ) + { + case SOT_FORMATSTR_ID_STARWRITER_8 : + case SOT_FORMATSTR_ID_STARWRITER_8_TEMPLATE : + return SvGlobalName( SO3_SW_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARWRITERWEB_8 : + return SvGlobalName( SO3_SWWEB_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARWRITERGLOB_8 : + return SvGlobalName( SO3_SWGLOB_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARDRAW_8 : + case SOT_FORMATSTR_ID_STARDRAW_8_TEMPLATE : + return SvGlobalName( SO3_SDRAW_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARIMPRESS_8 : + case SOT_FORMATSTR_ID_STARIMPRESS_8_TEMPLATE : + return SvGlobalName( SO3_SIMPRESS_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARCALC_8 : + case SOT_FORMATSTR_ID_STARCALC_8_TEMPLATE : + return SvGlobalName( SO3_SC_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARCHART_8 : + case SOT_FORMATSTR_ID_STARCHART_8_TEMPLATE : + return SvGlobalName( SO3_SCH_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARMATH_8 : + case SOT_FORMATSTR_ID_STARMATH_8_TEMPLATE : + return SvGlobalName( SO3_SM_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARWRITER_60 : + return SvGlobalName( SO3_SW_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARWRITERWEB_60 : + return SvGlobalName( SO3_SWWEB_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARWRITERGLOB_60 : + return SvGlobalName( SO3_SWGLOB_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARDRAW_60 : + return SvGlobalName( SO3_SDRAW_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARIMPRESS_60 : + return SvGlobalName( SO3_SIMPRESS_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARCALC_60 : + return SvGlobalName( SO3_SC_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARCHART_60 : + return SvGlobalName( SO3_SCH_CLASSID_60 ); + case SOT_FORMATSTR_ID_STARMATH_60 : + return SvGlobalName( SO3_SM_CLASSID_60 ); + default : + //DBG_ERROR( "Unknown UCB storage format!" ); + return SvGlobalName(); + } +} + +// All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle +// class, that uses the refcounted object as impl-class. + +enum RepresentModes { + nonset, + svstream, + xinputstream +}; + +class UCBStorageStream_Impl : public SvRefBase, public SvStream +{ + ~UCBStorageStream_Impl(); +public: + + virtual ULONG GetData( void* pData, ULONG nSize ); + virtual ULONG PutData( const void* pData, ULONG nSize ); + virtual ULONG SeekPos( ULONG nPos ); + virtual void SetSize( ULONG nSize ); + virtual void FlushData(); + virtual void ResetError(); + + UCBStorageStream* m_pAntiImpl; // only valid if an external reference exists + + String m_aOriginalName;// the original name before accessing the stream + String m_aName; // the actual name ( changed with a Rename command at the parent ) + String m_aURL; // the full path name to create the content + String m_aContentType; + String m_aOriginalContentType; + ByteString m_aKey; + ::ucbhelper::Content* m_pContent; // the content that provides the data + Reference<XInputStream> m_rSource; // the stream covering the original data of the content + SvStream* m_pStream; // the stream worked on; for readonly streams it is the original stream of the content + // for read/write streams it's a copy into a temporary file + String m_aTempURL; // URL of this temporary stream + RepresentModes m_nRepresentMode; // should it be used as XInputStream or as SvStream + long m_nError; + StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing ) + BOOL m_bSourceRead; // Source still contains useful information + BOOL m_bModified; // only modified streams will be sent to the original content + BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package + BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages + // this means that the root storage does an autocommit when its external + // reference is destroyed + BOOL m_bIsOLEStorage;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream + + UCBStorageStream_Impl( const String&, StreamMode, UCBStorageStream*, BOOL, const ByteString* pKey=0, BOOL bRepair = FALSE, Reference< XProgressHandler > xProgress = Reference< XProgressHandler >() ); + + void Free(); + BOOL Init(); + BOOL Clear(); + sal_Int16 Commit(); // if modified and commited: transfer an XInputStream to the content + BOOL Revert(); // discard all changes + BaseStorage* CreateStorage();// create an OLE Storage on the UCBStorageStream + ULONG GetSize(); + + ULONG ReadSourceWriteTemporary( ULONG aLength ); // read aLength from source and copy to temporary, + // no seeking is produced + ULONG ReadSourceWriteTemporary(); // read source till the end and copy to temporary, + + ULONG CopySourceToTemporary(); // same as ReadSourceWriteToTemporary() + // but the writing is done at the end of temporary + // pointer position is not changed + Reference<XInputStream> GetXInputStream(); // return XInputStream, after that + // this class is close to be unusable + // since it can not read and write + using SvStream::SetError; + void SetError( sal_uInt32 nError ); + void PrepareCachedForReopen( StreamMode nMode ); +}; + +SV_DECL_IMPL_REF( UCBStorageStream_Impl ); + +struct UCBStorageElement_Impl; +DECLARE_LIST( UCBStorageElementList_Impl, UCBStorageElement_Impl* ) + +class UCBStorage_Impl : public SvRefBase +{ + ~UCBStorage_Impl(); +public: + UCBStorage* m_pAntiImpl; // only valid if external references exists + + String m_aOriginalName;// the original name before accessing the storage + String m_aName; // the actual name ( changed with a Rename command at the parent ) + String m_aURL; // the full path name to create the content + String m_aContentType; + String m_aOriginalContentType; + ::ucbhelper::Content* m_pContent; // the content that provides the storage elements + ::utl::TempFile* m_pTempFile; // temporary file, only for storages on stream + SvStream* m_pSource; // original stream, only for storages on a stream + //SvStream* m_pStream; // the corresponding editable stream, only for storage on a stream + long m_nError; + StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing ) + BOOL m_bModified; // only modified elements will be sent to the original content + BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package + BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages + // this means that the root storage does an autocommit when its external + // reference is destroyed + BOOL m_bIsRoot; // marks this storage as root storages that manages all oommits and reverts + BOOL m_bDirty; // ??? + BOOL m_bIsLinked; + BOOL m_bListCreated; + ULONG m_nFormat; + String m_aUserTypeName; + SvGlobalName m_aClassId; + + UCBStorageElementList_Impl m_aChildrenList; + + BOOL m_bRepairPackage; + Reference< XProgressHandler > m_xProgressHandler; + + UNOStorageHolderList* m_pUNOStorageHolderList; + UCBStorage_Impl( const ::ucbhelper::Content&, const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() ); + UCBStorage_Impl( const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() ); + UCBStorage_Impl( SvStream&, UCBStorage*, BOOL ); + void Init(); + sal_Int16 Commit(); + BOOL Revert(); + BOOL Insert( ::ucbhelper::Content *pContent ); + UCBStorage_Impl* OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect ); + UCBStorageStream_Impl* OpenStream( UCBStorageElement_Impl*, StreamMode, BOOL, const ByteString* pKey=0 ); + void SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const String& ); + void GetProps( sal_Int32&, Sequence < Sequence < PropertyValue > >& rSequence, const String& ); + sal_Int32 GetObjectCount(); + void ReadContent(); + void CreateContent(); + ::ucbhelper::Content* GetContent() + { if ( !m_pContent ) CreateContent(); return m_pContent; } + UCBStorageElementList_Impl& GetChildrenList() + { + long nError = m_nError; + ReadContent(); + if ( m_nMode & STREAM_WRITE ) + { + m_nError = nError; + if ( m_pAntiImpl ) + { + m_pAntiImpl->ResetError(); + m_pAntiImpl->SetError( nError ); + } + } + + return m_aChildrenList; + } + + void SetError( long nError ); +}; + +SV_DECL_IMPL_REF( UCBStorage_Impl ); + +// this struct contains all neccessary information on an element inside a UCBStorage +struct UCBStorageElement_Impl +{ + String m_aName; // the actual URL relative to the root "folder" + String m_aOriginalName;// the original name in the content + ULONG m_nSize; + BOOL m_bIsFolder; // Only TRUE when it is a UCBStorage ! + BOOL m_bIsStorage; // Also TRUE when it is an OLEStorage ! + BOOL m_bIsRemoved; // element will be removed on commit + BOOL m_bIsInserted; // element will be removed on revert + UCBStorage_ImplRef m_xStorage; // reference to the "real" storage + UCBStorageStream_ImplRef m_xStream; // reference to the "real" stream + + UCBStorageElement_Impl( const ::rtl::OUString& rName, + BOOL bIsFolder = FALSE, ULONG nSize = 0 ) + : m_aName( rName ) + , m_aOriginalName( rName ) + , m_nSize( nSize ) + , m_bIsFolder( bIsFolder ) + , m_bIsStorage( bIsFolder ) + , m_bIsRemoved( FALSE ) + , m_bIsInserted( FALSE ) + { + } + + ::ucbhelper::Content* GetContent(); + BOOL IsModified(); + String GetContentType(); + void SetContentType( const String& ); + String GetOriginalContentType(); + BOOL IsLoaded() + { return m_xStream.Is() || m_xStorage.Is(); } +}; + +::ucbhelper::Content* UCBStorageElement_Impl::GetContent() +{ + if ( m_xStream.Is() ) + return m_xStream->m_pContent; + else if ( m_xStorage.Is() ) + return m_xStorage->GetContent(); + else + return NULL; +} + +String UCBStorageElement_Impl::GetContentType() +{ + if ( m_xStream.Is() ) + return m_xStream->m_aContentType; + else if ( m_xStorage.Is() ) + return m_xStorage->m_aContentType; + else + { + DBG_ERROR("Element not loaded!"); + return String(); + } +} + +void UCBStorageElement_Impl::SetContentType( const String& rType ) +{ + if ( m_xStream.Is() ) { + m_xStream->m_aContentType = m_xStream->m_aOriginalContentType = rType; + } + else if ( m_xStorage.Is() ) { + m_xStorage->m_aContentType = m_xStorage->m_aOriginalContentType = rType; + } + else { + DBG_ERROR("Element not loaded!"); + } +} + +String UCBStorageElement_Impl::GetOriginalContentType() +{ + if ( m_xStream.Is() ) + return m_xStream->m_aOriginalContentType; + else if ( m_xStorage.Is() ) + return m_xStorage->m_aOriginalContentType; + else + return String(); +} + +BOOL UCBStorageElement_Impl::IsModified() +{ + BOOL bModified = m_bIsRemoved || m_bIsInserted || m_aName != m_aOriginalName; + if ( bModified ) + { + if ( m_xStream.Is() ) + bModified = m_xStream->m_aContentType != m_xStream->m_aOriginalContentType; + else if ( m_xStorage.Is() ) + bModified = m_xStorage->m_aContentType != m_xStorage->m_aOriginalContentType; + } + + return bModified; +} + +UCBStorageStream_Impl::UCBStorageStream_Impl( const String& rName, StreamMode nMode, UCBStorageStream* pStream, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress ) + : m_pAntiImpl( pStream ) + , m_aURL( rName ) + , m_pContent( NULL ) + , m_pStream( NULL ) + , m_nRepresentMode( nonset ) + , m_nError( 0 ) + , m_nMode( nMode ) + , m_bSourceRead( !( nMode & STREAM_TRUNC ) ) + , m_bModified( FALSE ) + , m_bCommited( FALSE ) + , m_bDirect( bDirect ) + , m_bIsOLEStorage( FALSE ) +{ + // name is last segment in URL + INetURLObject aObj( rName ); + m_aName = m_aOriginalName = aObj.GetLastName(); + try + { + // create the content + Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv; + + ::rtl::OUString aTemp( rName ); + + if ( bRepair ) + { + xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(), + xProgress ); + aTemp += rtl::OUString(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 ) + { + DBG_ERROR( "Misuse of the XInputstream!" ); + SetError( ERRCODE_IO_ACCESSDENIED ); + } + else + { + if( m_bModified ) + { + // use wrapper around temporary stream + if( Init() ) + { + CopySourceToTemporary(); + + // owner transfer of stream to wrapper + aResult = new ::utl::OInputStreamWrapper( m_pStream, TRUE ); + m_pStream->Seek(0); + + if( aResult.is() ) + { + // temporary stream can not be used here any more + // and can not be opened untill wrapper is closed + // stream is deleted by wrapper after use + m_pStream = NULL; + m_nRepresentMode = xinputstream; + } + } + } + else + { + Free(); + + // open a new instance of XInputStream + try + { + aResult = m_pContent->openStream(); + } + catch ( Exception& ) + { + // usually means that stream could not be opened + } + + if( aResult.is() ) + m_nRepresentMode = xinputstream; + else + SetError( ERRCODE_IO_ACCESSDENIED ); + } + } + + return aResult; +} + +BOOL UCBStorageStream_Impl::Init() +{ + if( m_nRepresentMode == xinputstream ) + { + DBG_ERROR( "XInputStream misuse!" ); + SetError( ERRCODE_IO_ACCESSDENIED ); + return FALSE; + } + + if( !m_pStream ) + { + // no temporary stream was created + // create one + + m_nRepresentMode = svstream; // can not be used as XInputStream + + if ( !m_aTempURL.Len() ) + m_aTempURL = ::utl::TempFile().GetURL(); + + m_pStream = ::utl::UcbStreamHelper::CreateStream( m_aTempURL, STREAM_STD_READWRITE, sal_True /* bFileExists */ ); +#if OSL_DEBUG_LEVEL > 1 + ++nOpenFiles; +#endif + + if( !m_pStream ) + { + DBG_ERROR( "Suspicious temporary stream creation!" ); + SetError( SVSTREAM_CANNOT_MAKE ); + return FALSE; + } + + SetError( m_pStream->GetError() ); + } + + if( m_bSourceRead && !m_rSource.is() ) + { + // source file contain usefull information and is not opened + // open it from the point of noncopied data + + try + { + m_rSource = m_pContent->openStream(); + } + catch ( Exception& ) + { + // usually means that stream could not be opened + } + + if( m_rSource.is() ) + { + m_pStream->Seek( STREAM_SEEK_TO_END ); + + try + { + m_rSource->skipBytes( m_pStream->Tell() ); + } + catch( BufferSizeExceededException& ) + { + // the temporary stream already contain all the data + m_bSourceRead = FALSE; + } + catch( Exception& ) + { + // something is really wrong + m_bSourceRead = FALSE; + DBG_ERROR( "Can not operate original stream!" ); + SetError( SVSTREAM_CANNOT_MAKE ); + } + + m_pStream->Seek( 0 ); + } + else + { + // if the new file is edited than no source exist + m_bSourceRead = FALSE; + //SetError( SVSTREAM_CANNOT_MAKE ); + } + } + + DBG_ASSERT( m_rSource.is() || !m_bSourceRead, "Unreadable source stream!" ); + + return sal_True; +} + +ULONG UCBStorageStream_Impl::ReadSourceWriteTemporary() +{ + // read source stream till the end and copy all the data to + // the current position of the temporary stream + + ULONG aResult = 0; + + if( m_bSourceRead ) + { + Sequence<sal_Int8> aData(32000); + + try + { + ULONG aReaded; + do + { + aReaded = m_rSource->readBytes( aData, 32000 ); + aResult += m_pStream->Write( aData.getArray(), aReaded ); + } while( aReaded == 32000 ); + } +#if OSL_DEBUG_LEVEL > 1 + catch( Exception & e ) + { + OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + } + + m_bSourceRead = FALSE; + + return aResult; + +} + +ULONG UCBStorageStream_Impl::ReadSourceWriteTemporary( ULONG aLength ) +{ + // read aLength bite from the source stream and copy them to the current + // position of the temporary stream + + ULONG aResult = 0; + + if( m_bSourceRead ) + { + Sequence<sal_Int8> aData(32000); + + try + { + + ULONG aReaded = 32000; + + for( ULONG pInd = 0; pInd < aLength && aReaded == 32000 ; pInd += 32000 ) + { + ULONG aToCopy = min( aLength - pInd, 32000 ); + aReaded = m_rSource->readBytes( aData, aToCopy ); + aResult += m_pStream->Write( aData.getArray(), aReaded ); + } + + if( aResult < aLength ) + m_bSourceRead = FALSE; + } +#if OSL_DEBUG_LEVEL > 1 + catch( Exception & e ) + { + OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + } + + return aResult; +} + +ULONG UCBStorageStream_Impl::CopySourceToTemporary() +{ + // current position of the temporary stream is not changed + ULONG aResult = 0; + + if( m_bSourceRead ) + { + ULONG aPos = m_pStream->Tell(); + m_pStream->Seek( STREAM_SEEK_TO_END ); + aResult = ReadSourceWriteTemporary(); + m_pStream->Seek( aPos ); + } + + return aResult; + +} + +// UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream +// of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified +ULONG UCBStorageStream_Impl::GetData( void* pData, ULONG nSize ) +{ + ULONG aResult = 0; + + if( !Init() ) + return 0; + + + // read data that is in temporary stream + aResult = m_pStream->Read( pData, nSize ); + if( m_bSourceRead && aResult < nSize ) + { + // read the tail of the data from original stream + // copy this tail to the temporary stream + + ULONG aToRead = nSize - aResult; + pData = (void*)( (char*)pData + aResult ); + + try + { + Sequence<sal_Int8> aData( aToRead ); + ULONG aReaded = m_rSource->readBytes( aData, aToRead ); + aResult += m_pStream->Write( (void*)aData.getArray(), aReaded ); + memcpy( pData, aData.getArray(), aReaded ); + } +#if OSL_DEBUG_LEVEL > 1 + catch( Exception & e ) + { + OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#else + catch( Exception & ) + { +#endif + } + + if( aResult < nSize ) + m_bSourceRead = FALSE; + } + + return aResult; +} + +ULONG UCBStorageStream_Impl::PutData( const void* pData, ULONG nSize ) +{ + if ( !(m_nMode & STREAM_WRITE) ) + { + SetError( ERRCODE_IO_ACCESSDENIED ); + return 0; // ?mav? + } + + if( !nSize || !Init() ) + return 0; + + ULONG aResult = m_pStream->Write( pData, nSize ); + + m_bModified = aResult > 0; + + return aResult; + +} + +ULONG UCBStorageStream_Impl::SeekPos( ULONG nPos ) +{ + if( !Init() ) + return 0; + + ULONG aResult; + + if( nPos == STREAM_SEEK_TO_END ) + { + m_pStream->Seek( STREAM_SEEK_TO_END ); + ReadSourceWriteTemporary(); + aResult = m_pStream->Tell(); + } + else + { + // the problem is that even if nPos is larger the the length + // of the stream the stream pointer will be moved to this position + // so we have to check if temporary stream does not contain required position + + if( m_pStream->Tell() > nPos + || m_pStream->Seek( STREAM_SEEK_TO_END ) > nPos ) + { + // no copiing is required + aResult = m_pStream->Seek( nPos ); + } + else + { + // the temp stream pointer points to the end now + aResult = m_pStream->Tell(); + + if( aResult < nPos ) + { + if( m_bSourceRead ) + { + aResult += ReadSourceWriteTemporary( nPos - aResult ); + if( aResult < nPos ) + m_bSourceRead = FALSE; + + DBG_ASSERT( aResult == m_pStream->Tell(), "Error in stream arithmetic!\n" ); + } + + if( (m_nMode & STREAM_WRITE) && !m_bSourceRead && aResult < nPos ) + { + // it means that all the Source stream was copied already + // but the required position still was not reached + // for writable streams it should be done + m_pStream->SetStreamSize( nPos ); + aResult = m_pStream->Seek( STREAM_SEEK_TO_END ); + DBG_ASSERT( aResult == nPos, "Error in stream arithmetic!\n" ); + } + } + } + } + + return aResult; +} + +void UCBStorageStream_Impl::SetSize( ULONG nSize ) +{ + if ( !(m_nMode & STREAM_WRITE) ) + { + SetError( ERRCODE_IO_ACCESSDENIED ); + return; + } + + if( !Init() ) + return; + + m_bModified = TRUE; + + if( m_bSourceRead ) + { + ULONG aPos = m_pStream->Tell(); + m_pStream->Seek( STREAM_SEEK_TO_END ); + if( m_pStream->Tell() < nSize ) + ReadSourceWriteTemporary( nSize - m_pStream->Tell() ); + m_pStream->Seek( aPos ); + } + + m_pStream->SetStreamSize( nSize ); + m_bSourceRead = FALSE; +} + +void UCBStorageStream_Impl::FlushData() +{ + if( m_pStream ) + { + CopySourceToTemporary(); + m_pStream->Flush(); + } + + m_bCommited = TRUE; +} + +void UCBStorageStream_Impl::SetError( sal_uInt32 nErr ) +{ + if ( !m_nError ) + { + m_nError = nErr; + SvStream::SetError( nErr ); + if ( m_pAntiImpl ) m_pAntiImpl->SetError( nErr ); + } +} + +void UCBStorageStream_Impl::ResetError() +{ + m_nError = 0; + SvStream::ResetError(); + if ( m_pAntiImpl ) + m_pAntiImpl->ResetError(); +} + +ULONG UCBStorageStream_Impl::GetSize() +{ + if( !Init() ) + return 0; + + ULONG nPos = m_pStream->Tell(); + m_pStream->Seek( STREAM_SEEK_TO_END ); + ReadSourceWriteTemporary(); + ULONG nRet = m_pStream->Tell(); + m_pStream->Seek( nPos ); + + return nRet; +} + +BaseStorage* UCBStorageStream_Impl::CreateStorage() +{ + // create an OLEStorage on a SvStream ( = this ) + // it gets the root attribute because otherwise it would probably not write before my root is commited + UCBStorageStream* pNewStorageStream = new UCBStorageStream( this ); + Storage *pStorage = new Storage( *pNewStorageStream, m_bDirect ); + + // GetError() call cleares error code for OLE storages, must be changed in future + long nTmpErr = pStorage->GetError(); + pStorage->SetError( nTmpErr ); + + m_bIsOLEStorage = !nTmpErr; + return static_cast< BaseStorage* > ( pStorage ); +} + +sal_Int16 UCBStorageStream_Impl::Commit() +{ + // send stream to the original content + // the parent storage is responsible for the correct handling of deleted contents + if ( m_bCommited || m_bIsOLEStorage || m_bDirect ) + { + // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage + // was commited as well ( if not opened in direct mode ) + + if ( m_bModified ) + { + try + { + CopySourceToTemporary(); + + // release all stream handles + Free(); + + // the temporary file does not exist only for truncated streams + DBG_ASSERT( m_aTempURL.Len() || ( m_nMode & STREAM_TRUNC ), "No temporary file to read from!"); + if ( !m_aTempURL.Len() && !( m_nMode & STREAM_TRUNC ) ) + throw RuntimeException(); + + // create wrapper to stream that is only used while reading inside package component + Reference < XInputStream > xStream = new FileStreamWrapper_Impl( m_aTempURL ); + + Any aAny; + InsertCommandArgument aArg; + aArg.Data = xStream; + aArg.ReplaceExisting = sal_True; + aAny <<= aArg; + m_pContent->executeCommand( ::rtl::OUString(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 = FALSE; + m_bSourceRead = TRUE; + } + catch ( CommandAbortedException& ) + { + // any command wasn't executed successfully - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + catch ( RuntimeException& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + catch ( Exception& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + + m_bCommited = FALSE; + return COMMIT_RESULT_SUCCESS; + } + } + + return COMMIT_RESULT_NOTHING_TO_DO; +} + +BOOL UCBStorageStream_Impl::Revert() +{ + // if an OLEStorage is created on this stream, no "revert" is neccessary because OLEStorages do nothing on "Revert" ! + if ( m_bCommited ) + { + DBG_ERROR("Revert while commit is in progress!" ); + return FALSE; // ??? + } + + Free(); + if ( m_aTempURL.Len() ) + { + ::utl::UCBContentHelper::Kill( m_aTempURL ); + m_aTempURL.Erase(); + } + + m_bSourceRead = FALSE; + try + { + m_rSource = m_pContent->openStream(); + if( m_rSource.is() ) + { + if ( m_pAntiImpl && ( m_nMode & STREAM_TRUNC ) ) + // stream is in use and should be truncated + m_bSourceRead = FALSE; + else + { + m_nMode &= ~STREAM_TRUNC; + m_bSourceRead = TRUE; + } + } + else + SetError( SVSTREAM_CANNOT_MAKE ); + } + catch ( ContentCreationException& ) + { + SetError( ERRCODE_IO_GENERAL ); + } + catch ( RuntimeException& ) + { + SetError( ERRCODE_IO_GENERAL ); + } + catch ( Exception& ) + { + } + + m_bModified = FALSE; + m_aName = m_aOriginalName; + m_aContentType = m_aOriginalContentType; + return ( GetError() == ERRCODE_NONE ); +} + +BOOL UCBStorageStream_Impl::Clear() +{ + BOOL bRet = ( m_pAntiImpl == NULL ); + DBG_ASSERT( bRet, "Removing used stream!" ); + if( bRet ) + { + Free(); + } + + return bRet; +} + +void UCBStorageStream_Impl::Free() +{ +#if OSL_DEBUG_LEVEL > 1 + if ( m_pStream ) + { + if ( m_aTempURL.Len() ) + --nOpenFiles; + else + --nOpenStreams; + } +#endif + + m_nRepresentMode = nonset; + m_rSource = Reference< XInputStream >(); + DELETEZ( m_pStream ); +} + +void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode ) +{ + sal_Bool isWritable = (( m_nMode & STREAM_WRITE ) != 0 ); + if ( isWritable ) + { + // once stream was writable, never reset to readonly + nMode |= STREAM_WRITE; + } + + m_nMode = nMode; + Free(); + + if ( nMode & STREAM_TRUNC ) + { + m_bSourceRead = 0; // usually it should be 0 already but just in case... + + if ( m_aTempURL.Len() ) + { + ::utl::UCBContentHelper::Kill( m_aTempURL ); + m_aTempURL.Erase(); + } + } +} + +UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) +{ + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorageStream ! + pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey ); + pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress ) +{ + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorageStream ! + pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey, bRepair, xProgress ); + pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl *pImpl ) + : pImp( pImpl ) +{ + pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used + pImp->m_pAntiImpl = this; + SetError( pImp->m_nError ); + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorageStream::~UCBStorageStream() +{ + if ( pImp->m_nMode & STREAM_WRITE ) + pImp->Flush(); + pImp->m_pAntiImpl = NULL; + pImp->Free(); + pImp->ReleaseRef(); +} + +ULONG UCBStorageStream::Read( void * pData, ULONG nSize ) +{ + //return pImp->m_pStream->Read( pData, nSize ); + return pImp->GetData( pData, nSize ); +} + +ULONG UCBStorageStream::Write( const void* pData, ULONG nSize ) +{ +/* + // mba: does occur in writer ! + if ( pImp->m_bCommited ) + { + DBG_ERROR("Writing while commit is in progress!" ); + return 0; + } +*/ + // pImp->m_bModified = TRUE; + //return pImp->m_pStream->Write( pData, nSize ); + return pImp->PutData( pData, nSize ); +} + +ULONG UCBStorageStream::Seek( ULONG nPos ) +{ + //return pImp->m_pStream->Seek( nPos ); + return pImp->Seek( nPos ); +} + +ULONG UCBStorageStream::Tell() +{ + if( !pImp->Init() ) + return 0; + return pImp->m_pStream->Tell(); +} + +void UCBStorageStream::Flush() +{ + // streams are never really transacted, so flush also means commit ! + Commit(); +} + +BOOL UCBStorageStream::SetSize( ULONG nNewSize ) +{ +/* + if ( pImp->m_bCommited ) + { + DBG_ERROR("Changing stream size while commit is in progress!" ); + return FALSE; + } +*/ + // pImp->m_bModified = TRUE; + //return pImp->m_pStream->SetStreamSize( nNewSize ); + pImp->SetSize( nNewSize ); + return !pImp->GetError(); +} + +BOOL UCBStorageStream::Validate( BOOL bWrite ) const +{ + return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); +} + +BOOL UCBStorageStream::ValidateMode( StreamMode m ) const +{ + // ??? + if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx + return TRUE; + USHORT nCurMode = 0xFFFF; + if( ( m & 3 ) == STREAM_READ ) + { + // only SHARE_DENYWRITE or SHARE_DENYALL allowed + if( ( ( m & STREAM_SHARE_DENYWRITE ) + && ( nCurMode & STREAM_SHARE_DENYWRITE ) ) + || ( ( m & STREAM_SHARE_DENYALL ) + && ( nCurMode & STREAM_SHARE_DENYALL ) ) ) + return TRUE; + } + else + { + // only SHARE_DENYALL allowed + // storages open in r/o mode are OK, since only + // the commit may fail + if( ( m & STREAM_SHARE_DENYALL ) + && ( nCurMode & STREAM_SHARE_DENYALL ) ) + return TRUE; + } + + return TRUE; +} + +const SvStream* UCBStorageStream::GetSvStream() const +{ + if( !pImp->Init() ) + return NULL; + + pImp->CopySourceToTemporary(); + return pImp->m_pStream; // should not live longer then pImp!!! +} + +SvStream* UCBStorageStream::GetModifySvStream() +{ + return (SvStream*)pImp; +} + +Reference< XInputStream > UCBStorageStream::GetXInputStream() const +{ + return pImp->GetXInputStream(); +} + +BOOL UCBStorageStream::Equals( const BaseStorageStream& rStream ) const +{ + // ??? + return ((BaseStorageStream*) this ) == &rStream; +} + +BOOL UCBStorageStream::Commit() +{ + // mark this stream for sending it on root commit + pImp->FlushData(); + return TRUE; +} + +BOOL UCBStorageStream::Revert() +{ + return pImp->Revert(); +} + +BOOL UCBStorageStream::CopyTo( BaseStorageStream* pDestStm ) +{ + if( !pImp->Init() ) + return FALSE; + + UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pDestStm ); + if ( pStg ) + pStg->pImp->m_aContentType = pImp->m_aContentType; + + pDestStm->SetSize( 0 ); + Seek( STREAM_SEEK_TO_END ); + INT32 n = Tell(); + if( n < 0 ) + return FALSE; + + if( pDestStm->SetSize( n ) && n ) + { + BYTE* p = new BYTE[ 4096 ]; + Seek( 0L ); + pDestStm->Seek( 0L ); + while( n ) + { + UINT32 nn = n; + if( nn > 4096 ) + nn = 4096; + if( Read( p, nn ) != nn ) + break; + if( pDestStm->Write( p, nn ) != nn ) + break; + n -= nn; + } + + delete[] p; + } + + return TRUE; +} + +BOOL UCBStorageStream::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) +{ + if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) + return FALSE; + + if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL ) + { + ::rtl::OUString aTmp; + rValue >>= aTmp; + pImp->m_aContentType = aTmp; + } + + try + { + if ( pImp->m_pContent ) + { + pImp->m_pContent->setPropertyValue( rName, rValue ); + return TRUE; + } + } + catch ( Exception& ) + { + } + + return FALSE; +} + +BOOL UCBStorageStream::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + try + { + if ( pImp->m_pContent ) + { + rValue = pImp->m_pContent->getPropertyValue( rName ); + return TRUE; + } + } + catch ( Exception& ) + { + } + + return FALSE; +} + +UCBStorage::UCBStorage( SvStream& rStrm, BOOL bDirect ) +{ + String aURL = GetLinkedFile( rStrm ); + if ( aURL.Len() ) + { + StreamMode nMode = STREAM_READ; + if( rStrm.IsWritable() ) + nMode = STREAM_READ | STREAM_WRITE; + + ::ucbhelper::Content aContent( aURL, Reference < XCommandEnvironment >() ); + pImp = new UCBStorage_Impl( aContent, aURL, nMode, this, bDirect, TRUE ); + } + else + { + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorage ! + pImp = new UCBStorage_Impl( rStrm, this, bDirect ); + } + + pImp->AddRef(); + pImp->Init(); + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorage::UCBStorage( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot ) +{ + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorage ! + pImp = new UCBStorage_Impl( rContent, rName, nMode, this, bDirect, bIsRoot ); + pImp->AddRef(); + pImp->Init(); + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) +{ + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorage ! + pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, bIsRepair, xProgressHandler ); + pImp->AddRef(); + pImp->Init(); + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot ) +{ + // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized + // to class UCBStorage ! + pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, sal_False, Reference< XProgressHandler >() ); + pImp->AddRef(); + pImp->Init(); + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorage::UCBStorage( UCBStorage_Impl *pImpl ) + : pImp( pImpl ) +{ + pImp->m_pAntiImpl = this; + SetError( pImp->m_nError ); + pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used + StorageBase::m_nMode = pImp->m_nMode; +} + +UCBStorage::~UCBStorage() +{ + if ( pImp->m_bIsRoot && pImp->m_bDirect && ( !pImp->m_pTempFile || pImp->m_pSource ) ) + // DirectMode is simulated with an AutoCommit + Commit(); + + pImp->m_pAntiImpl = NULL; + pImp->ReleaseRef(); +} + +UCBStorage_Impl::UCBStorage_Impl( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) + : m_pAntiImpl( pStorage ) + , m_pContent( new ::ucbhelper::Content( rContent ) ) + , m_pTempFile( NULL ) + , m_pSource( NULL ) + //, m_pStream( NULL ) + , m_nError( 0 ) + , m_nMode( nMode ) + , m_bModified( FALSE ) + , m_bCommited( FALSE ) + , m_bDirect( bDirect ) + , m_bIsRoot( bIsRoot ) + , m_bDirty( FALSE ) + , m_bIsLinked( TRUE ) + , m_bListCreated( FALSE ) + , m_nFormat( 0 ) + , m_aClassId( SvGlobalName() ) + , m_bRepairPackage( bIsRepair ) + , m_xProgressHandler( xProgressHandler ) + , m_pUNOStorageHolderList( NULL ) + +{ + String aName( rName ); + if( !aName.Len() ) + { + // no name given = use temporary name! + DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" ); + m_pTempFile = new ::utl::TempFile; + m_pTempFile->EnableKillingFile( TRUE ); + m_aName = m_aOriginalName = aName = m_pTempFile->GetURL(); + } + + m_aURL = rName; +} + +UCBStorage_Impl::UCBStorage_Impl( const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) + : m_pAntiImpl( pStorage ) + , m_pContent( NULL ) + , m_pTempFile( NULL ) + , m_pSource( NULL ) + //, m_pStream( NULL ) + , m_nError( 0 ) + , m_nMode( nMode ) + , m_bModified( FALSE ) + , m_bCommited( FALSE ) + , m_bDirect( bDirect ) + , m_bIsRoot( bIsRoot ) + , m_bDirty( FALSE ) + , m_bIsLinked( FALSE ) + , m_bListCreated( FALSE ) + , m_nFormat( 0 ) + , m_aClassId( SvGlobalName() ) + , m_bRepairPackage( bIsRepair ) + , m_xProgressHandler( xProgressHandler ) + , m_pUNOStorageHolderList( NULL ) +{ + String aName( rName ); + if( !aName.Len() ) + { + // no name given = use temporary name! + DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" ); + m_pTempFile = new ::utl::TempFile; + m_pTempFile->EnableKillingFile( TRUE ); + m_aName = m_aOriginalName = aName = m_pTempFile->GetURL(); + } + + if ( m_bIsRoot ) + { + // create the special package URL for the package content + String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://"); + aTemp += String(INetURLObject::encode( aName, INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL )); + m_aURL = aTemp; + + if ( m_nMode & STREAM_WRITE ) + { + // the root storage opens the package, so make sure that there is any + SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aName, STREAM_STD_READWRITE, m_pTempFile != 0 /* bFileExists */ ); + delete pStream; + } + } + else + { + // substorages are opened like streams: the URL is a "child URL" of the root package URL + m_aURL = rName; + if ( m_aURL.CompareToAscii( "vnd.sun.star.pkg://", 19 ) != 0 ) + m_bIsLinked = TRUE; + } +} + +UCBStorage_Impl::UCBStorage_Impl( SvStream& rStream, UCBStorage* pStorage, BOOL bDirect ) + : m_pAntiImpl( pStorage ) + , m_pContent( NULL ) + , m_pTempFile( new ::utl::TempFile ) + , m_pSource( &rStream ) + , m_nError( 0 ) + , m_bModified( FALSE ) + , m_bCommited( FALSE ) + , m_bDirect( bDirect ) + , m_bIsRoot( TRUE ) + , m_bDirty( FALSE ) + , m_bIsLinked( FALSE ) + , m_bListCreated( FALSE ) + , m_nFormat( 0 ) + , m_aClassId( SvGlobalName() ) + , m_bRepairPackage( FALSE ) + , m_pUNOStorageHolderList( NULL ) +{ + // opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call, + // which will be called in the storages' dtor + m_pTempFile->EnableKillingFile( TRUE ); + DBG_ASSERT( !bDirect, "Storage on a stream must not be opened in direct mode!" ); + + // UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only + // accessed readonly + // the root storage opens the package; create the special package URL for the package content + String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://"); + aTemp += String(INetURLObject::encode( m_pTempFile->GetURL(), INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL )); + m_aURL = aTemp; + + // copy data into the temporary file + SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), STREAM_STD_READWRITE, sal_True /* bFileExists */ ); + if ( pStream ) + { + rStream.Seek(0); + rStream >> *pStream; + pStream->Flush(); + DELETEZ( pStream ); + } + + // close stream and let content access the file + m_pSource->Seek(0); + + // check opening mode + m_nMode = STREAM_READ; + if( rStream.IsWritable() ) + m_nMode = STREAM_READ | STREAM_WRITE; +} + +void UCBStorage_Impl::Init() +{ + // name is last segment in URL + INetURLObject aObj( m_aURL ); + if ( !m_aName.Len() ) + // if the name was not already set to a temp name + m_aName = m_aOriginalName = aObj.GetLastName(); + + // don't create the content for disk spanned files, avoid too early access to directory and/or manifest + if ( !m_pContent && !( m_nMode & STORAGE_DISKSPANNED_MODE ) ) + CreateContent(); + + if ( m_nMode & STORAGE_DISKSPANNED_MODE ) + { + // Hack! Avoid access to the manifest file until mediatype is not available in the first segment of a + // disk spanned file + m_aContentType = m_aOriginalContentType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.xml.impress") ); + } + else if ( m_pContent ) + { + if ( m_bIsLinked ) + { + if( m_bIsRoot ) + { + ReadContent(); + if ( m_nError == ERRCODE_NONE ) + { + // read the manifest.xml file + aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("META-INF") ) ); + aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("manifest.xml") ) ); + + // create input stream + SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aObj.GetMainURL( INetURLObject::NO_DECODE ), STREAM_STD_READ ); + // no stream means no manifest.xml + if ( pStream ) + { + if ( !pStream->GetError() ) + { + ::utl::OInputStreamWrapper* pHelper = new ::utl::OInputStreamWrapper( *pStream ); + com::sun::star::uno::Reference < ::com::sun::star::io::XInputStream > xInputStream( pHelper ); + + // create a manifest reader object that will read in the manifest from the stream + Reference < ::com::sun::star::packages::manifest::XManifestReader > xReader = + Reference< ::com::sun::star::packages::manifest::XManifestReader > + ( ::comphelper::getProcessServiceFactory()->createInstance( + ::rtl::OUString(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 = 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); + } + + BOOL bIsFolder( xRow->getBoolean(2) ); + sal_Int64 nSize = xRow->getLong(4); + UCBStorageElement_Impl* pElement = new UCBStorageElement_Impl( aTitle, bIsFolder, (ULONG) nSize ); + m_aChildrenList.Insert( pElement, LIST_APPEND ); + + sal_Bool bIsOfficeDocument = m_bIsLinked || ( m_aClassId != SvGlobalName() ); + if ( bIsFolder ) + { + if ( m_bIsLinked ) + OpenStorage( pElement, m_nMode, m_bDirect ); + if ( pElement->m_xStorage.Is() ) + pElement->m_xStorage->Init(); + } + else if ( bIsOfficeDocument ) + { + // streams can be external OLE objects, so they are now folders, but storages! + String aName( m_aURL ); + aName += '/'; + aName += String( xRow->getString(1) ); + + Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv; + if ( m_bRepairPackage ) + { + xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(), + m_xProgressHandler ); + aName += String( RTL_CONSTASCII_USTRINGPARAM( "?repairpackage" ) ); + } + + ::ucbhelper::Content aContent( aName, xComEnv ); + + ::rtl::OUString aMediaType; + Any aAny = aContent.getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")) ); + if ( ( aAny >>= aMediaType ) && ( aMediaType.compareToAscii("application/vnd.sun.star.oleobject") == 0 ) ) + pElement->m_bIsStorage = TRUE; + else if ( !aMediaType.getLength() ) + { + // older files didn't have that special content type, so they must be detected + OpenStream( pElement, STREAM_STD_READ, m_bDirect ); + if ( Storage::IsStorageFile( pElement->m_xStream ) ) + pElement->m_bIsStorage = TRUE; + else + pElement->m_xStream->Free(); + } + } + } + } + } + catch ( InteractiveIOException& r ) + { + if ( r.Code != IOErrorCode_NOT_EXISTING ) + SetError( ERRCODE_IO_GENERAL ); + } + catch ( CommandAbortedException& ) + { + // any command wasn't executed successfully - not specified + if ( !( m_nMode & STREAM_WRITE ) ) + // if the folder was just inserted and not already commited, this is not an error! + SetError( ERRCODE_IO_GENERAL ); + } + catch ( RuntimeException& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + } + catch ( ResultSetException& ) + { + // means that the package file is broken + SetError( ERRCODE_IO_BROKENPACKAGE ); + } + catch ( SQLException& ) + { + // means that the file can be broken + SetError( ERRCODE_IO_WRONGFORMAT ); + } + catch ( Exception& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + } +} + +void UCBStorage_Impl::SetError( long nError ) +{ + if ( !m_nError ) + { + m_nError = nError; + if ( m_pAntiImpl ) m_pAntiImpl->SetError( nError ); + } +} + +sal_Int32 UCBStorage_Impl::GetObjectCount() +{ + sal_Int32 nCount = m_aChildrenList.Count(); + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + while ( pElement ) + { + DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); + if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) + nCount += pElement->m_xStorage->GetObjectCount(); + pElement = m_aChildrenList.Next(); + } + + return nCount; +} + +::rtl::OUString Find_Impl( const Sequence < Sequence < PropertyValue > >& rSequence, const ::rtl::OUString& rPath ) +{ + BOOL bFound = FALSE; + for ( sal_Int32 nSeqs=0; nSeqs<rSequence.getLength(); nSeqs++ ) + { + const Sequence < PropertyValue >& rMyProps = rSequence[nSeqs]; + ::rtl::OUString aType; + + for ( sal_Int32 nProps=0; nProps<rMyProps.getLength(); nProps++ ) + { + const PropertyValue& rAny = rMyProps[nProps]; + if ( rAny.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("FullPath")) ) + { + rtl::OUString aTmp; + if ( ( rAny.Value >>= aTmp ) && aTmp == rPath ) + bFound = 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(); + + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + while ( pElement ) + { + DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); + if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) + pElement->m_xStorage->SetProps( rSequence, aPath ); + else + { + String aElementPath( aPath ); + aElementPath += pElement->m_aName; + pElement->SetContentType( Find_Impl( rSequence, aElementPath ) ); + } + + pElement = m_aChildrenList.Next(); + } + + if ( m_aContentType.Len() ) + { + // get the clipboard format using the content type + ::com::sun::star::datatransfer::DataFlavor aDataFlavor; + aDataFlavor.MimeType = m_aContentType; + m_nFormat = SotExchange::GetFormat( aDataFlavor ); + + // get the ClassId using the clipboard format ( internal table ) + m_aClassId = GetClassId_Impl( m_nFormat ); + + // get human presentable name using the clipboard format + SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor ); + m_aUserTypeName = aDataFlavor.HumanPresentableName; + } +} + +void UCBStorage_Impl::GetProps( sal_Int32& nProps, Sequence < Sequence < PropertyValue > >& rSequence, const String& rPath ) +{ + // first my own properties + Sequence < PropertyValue > aProps(2); + + // first property is the "FullPath" name + // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder + String aPath( rPath ); + if ( !m_bIsRoot ) + aPath += m_aName; + aPath += '/'; + aProps[0].Name = ::rtl::OUString(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 + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + while ( pElement ) + { + DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); + if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) + // storages add there properties by themselves ( see above ) + pElement->m_xStorage->GetProps( nProps, rSequence, aPath ); + else + { + // properties of streams + String aElementPath( aPath ); + aElementPath += pElement->m_aName; + aProps[0].Name = ::rtl::OUString(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; + } + + pElement = m_aChildrenList.Next(); + } +} + +UCBStorage_Impl::~UCBStorage_Impl() +{ + if ( m_pUNOStorageHolderList ) + { + for ( UNOStorageHolderList::iterator aIter = m_pUNOStorageHolderList->begin(); + aIter != m_pUNOStorageHolderList->end(); ++aIter ) + if ( *aIter ) + { + (*aIter)->InternalDispose(); + (*aIter)->release(); + (*aIter) = NULL; + } + + m_pUNOStorageHolderList->clear(); + DELETEZ( m_pUNOStorageHolderList ); + } + + // first delete elements! + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + while ( pElement ) + { + delete pElement; + pElement = m_aChildrenList.Next(); + } + + m_aChildrenList.Clear(); + delete m_pContent; + delete m_pTempFile; +} + +BOOL UCBStorage_Impl::Insert( ::ucbhelper::Content *pContent ) +{ + // a new substorage is inserted into a UCBStorage ( given by the parameter pContent ) + // it must be inserted with a title and a type + BOOL bRet = FALSE; + + try + { + Sequence< ContentInfo > aInfo = pContent->queryCreatableContentsInfo(); + sal_Int32 nCount = aInfo.getLength(); + if ( nCount == 0 ) + return sal_False; + + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + // Simply look for the first KIND_FOLDER... + const ContentInfo & rCurr = aInfo[i]; + if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER ) + { + // Make sure the only required bootstrap property is "Title", + const Sequence< Property > & rProps = rCurr.Properties; + if ( rProps.getLength() != 1 ) + continue; + + if ( !rProps[ 0 ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) + continue; + + Sequence < ::rtl::OUString > aNames(1); + ::rtl::OUString* pNames = aNames.getArray(); + pNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ); + Sequence < Any > aValues(1); + Any* pValues = aValues.getArray(); + pValues[0] = makeAny( ::rtl::OUString( m_aName ) ); + + Content aNewFolder; + if ( !pContent->insertNewContent( rCurr.Type, aNames, aValues, aNewFolder ) ) + continue; + + // remove old content, create an "empty" new one and initialize it with the new inserted + DELETEZ( m_pContent ); + m_pContent = new ::ucbhelper::Content( aNewFolder ); + bRet = TRUE; + } + } + } + catch ( CommandAbortedException& ) + { + // any command wasn't executed successfully - not specified + SetError( ERRCODE_IO_GENERAL ); + } + catch ( RuntimeException& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + } + catch ( Exception& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + } + + return bRet; +} + +sal_Int16 UCBStorage_Impl::Commit() +{ + // send all changes to the package + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + sal_Int16 nRet = COMMIT_RESULT_NOTHING_TO_DO; + + // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no + // commit command has been sent + if ( ( m_nMode & STREAM_WRITE ) && ( m_bCommited || m_bDirect ) ) + { + try + { + // all errors will be caught in the "catch" statement outside the loop + while ( pElement && nRet ) + { + ::ucbhelper::Content* pContent = pElement->GetContent(); + BOOL bDeleteContent = FALSE; + if ( !pContent && pElement->IsModified() ) + { + // if the element has never been opened, no content has been created until now + bDeleteContent = TRUE; // remember to delete it later + String aName( m_aURL ); + aName += '/'; + aName += pElement->m_aOriginalName; + pContent = new ::ucbhelper::Content( aName, Reference< ::com::sun::star::ucb::XCommandEnvironment > () ); + } + + if ( pElement->m_bIsRemoved ) + { + // was it inserted, then removed (so there would be nothing to do!) + if ( !pElement->m_bIsInserted ) + { + // first remove all open stream handles + if( !pElement->m_xStream.Is() || pElement->m_xStream->Clear() ) + { + pContent->executeCommand( ::rtl::OUString(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 <<= (BOOL) TRUE; + pElement->m_xStream->m_pContent->setPropertyValue(String::CreateFromAscii("Encrypted"), aValue ); + } + + pContent = pElement->GetContent(); + } + + if ( pElement->m_aName != pElement->m_aOriginalName ) + { + // name ( title ) of the element was changed + nLocalRet = COMMIT_RESULT_SUCCESS; + Any aAny; + aAny <<= (rtl::OUString) pElement->m_aName; + pContent->setPropertyValue( ::rtl::OUString(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; + + pElement = m_aChildrenList.Next(); + } + } + catch ( ContentCreationException& ) + { + // content could not be created + SetError( ERRCODE_IO_NOTEXISTS ); + return COMMIT_RESULT_FAILURE; + } + catch ( CommandAbortedException& ) + { + // any command wasn't executed successfully - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + catch ( RuntimeException& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + catch ( Exception& ) + { + // any other error - not specified + SetError( ERRCODE_IO_GENERAL ); + return COMMIT_RESULT_FAILURE; + } + + if ( m_bIsRoot && m_pContent ) + { + // the root storage must flush the root package content + if ( nRet == COMMIT_RESULT_SUCCESS ) + { + try + { + // commit the media type to the JAR file + // clipboard format and ClassId will be retrieved from the media type when the file is loaded again + Any aType; + aType <<= (rtl::OUString) m_aContentType; + m_pContent->setPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")), aType ); + + if ( m_bIsLinked ) + { + // write a manifest file + // first create a subfolder "META-inf" + Content aNewSubFolder; + BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, String::CreateFromAscii("META-INF"), aNewSubFolder ); + if ( bRet ) + { + // create a stream to write the manifest file - use a temp file + String aURL( aNewSubFolder.getURL() ); + ::utl::TempFile* pTempFile = new ::utl::TempFile( &aURL ); + + // get the stream from the temp file and create an output stream wrapper + SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE ); + ::utl::OOutputStreamWrapper* pHelper = new ::utl::OOutputStreamWrapper( *pStream ); + com::sun::star::uno::Reference < ::com::sun::star::io::XOutputStream > xOutputStream( pHelper ); + + // create a manifest writer object that will fill the stream + Reference < ::com::sun::star::packages::manifest::XManifestWriter > xWriter = + Reference< ::com::sun::star::packages::manifest::XManifestWriter > + ( ::comphelper::getProcessServiceFactory()->createInstance( + ::rtl::OUString(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 + UCBStorageElement_Impl* pInnerElement = m_aChildrenList.First(); + BOOL bRet = TRUE; + while ( pInnerElement && bRet ) + { + UCBStorageElement_Impl* pNext = m_aChildrenList.Next(); + if ( pInnerElement->m_bIsRemoved ) + { + // is this correct use of our list class ?! + m_aChildrenList.Remove( pInnerElement ); + } + else + { + pInnerElement->m_aOriginalName = pInnerElement->m_aName; + pInnerElement->m_bIsInserted = FALSE; + } + + pInnerElement = pNext; + } + } + + m_bCommited = FALSE; + } + + return nRet; +} + +BOOL UCBStorage_Impl::Revert() +{ + UCBStorageElement_Impl* pElement = m_aChildrenList.First(); + BOOL bRet = TRUE; + while ( pElement && bRet ) + { + pElement->m_bIsRemoved = FALSE; + if ( pElement->m_bIsInserted ) + { + m_aChildrenList.Remove( pElement ); // correct usage of list ??? + } + else + { + if ( pElement->m_xStream.Is() ) + { + pElement->m_xStream->m_bCommited = sal_False; + pElement->m_xStream->Revert(); + } + else if ( pElement->m_xStorage.Is() ) + { + pElement->m_xStorage->m_bCommited = sal_False; + pElement->m_xStorage->Revert(); + } + + pElement->m_aName = pElement->m_aOriginalName; + pElement->m_bIsRemoved = FALSE; + } + + pElement = m_aChildrenList.Next(); + } + + return bRet; +} + +const String& UCBStorage::GetName() const +{ + return pImp->m_aName; // pImp->m_aURL ?! +} + +BOOL UCBStorage::IsRoot() const +{ + return pImp->m_bIsRoot; +} + +void UCBStorage::SetDirty() +{ + pImp->m_bDirty = TRUE; +} + +void UCBStorage::SetClass( const SvGlobalName & rClass, ULONG nOriginalClipFormat, const String & rUserTypeName ) +{ + pImp->m_aClassId = rClass; + pImp->m_nFormat = nOriginalClipFormat; + pImp->m_aUserTypeName = rUserTypeName; + + // in UCB storages only the content type will be stored, all other information can be reconstructed + // ( see the UCBStorage_Impl::Init() method ) + ::com::sun::star::datatransfer::DataFlavor aDataFlavor; + SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor ); + pImp->m_aContentType = aDataFlavor.MimeType; +} + +void UCBStorage::SetClassId( const ClsId& rClsId ) +{ + pImp->m_aClassId = SvGlobalName( (const CLSID&) rClsId ); + if ( pImp->m_aClassId == SvGlobalName() ) + return; + + // in OLE storages the clipboard format an the user name will be transferred when a storage is copied because both are + // stored in one the substreams + // UCB storages store the content type information as content type in the manifest file and so this information must be + // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from + // the content type + pImp->m_nFormat = GetFormatId_Impl( pImp->m_aClassId ); + if ( pImp->m_nFormat ) + { + ::com::sun::star::datatransfer::DataFlavor aDataFlavor; + SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor ); + pImp->m_aUserTypeName = aDataFlavor.HumanPresentableName; + pImp->m_aContentType = aDataFlavor.MimeType; + } +} + +const ClsId& UCBStorage::GetClassId() const +{ + return ( const ClsId& ) pImp->m_aClassId.GetCLSID(); +} + +void UCBStorage::SetConvertClass( const SvGlobalName & /*rConvertClass*/, ULONG /*nOriginalClipFormat*/, const String & /*rUserTypeName*/ ) +{ + // ??? +} + +BOOL UCBStorage::ShouldConvert() +{ + // ??? + return FALSE; +} + +SvGlobalName UCBStorage::GetClassName() +{ + return pImp->m_aClassId; +} + +ULONG UCBStorage::GetFormat() +{ + return pImp->m_nFormat; +} + +String UCBStorage::GetUserName() +{ + DBG_ERROR("UserName is not implemented in UCB storages!" ); + return pImp->m_aUserTypeName; +} + +void UCBStorage::FillInfoList( SvStorageInfoList* pList ) const +{ + // put information in childrenlist into StorageInfoList + UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); + while ( pElement ) + { + if ( !pElement->m_bIsRemoved ) + { + // problem: what about the size of a substorage ?! + ULONG nSize = pElement->m_nSize; + if ( pElement->m_xStream.Is() ) + nSize = pElement->m_xStream->GetSize(); + SvStorageInfo aInfo( pElement->m_aName, nSize, pElement->m_bIsStorage ); + pList->Append( aInfo ); + } + + pElement = pImp->m_aChildrenList.Next(); + } +} + +BOOL UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl& rElement, BaseStorage* pDest, const String& rNew ) const +{ + // insert stream or storage into the list or stream of the destination storage + // not into the content, this will be done on commit ! + // be aware of name changes ! + if ( !rElement.m_bIsStorage ) + { + // copy the streams data + // the destination stream must not be open + BaseStorageStream* pOtherStream = pDest->OpenStream( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ); + BaseStorageStream* pStream = NULL; + BOOL bDeleteStream = FALSE; + + // if stream is already open, it is allowed to copy it, so be aware of this + if ( rElement.m_xStream.Is() ) + pStream = rElement.m_xStream->m_pAntiImpl; + if ( !pStream ) + { + pStream = ( const_cast < UCBStorage* > (this) )->OpenStream( rElement.m_aName, STREAM_STD_READ, pImp->m_bDirect ); + bDeleteStream = TRUE; + } + + pStream->CopyTo( pOtherStream ); + SetError( pStream->GetError() ); + if( pOtherStream->GetError() ) + pDest->SetError( pOtherStream->GetError() ); + else + pOtherStream->Commit(); + + if ( bDeleteStream ) + delete pStream; + delete pOtherStream; + } + else + { + // copy the storage content + // the destination storage must not be open + BaseStorage* pStorage = NULL; + + // if stream is already open, it is allowed to copy it, so be aware of this + BOOL bDeleteStorage = FALSE; + if ( rElement.m_xStorage.Is() ) + pStorage = rElement.m_xStorage->m_pAntiImpl; + if ( !pStorage ) + { + pStorage = ( const_cast < UCBStorage* > (this) )->OpenStorage( rElement.m_aName, pImp->m_nMode, pImp->m_bDirect ); + bDeleteStorage = TRUE; + } + + UCBStorage* pUCBDest = PTR_CAST( UCBStorage, pDest ); + UCBStorage* pUCBCopy = PTR_CAST( UCBStorage, pStorage ); + + BOOL bOpenUCBStorage = pUCBDest && pUCBCopy; + BaseStorage* pOtherStorage = bOpenUCBStorage ? + pDest->OpenUCBStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ) : + pDest->OpenOLEStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ); + + // For UCB storages, the class id and the format id may differ, + // do passing the class id is not sufficient. + if( bOpenUCBStorage ) + pOtherStorage->SetClass( pStorage->GetClassName(), + pStorage->GetFormat(), + pUCBCopy->pImp->m_aUserTypeName ); + else + pOtherStorage->SetClassId( pStorage->GetClassId() ); + pStorage->CopyTo( pOtherStorage ); + SetError( pStorage->GetError() ); + if( pOtherStorage->GetError() ) + pDest->SetError( pOtherStorage->GetError() ); + else + pOtherStorage->Commit(); + + if ( bDeleteStorage ) + delete pStorage; + delete pOtherStorage; + } + + return BOOL( Good() && pDest->Good() ); +} + +UCBStorageElement_Impl* UCBStorage::FindElement_Impl( const String& rName ) const +{ + DBG_ASSERT( rName.Len(), "Name is empty!" ); + UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); + while ( pElement ) + { + if ( pElement->m_aName == rName && !pElement->m_bIsRemoved ) + break; + pElement = pImp->m_aChildrenList.Next(); + } + + return pElement; +} + +BOOL UCBStorage::CopyTo( BaseStorage* pDestStg ) const +{ + DBG_ASSERT( pDestStg != ((BaseStorage*)this), "Self-Copying is not possible!" ); + if ( pDestStg == ((BaseStorage*)this) ) + return FALSE; + + // perhaps it's also a problem if one storage is a parent of the other ?! + // or if not: could be optimized ?! + + // For UCB storages, the class id and the format id may differ, + // do passing the class id is not sufficient. + if( pDestStg->ISA( UCBStorage ) ) + pDestStg->SetClass( pImp->m_aClassId, pImp->m_nFormat, + pImp->m_aUserTypeName ); + else + pDestStg->SetClassId( GetClassId() ); + pDestStg->SetDirty(); + + BOOL bRet = TRUE; + UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); + while ( pElement && bRet ) + { + if ( !pElement->m_bIsRemoved ) + bRet = CopyStorageElement_Impl( *pElement, pDestStg, pElement->m_aName ); + pElement = pImp->m_aChildrenList.Next(); + } + + if( !bRet ) + SetError( pDestStg->GetError() ); + return BOOL( Good() && pDestStg->Good() ); +} + +BOOL UCBStorage::CopyTo( const String& rElemName, BaseStorage* pDest, const String& rNew ) +{ + if( !rElemName.Len() ) + return FALSE; + + if ( pDest == ((BaseStorage*) this) ) + { + // can't double an element + return FALSE; + } + else + { + // for copying no optimization is usefull, because in every case the stream data must be copied + UCBStorageElement_Impl* pElement = FindElement_Impl( rElemName ); + if ( pElement ) + return CopyStorageElement_Impl( *pElement, pDest, rNew ); + else + { + SetError( SVSTREAM_FILE_NOT_FOUND ); + return FALSE; + } + } +} + +BOOL UCBStorage::Commit() +{ + // mark this storage for sending it on root commit + pImp->m_bCommited = TRUE; + if ( pImp->m_bIsRoot ) + // the root storage coordinates commiting by sending a Commit command to its content + return ( pImp->Commit() != COMMIT_RESULT_FAILURE ); + else + return TRUE; +} + +BOOL UCBStorage::Revert() +{ + return pImp->Revert(); +} + +BaseStorageStream* UCBStorage::OpenStream( const String& rEleName, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) +{ + if( !rEleName.Len() ) + return NULL; + + // try to find the storage element + UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + if ( !pElement ) + { + // element does not exist, check if creation is allowed + if( ( nMode & STREAM_NOCREATE ) ) + { + SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); + String aName( pImp->m_aURL ); + aName += '/'; + aName += rEleName; + UCBStorageStream* pStream = new UCBStorageStream( aName, nMode, bDirect, pKey, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); + pStream->SetError( GetError() ); + pStream->pImp->m_aName = rEleName; + return pStream; + } + else + { + // create a new UCBStorageElement and insert it into the list + pElement = new UCBStorageElement_Impl( rEleName ); + pElement->m_bIsInserted = TRUE; + pImp->m_aChildrenList.Insert( pElement, LIST_APPEND ); + } + } + + if ( pElement && !pElement->m_bIsFolder ) + { + // check if stream is already created + if ( pElement->m_xStream.Is() ) + { + // stream has already been created; if it has no external reference, it may be opened another time + if ( pElement->m_xStream->m_pAntiImpl ) + { + DBG_ERROR("Stream is already open!" ); + SetError( SVSTREAM_ACCESS_DENIED ); // ??? + return NULL; + } + else + { + // check if stream is opened with the same keyword as before + // if not, generate a new stream because it could be encrypted vs. decrypted! + ByteString aKey; + if ( pKey ) + aKey = *pKey; + if ( pElement->m_xStream->m_aKey == aKey ) + { + pElement->m_xStream->PrepareCachedForReopen( nMode ); + + // DBG_ASSERT( bDirect == pElement->m_xStream->m_bDirect, "Wrong DirectMode!" ); + return new UCBStorageStream( pElement->m_xStream ); + } + } + } + + // stream is opened the first time + pImp->OpenStream( pElement, nMode, bDirect, pKey ); + + // if name has been changed before creating the stream: set name! + pElement->m_xStream->m_aName = rEleName; + return new UCBStorageStream( pElement->m_xStream ); + } + + return NULL; +} + +UCBStorageStream_Impl* UCBStorage_Impl::OpenStream( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) +{ + String aName( m_aURL ); + aName += '/'; + aName += pElement->m_aOriginalName; + pElement->m_xStream = new UCBStorageStream_Impl( aName, nMode, NULL, bDirect, pKey, m_bRepairPackage, m_xProgressHandler ); + return pElement->m_xStream; +} + +BaseStorage* UCBStorage::OpenUCBStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE ); +} + +BaseStorage* UCBStorage::OpenOLEStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, FALSE ); +} + +BaseStorage* UCBStorage::OpenStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) +{ + if( !rEleName.Len() ) + return NULL; + + return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE ); +} + +BaseStorage* UCBStorage::OpenStorage_Impl( const String& rEleName, StreamMode nMode, BOOL bDirect, BOOL bForceUCBStorage ) +{ + // try to find the storage element + UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + if ( !pElement ) + { + // element does not exist, check if creation is allowed + if( ( nMode & STREAM_NOCREATE ) ) + { + SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); + String aName( pImp->m_aURL ); + aName += '/'; + aName += rEleName; // ??? + UCBStorage *pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); + pStorage->pImp->m_bIsRoot = FALSE; + pStorage->pImp->m_bListCreated = sal_True; // the storage is pretty new, nothing to read + pStorage->SetError( GetError() ); + return pStorage; + } + + // create a new UCBStorageElement and insert it into the list + // problem: perhaps an OLEStorage should be created ?! + // Because nothing is known about the element that should be created, an external parameter is needed ! + pElement = new UCBStorageElement_Impl( rEleName ); + pElement->m_bIsInserted = TRUE; + pImp->m_aChildrenList.Insert( pElement, LIST_APPEND ); + } + + if ( !pElement->m_bIsFolder && ( pElement->m_bIsStorage || !bForceUCBStorage ) ) + { + // create OLE storages on a stream ( see ctor of SotStorage ) + // Such a storage will be created on a UCBStorageStream; it will write into the stream + // if it is opened in direct mode or when it is committed. In this case the stream will be + // modified and then it MUST be treated as commited. + if ( !pElement->m_xStream.Is() ) + { + BaseStorageStream* pStr = OpenStream( rEleName, nMode, bDirect ); + UCBStorageStream* pStream = PTR_CAST( UCBStorageStream, pStr ); + if ( !pStream ) + { + SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); + return NULL; + } + + pElement->m_xStream = pStream->pImp; + delete pStream; + } + + pElement->m_xStream->PrepareCachedForReopen( nMode ); + pElement->m_xStream->Init(); + + pElement->m_bIsStorage = TRUE; + return pElement->m_xStream->CreateStorage(); // can only be created in transacted mode + } + else if ( pElement->m_xStorage.Is() ) + { + // storage has already been opened; if it has no external reference, it may be opened another time + if ( pElement->m_xStorage->m_pAntiImpl ) + { + DBG_ERROR("Storage is already open!" ); + SetError( SVSTREAM_ACCESS_DENIED ); // ??? + } + else + { + BOOL bIsWritable = (( pElement->m_xStorage->m_nMode & STREAM_WRITE ) != 0); + if ( !bIsWritable && (( nMode & STREAM_WRITE ) != 0 )) + { + String aName( pImp->m_aURL ); + aName += '/'; + aName += pElement->m_aOriginalName; + UCBStorage* pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); + pElement->m_xStorage = pStorage->pImp; + return pStorage; + } + else + { +// DBG_ASSERT( bDirect == pElement->m_xStorage->m_bDirect, "Wrong DirectMode!" ); + return new UCBStorage( pElement->m_xStorage ); + } + } + } + else if ( !pElement->m_xStream.Is() ) + { + // storage is opened the first time + BOOL bIsWritable = (( pImp->m_nMode & STREAM_WRITE ) != 0 ); + if ( pImp->m_bIsLinked && pImp->m_bIsRoot && bIsWritable ) + { + // make sure that the root storage object has been created before substorages will be created + INetURLObject aFolderObj( pImp->m_aURL ); + String aName = aFolderObj.GetName(); + aFolderObj.removeSegment(); + + Content aFolder( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ), Reference < XCommandEnvironment >() ); + pImp->m_pContent = new Content; + BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, pImp->m_aName, *pImp->m_pContent ); + if ( !bRet ) + { + SetError( SVSTREAM_CANNOT_MAKE ); + return NULL; + } + } + + UCBStorage_Impl* pStor = pImp->OpenStorage( pElement, nMode, bDirect ); + if ( pStor ) + { + if ( pElement->m_bIsInserted ) + pStor->m_bListCreated = sal_True; // the storage is pretty new, nothing to read + + return new UCBStorage( pStor ); + } + } + + return NULL; +} + +UCBStorage_Impl* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect ) +{ + UCBStorage_Impl* pRet = NULL; + String aName( m_aURL ); + aName += '/'; + aName += pElement->m_aOriginalName; // ??? + + pElement->m_bIsStorage = pElement->m_bIsFolder = TRUE; + + if ( m_bIsLinked && !::utl::UCBContentHelper::Exists( aName ) ) + { + Content aNewFolder; + BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, pElement->m_aOriginalName, aNewFolder ); + if ( bRet ) + pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler ); + } + else + { + pRet = new UCBStorage_Impl( aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler ); + } + + if ( pRet ) + { + pRet->m_bIsLinked = m_bIsLinked; + pRet->m_bIsRoot = FALSE; + + // if name has been changed before creating the stream: set name! + pRet->m_aName = pElement->m_aOriginalName; + pElement->m_xStorage = pRet; + } + + if ( pRet ) + pRet->Init(); + + return pRet; +} + +BOOL UCBStorage::IsStorage( const String& rEleName ) const +{ + if( !rEleName.Len() ) + return FALSE; + + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement && pElement->m_bIsStorage ); +} + +BOOL UCBStorage::IsStream( const String& rEleName ) const +{ + if( !rEleName.Len() ) + return FALSE; + + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement && !pElement->m_bIsStorage ); +} + +BOOL UCBStorage::IsContained( const String & rEleName ) const +{ + if( !rEleName.Len() ) + return FALSE; + const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + return ( pElement != NULL ); +} + +BOOL UCBStorage::Remove( const String& rEleName ) +{ + if( !rEleName.Len() ) + return FALSE; + + UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + if ( pElement ) + { + pElement->m_bIsRemoved = TRUE; + } + else + SetError( SVSTREAM_FILE_NOT_FOUND ); + + return ( pElement != NULL ); +} + +BOOL UCBStorage::Rename( const String& rEleName, const String& rNewName ) +{ + if( !rEleName.Len()|| !rNewName.Len() ) + return FALSE; + + UCBStorageElement_Impl *pAlreadyExisting = FindElement_Impl( rNewName ); + if ( pAlreadyExisting ) + { + SetError( SVSTREAM_ACCESS_DENIED ); + return FALSE; // can't change to a name that is already used + } + + UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); + if ( pElement ) + { + pElement->m_aName = rNewName; + } + else + SetError( SVSTREAM_FILE_NOT_FOUND ); + + return pElement != NULL; +} + +BOOL UCBStorage::MoveTo( const String& rEleName, BaseStorage* pNewSt, const String& rNewName ) +{ + if( !rEleName.Len() || !rNewName.Len() ) + return FALSE; + + if ( pNewSt == ((BaseStorage*) this) && !FindElement_Impl( rNewName ) ) + { + return Rename( rEleName, rNewName ); + } + else + { +/* + if ( PTR_CAST( UCBStorage, pNewSt ) ) + { + // because the element is moved, not copied, a special optimization is possible : + // first copy the UCBStorageElement; flag old element as "Removed" and new as "Inserted", + // clear original name/type of the new element + // if moved element is open: copy content, but change absolute URL ( and those of all children of the element! ), + // clear original name/type of new content, keep the old original stream/storage, but forget its working streams, + // close original UCBContent and original stream, only the TempFile and its stream may remain unchanged, but now + // belong to the new content + // if original and editable stream are identical ( readonly element ), it has to be copied to the editable + // stream of the destination object + // Not implemented at the moment ( risky?! ), perhaps later + } +*/ + // MoveTo is done by first copying to the new destination and then removing the old element + BOOL bRet = CopyTo( rEleName, pNewSt, rNewName ); + if ( bRet ) + bRet = Remove( rEleName ); + return bRet; + } +} + +BOOL UCBStorage::ValidateFAT() +{ + // ??? + return TRUE; +} + +BOOL UCBStorage::Validate( BOOL bWrite ) const +{ + // ??? + return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); +} + +BOOL UCBStorage::ValidateMode( StreamMode m ) const +{ + // ??? + if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx + return TRUE; + USHORT nCurMode = 0xFFFF; + if( ( m & 3 ) == STREAM_READ ) + { + // only SHARE_DENYWRITE or SHARE_DENYALL allowed + if( ( ( m & STREAM_SHARE_DENYWRITE ) + && ( nCurMode & STREAM_SHARE_DENYWRITE ) ) + || ( ( m & STREAM_SHARE_DENYALL ) + && ( nCurMode & STREAM_SHARE_DENYALL ) ) ) + return TRUE; + } + else + { + // only SHARE_DENYALL allowed + // storages open in r/o mode are OK, since only + // the commit may fail + if( ( m & STREAM_SHARE_DENYALL ) + && ( nCurMode & STREAM_SHARE_DENYALL ) ) + return TRUE; + } + + return TRUE; +} + +const SvStream* UCBStorage::GetSvStream() const +{ + // this would cause a complete download of the file + // as it looks, this method is NOT used inside of SOT, only exported by class SotStorage - but for what ??? + return pImp->m_pSource; +} + +BOOL UCBStorage::Equals( const BaseStorage& rStorage ) const +{ + // ??? + return ((BaseStorage*)this) == &rStorage; +} + +BOOL UCBStorage::IsStorageFile( const String& rFileName ) +{ + String aFileURL = rFileName; + INetURLObject aObj( aFileURL ); + if ( aObj.GetProtocol() == INET_PROT_NOT_VALID ) + { + ::utl::LocalFileHelper::ConvertPhysicalNameToURL( rFileName, aFileURL ); + aObj.SetURL( aFileURL ); + aFileURL = aObj.GetMainURL( INetURLObject::NO_DECODE ); + } + + SvStream * pStm = ::utl::UcbStreamHelper::CreateStream( aFileURL, STREAM_STD_READ ); + BOOL bRet = UCBStorage::IsStorageFile( pStm ); + delete pStm; + return bRet; +} + +BOOL UCBStorage::IsStorageFile( SvStream* pFile ) +{ + if ( !pFile ) + return FALSE; + + ULONG nPos = pFile->Tell(); + pFile->Seek( STREAM_SEEK_TO_END ); + if ( pFile->Tell() < 4 ) + return FALSE; + + pFile->Seek(0); + UINT32 nBytes; + *pFile >> nBytes; + + // search for the magic bytes + BOOL bRet = ( nBytes == 0x04034b50 ); + if ( !bRet ) + { + // disk spanned file have an additional header in front of the usual one + bRet = ( nBytes == 0x08074b50 ); + if ( bRet ) + { + *pFile >> nBytes; + bRet = ( nBytes == 0x04034b50 ); + } + } + + pFile->Seek( nPos ); + return bRet; +} + +BOOL UCBStorage::IsDiskSpannedFile( SvStream* pFile ) +{ + if ( !pFile ) + return FALSE; + + ULONG nPos = pFile->Tell(); + pFile->Seek( STREAM_SEEK_TO_END ); + if ( !pFile->Tell() ) + return FALSE; + + pFile->Seek(0); + UINT32 nBytes; + *pFile >> nBytes; + + // disk spanned file have an additional header in front of the usual one + BOOL bRet = ( nBytes == 0x08074b50 ); + if ( bRet ) + { + *pFile >> nBytes; + bRet = ( nBytes == 0x04034b50 ); + } + + pFile->Seek( nPos ); + return bRet; +} + +String UCBStorage::GetLinkedFile( SvStream &rStream ) +{ + String aString; + ULONG nPos = rStream.Tell(); + rStream.Seek( STREAM_SEEK_TO_END ); + if ( !rStream.Tell() ) + return aString; + + rStream.Seek(0); + UINT32 nBytes; + rStream >> nBytes; + if( nBytes == 0x04034b50 ) + { + ByteString aTmp; + rStream.ReadByteString( aTmp ); + if ( aTmp.CompareTo( "ContentURL=", 11 ) == COMPARE_EQUAL ) + { + aTmp.Erase( 0, 11 ); + aString = String( aTmp, RTL_TEXTENCODING_UTF8 ); + } + } + + rStream.Seek( nPos ); + return aString; +} + +String UCBStorage::CreateLinkFile( const String& rName ) +{ + // create a stream to write the link file - use a temp file, because it may be no file content + INetURLObject aFolderObj( rName ); + String aName = aFolderObj.GetName(); + aFolderObj.removeSegment(); + String aFolderURL( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ); + ::utl::TempFile* pTempFile = new ::utl::TempFile( &aFolderURL ); + + // get the stream from the temp file + SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE | STREAM_TRUNC ); + + // write header + *pStream << ( UINT32 ) 0x04034b50; + + // assemble a new folder name in the destination folder + INetURLObject aObj( rName ); + String aTmpName = aObj.GetName(); + String aTitle = String::CreateFromAscii( "content." ); + aTitle += aTmpName; + + // create a folder and store its URL + Content aFolder( aFolderURL, Reference < XCommandEnvironment >() ); + Content aNewFolder; + BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTitle, aNewFolder ); + if ( !bRet ) + { + aFolderObj.insertName( aTitle ); + if ( ::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) ) + { + // Hack, because already existing files give the same CommandAbortedException as any other error ! + // append a number until the name can be used for a new folder + aTitle += '.'; + for ( sal_Int32 i=0; !bRet; i++ ) + { + String aTmp( aTitle ); + aTmp += String::CreateFromInt32( i ); + bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTmp, aNewFolder ); + if ( bRet ) + aTitle = aTmp; + else + { + aFolderObj.SetName( aTmp ); + if ( !::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) ) + // Hack, because already existing files give the same CommandAbortedException as any other error ! + break; + } + } + } + } + + if ( bRet ) + { + // get the URL + aObj.SetName( aTitle ); + String aURL = aObj.GetMainURL( INetURLObject::NO_DECODE ); + + // store it as key/value pair + String aLink = String::CreateFromAscii("ContentURL="); + aLink += aURL; + pStream->WriteByteString( aLink, RTL_TEXTENCODING_UTF8 ); + pStream->Flush(); + + // move the stream to its desired location + Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >() ); + DELETEZ( pTempFile ); + aFolder.transferContent( aSource, InsertOperation_MOVE, aName, NameClash::OVERWRITE ); + return aURL; + } + + pTempFile->EnableKillingFile( TRUE ); + delete pTempFile; + return String(); +} + +BOOL UCBStorage::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) +{ + if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) + return FALSE; + + if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL ) + { + ::rtl::OUString aTmp; + rValue >>= aTmp; + pImp->m_aContentType = aTmp; + } + + try + { + if ( pImp->GetContent() ) + { + pImp->m_pContent->setPropertyValue( rName, rValue ); + return TRUE; + } + } + catch ( Exception& ) + { + } + + return FALSE; +} + +BOOL UCBStorage::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + try + { + if ( pImp->GetContent() ) + { + rValue = pImp->m_pContent->getPropertyValue( rName ); + return TRUE; + } + } + catch ( Exception& ) + { + } + + return FALSE; +} + +BOOL UCBStorage::GetProperty( const String& rEleName, const String& rName, ::com::sun::star::uno::Any& rValue ) +{ + UCBStorageElement_Impl *pEle = FindElement_Impl( rEleName ); + if ( !pEle ) + return FALSE; + + if ( !pEle->m_bIsFolder ) + { + if ( !pEle->m_xStream.Is() ) + pImp->OpenStream( pEle, pImp->m_nMode, pImp->m_bDirect ); + if ( pEle->m_xStream->m_nError ) + { + pEle->m_xStream.Clear(); + return FALSE; + } + + try + { + if ( pEle->m_xStream->m_pContent ) + { + rValue = pEle->m_xStream->m_pContent->getPropertyValue( rName ); + return TRUE; + } + } + catch ( Exception& ) + { + } + } + else + { + if ( !pEle->m_xStorage.Is() ) + pImp->OpenStorage( pEle, pImp->m_nMode, pImp->m_bDirect ); + if ( pEle->m_xStorage->m_nError ) + { + pEle->m_xStorage.Clear(); + return FALSE; + } + + try + { + if ( pEle->m_xStorage->GetContent() ) + { + rValue = pEle->m_xStorage->m_pContent->getPropertyValue( rName ); + return TRUE; + } + } + catch ( Exception& ) + { + } + } + + return FALSE; +} + +UNOStorageHolderList* UCBStorage::GetUNOStorageHolderList() +{ + if ( !pImp->m_pUNOStorageHolderList ) + pImp->m_pUNOStorageHolderList = new UNOStorageHolderList; + + return pImp->m_pUNOStorageHolderList; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |