/************************************************************************* * * 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 * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sot.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stg.hxx" #include "storinfo.hxx" #include #include #include #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 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(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(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(static_cast(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(static_cast(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 m_rSource; // the stream covering the original data of the content SvStream* m_pStream; // the stream worked on; for readonly streams it is the original stream of the content // for read/write streams it's a copy into a temporary file String m_aTempURL; // URL of this temporary stream RepresentModes m_nRepresentMode; // should it be used as XInputStream or as SvStream long m_nError; StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing ) BOOL m_bSourceRead; // Source still contains useful information BOOL m_bModified; // only modified streams will be sent to the original content BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages // this means that the root storage does an autocommit when its external // reference is destroyed BOOL m_bIsOLEStorage;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream UCBStorageStream_Impl( const String&, StreamMode, UCBStorageStream*, BOOL, const ByteString* pKey=0, BOOL bRepair = FALSE, Reference< XProgressHandler > xProgress = Reference< XProgressHandler >() ); void Free(); BOOL Init(); BOOL Clear(); sal_Int16 Commit(); // if modified and commited: transfer an XInputStream to the content BOOL Revert(); // discard all changes BaseStorage* CreateStorage();// create an OLE Storage on the UCBStorageStream ULONG GetSize(); ULONG ReadSourceWriteTemporary( ULONG aLength ); // read aLength from source and copy to temporary, // no seeking is produced ULONG ReadSourceWriteTemporary(); // read source till the end and copy to temporary, // no seeking is produced #if 0 ULONG CopySourceToTemporary( ULONG aLength ); // same as ReadSourceWriteToTemporary( aLength ) // but the writing is done at the end of temporary // pointer position is not changed #endif ULONG CopySourceToTemporary(); // same as ReadSourceWriteToTemporary() // but the writing is done at the end of temporary // pointer position is not changed Reference GetXInputStream(); // return XInputStream, after that // this class is close to be unusable // since it can not read and write using SvStream::SetError; void SetError( sal_uInt32 nError ); void PrepareCachedForReopen( StreamMode nMode ); }; SV_DECL_IMPL_REF( UCBStorageStream_Impl ); struct UCBStorageElement_Impl; DECLARE_LIST( UCBStorageElementList_Impl, UCBStorageElement_Impl* ) class UCBStorage_Impl : public SvRefBase { ~UCBStorage_Impl(); public: UCBStorage* m_pAntiImpl; // only valid if external references exists String m_aOriginalName;// the original name before accessing the storage String m_aName; // the actual name ( changed with a Rename command at the parent ) String m_aURL; // the full path name to create the content String m_aContentType; String m_aOriginalContentType; ::ucbhelper::Content* m_pContent; // the content that provides the storage elements ::utl::TempFile* m_pTempFile; // temporary file, only for storages on stream SvStream* m_pSource; // original stream, only for storages on a stream //SvStream* m_pStream; // the corresponding editable stream, only for storage on a stream long m_nError; StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing ) BOOL m_bModified; // only modified elements will be sent to the original content BOOL m_bCommited; // sending the streams is coordinated by the root storage of the package BOOL m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages // this means that the root storage does an autocommit when its external // reference is destroyed BOOL m_bIsRoot; // marks this storage as root storages that manages all oommits and reverts BOOL m_bDirty; // ??? BOOL m_bIsLinked; BOOL m_bListCreated; ULONG m_nFormat; String m_aUserTypeName; SvGlobalName m_aClassId; UCBStorageElementList_Impl m_aChildrenList; BOOL m_bRepairPackage; Reference< XProgressHandler > m_xProgressHandler; UNOStorageHolderList* m_pUNOStorageHolderList; UCBStorage_Impl( const ::ucbhelper::Content&, const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() ); UCBStorage_Impl( const String&, StreamMode, UCBStorage*, BOOL, BOOL, BOOL = FALSE, Reference< XProgressHandler > = Reference< XProgressHandler >() ); UCBStorage_Impl( SvStream&, UCBStorage*, BOOL ); void Init(); sal_Int16 Commit(); BOOL Revert(); BOOL Insert( ::ucbhelper::Content *pContent ); UCBStorage_Impl* OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect ); UCBStorageStream_Impl* OpenStream( UCBStorageElement_Impl*, StreamMode, BOOL, const ByteString* pKey=0 ); void SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const String& ); void GetProps( sal_Int32&, Sequence < Sequence < PropertyValue > >& rSequence, const String& ); sal_Int32 GetObjectCount(); void ReadContent(); void CreateContent(); ::ucbhelper::Content* GetContent() { if ( !m_pContent ) CreateContent(); return m_pContent; } UCBStorageElementList_Impl& GetChildrenList() { long nError = m_nError; ReadContent(); if ( m_nMode & STREAM_WRITE ) { m_nError = nError; if ( m_pAntiImpl ) { m_pAntiImpl->ResetError(); m_pAntiImpl->SetError( nError ); } } return m_aChildrenList; } void SetError( long nError ); }; SV_DECL_IMPL_REF( UCBStorage_Impl ); // this struct contains all neccessary information on an element inside a UCBStorage struct UCBStorageElement_Impl { String m_aName; // the actual URL relative to the root "folder" String m_aOriginalName;// the original name in the content ULONG m_nSize; BOOL m_bIsFolder; // Only TRUE when it is a UCBStorage ! BOOL m_bIsStorage; // Also TRUE when it is an OLEStorage ! BOOL m_bIsRemoved; // element will be removed on commit BOOL m_bIsInserted; // element will be removed on revert UCBStorage_ImplRef m_xStorage; // reference to the "real" storage UCBStorageStream_ImplRef m_xStream; // reference to the "real" stream UCBStorageElement_Impl( const ::rtl::OUString& rName, BOOL bIsFolder = FALSE, ULONG nSize = 0 ) : m_aName( rName ) , m_aOriginalName( rName ) , m_nSize( nSize ) , m_bIsFolder( bIsFolder ) , m_bIsStorage( bIsFolder ) , m_bIsRemoved( FALSE ) , m_bIsInserted( FALSE ) { } ::ucbhelper::Content* GetContent(); BOOL IsModified(); String GetContentType(); void SetContentType( const String& ); String GetOriginalContentType(); BOOL IsLoaded() { return m_xStream.Is() || m_xStorage.Is(); } }; ::ucbhelper::Content* UCBStorageElement_Impl::GetContent() { if ( m_xStream.Is() ) return m_xStream->m_pContent; else if ( m_xStorage.Is() ) return m_xStorage->GetContent(); else return NULL; } String UCBStorageElement_Impl::GetContentType() { if ( m_xStream.Is() ) return m_xStream->m_aContentType; else if ( m_xStorage.Is() ) return m_xStorage->m_aContentType; else { DBG_ERROR("Element not loaded!"); return String(); } } void UCBStorageElement_Impl::SetContentType( const String& rType ) { if ( m_xStream.Is() ) { m_xStream->m_aContentType = m_xStream->m_aOriginalContentType = rType; } else if ( m_xStorage.Is() ) { m_xStorage->m_aContentType = m_xStorage->m_aOriginalContentType = rType; } else { DBG_ERROR("Element not loaded!"); } } String UCBStorageElement_Impl::GetOriginalContentType() { if ( m_xStream.Is() ) return m_xStream->m_aOriginalContentType; else if ( m_xStorage.Is() ) return m_xStorage->m_aOriginalContentType; else return String(); } BOOL UCBStorageElement_Impl::IsModified() { BOOL bModified = m_bIsRemoved || m_bIsInserted || m_aName != m_aOriginalName; if ( bModified ) { if ( m_xStream.Is() ) bModified = m_xStream->m_aContentType != m_xStream->m_aOriginalContentType; else if ( m_xStorage.Is() ) bModified = m_xStorage->m_aContentType != m_xStorage->m_aOriginalContentType; } return bModified; } UCBStorageStream_Impl::UCBStorageStream_Impl( const String& rName, StreamMode nMode, UCBStorageStream* pStream, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress ) : m_pAntiImpl( pStream ) , m_aURL( rName ) , m_pContent( NULL ) , m_pStream( NULL ) , m_nRepresentMode( nonset ) , m_nError( 0 ) , m_nMode( nMode ) , m_bSourceRead( !( nMode & STREAM_TRUNC ) ) , m_bModified( FALSE ) , m_bCommited( FALSE ) , m_bDirect( bDirect ) , m_bIsOLEStorage( FALSE ) { // name is last segment in URL INetURLObject aObj( rName ); m_aName = m_aOriginalName = aObj.GetLastName(); try { // create the content Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv; ::rtl::OUString aTemp( rName ); if ( bRepair ) { xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(), xProgress ); aTemp += rtl::OUString::createFromAscii("?repairpackage"); } m_pContent = new ::ucbhelper::Content( aTemp, xComEnv ); if ( pKey ) { m_aKey = *pKey; // stream is encrypted and should be decrypted (without setting the key we'll get the raw data) sal_uInt8 aBuffer[RTL_DIGEST_LENGTH_SHA1]; rtlDigestError nErr = rtl_digest_SHA1( pKey->GetBuffer(), pKey->Len(), aBuffer, RTL_DIGEST_LENGTH_SHA1 ); if ( nErr == rtl_Digest_E_None ) { sal_uInt8* pBuffer = aBuffer; ::com::sun::star::uno::Sequence < sal_Int8 > aSequ( (sal_Int8*) pBuffer, RTL_DIGEST_LENGTH_SHA1 ); ::com::sun::star::uno::Any aAny; aAny <<= aSequ; m_pContent->setPropertyValue( ::rtl::OUString::createFromAscii("EncryptionKey"), aAny ); } } } catch ( ContentCreationException& ) { // content could not be created SetError( SVSTREAM_CANNOT_MAKE ); } catch ( RuntimeException& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); } } UCBStorageStream_Impl::~UCBStorageStream_Impl() { if( m_rSource.is() ) m_rSource = Reference< XInputStream >(); if( m_pStream ) delete m_pStream; if ( m_aTempURL.Len() ) ::utl::UCBContentHelper::Kill( m_aTempURL ); if( m_pContent ) delete m_pContent; } Reference 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 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 aData(32000); try { ULONG aReaded = 32000; for( ULONG pInd = 0; pInd < aLength && aReaded == 32000 ; pInd += 32000 ) { ULONG aToCopy = min( aLength - pInd, 32000 ); aReaded = m_rSource->readBytes( aData, aToCopy ); aResult += m_pStream->Write( aData.getArray(), aReaded ); } if( aResult < aLength ) m_bSourceRead = FALSE; } #if OSL_DEBUG_LEVEL > 1 catch( Exception & e ) { OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); #else catch( Exception & ) { #endif } } return aResult; } ULONG UCBStorageStream_Impl::CopySourceToTemporary() { // current position of the temporary stream is not changed ULONG aResult = 0; if( m_bSourceRead ) { ULONG aPos = m_pStream->Tell(); m_pStream->Seek( STREAM_SEEK_TO_END ); aResult = ReadSourceWriteTemporary(); m_pStream->Seek( aPos ); } return aResult; } #if 0 ULONG UCBStorageStream_Impl::CopySourceToTemporary( ULONG aLength ) { // current position of the temporary stream is not changed ULONG aResult = 0; if( m_bSourceRead ) { ULONG aPos = m_pStream->Tell(); m_pStream->Seek( STREAM_SEEK_TO_END ); aResult = ReadSourceWriteTemporary( aLength ); m_pStream->Seek( aPos ); } return aResult; } #endif // UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream // of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified ULONG UCBStorageStream_Impl::GetData( void* pData, ULONG nSize ) { ULONG aResult = 0; if( !Init() ) return 0; // read data that is in temporary stream aResult = m_pStream->Read( pData, nSize ); if( m_bSourceRead && aResult < nSize ) { // read the tail of the data from original stream // copy this tail to the temporary stream ULONG aToRead = nSize - aResult; pData = (void*)( (char*)pData + aResult ); try { Sequence aData( aToRead ); ULONG aReaded = m_rSource->readBytes( aData, aToRead ); aResult += m_pStream->Write( (void*)aData.getArray(), aReaded ); memcpy( pData, aData.getArray(), aReaded ); } #if OSL_DEBUG_LEVEL > 1 catch( Exception & e ) { OSL_ENSURE( FALSE, ::rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); #else catch( Exception & ) { #endif } if( aResult < nSize ) m_bSourceRead = FALSE; } return aResult; } ULONG UCBStorageStream_Impl::PutData( const void* pData, ULONG nSize ) { if ( !(m_nMode & STREAM_WRITE) ) { SetError( ERRCODE_IO_ACCESSDENIED ); return 0; // ?mav? } if( !nSize || !Init() ) return 0; ULONG aResult = m_pStream->Write( pData, nSize ); m_bModified = aResult > 0; return aResult; } ULONG UCBStorageStream_Impl::SeekPos( ULONG nPos ) { if( !Init() ) return 0; ULONG aResult; if( nPos == STREAM_SEEK_TO_END ) { m_pStream->Seek( STREAM_SEEK_TO_END ); ReadSourceWriteTemporary(); aResult = m_pStream->Tell(); } else { // the problem is that even if nPos is larger the the length // of the stream the stream pointer will be moved to this position // so we have to check if temporary stream does not contain required position if( m_pStream->Tell() > nPos || m_pStream->Seek( STREAM_SEEK_TO_END ) > nPos ) { // no copiing is required aResult = m_pStream->Seek( nPos ); } else { // the temp stream pointer points to the end now aResult = m_pStream->Tell(); if( aResult < nPos ) { if( m_bSourceRead ) { aResult += ReadSourceWriteTemporary( nPos - aResult ); if( aResult < nPos ) m_bSourceRead = FALSE; DBG_ASSERT( aResult == m_pStream->Tell(), "Error in stream arithmetic!\n" ); } if( (m_nMode & STREAM_WRITE) && !m_bSourceRead && aResult < nPos ) { // it means that all the Source stream was copied already // but the required position still was not reached // for writable streams it should be done m_pStream->SetStreamSize( nPos ); aResult = m_pStream->Seek( STREAM_SEEK_TO_END ); DBG_ASSERT( aResult == nPos, "Error in stream arithmetic!\n" ); } } } } return aResult; } void UCBStorageStream_Impl::SetSize( ULONG nSize ) { if ( !(m_nMode & STREAM_WRITE) ) { SetError( ERRCODE_IO_ACCESSDENIED ); return; } if( !Init() ) return; m_bModified = TRUE; if( m_bSourceRead ) { ULONG aPos = m_pStream->Tell(); m_pStream->Seek( STREAM_SEEK_TO_END ); if( m_pStream->Tell() < nSize ) ReadSourceWriteTemporary( nSize - m_pStream->Tell() ); m_pStream->Seek( aPos ); } m_pStream->SetStreamSize( nSize ); m_bSourceRead = FALSE; } void UCBStorageStream_Impl::FlushData() { if( m_pStream ) { CopySourceToTemporary(); m_pStream->Flush(); } m_bCommited = TRUE; } void UCBStorageStream_Impl::SetError( sal_uInt32 nErr ) { if ( !m_nError ) { m_nError = nErr; SvStream::SetError( nErr ); if ( m_pAntiImpl ) m_pAntiImpl->SetError( nErr ); } } void UCBStorageStream_Impl::ResetError() { m_nError = 0; SvStream::ResetError(); if ( m_pAntiImpl ) m_pAntiImpl->ResetError(); } ULONG UCBStorageStream_Impl::GetSize() { if( !Init() ) return 0; ULONG nPos = m_pStream->Tell(); m_pStream->Seek( STREAM_SEEK_TO_END ); ReadSourceWriteTemporary(); ULONG nRet = m_pStream->Tell(); m_pStream->Seek( nPos ); return nRet; } BaseStorage* UCBStorageStream_Impl::CreateStorage() { // create an OLEStorage on a SvStream ( = this ) // it gets the root attribute because otherwise it would probably not write before my root is commited UCBStorageStream* pNewStorageStream = new UCBStorageStream( this ); Storage *pStorage = new Storage( *pNewStorageStream, m_bDirect ); // GetError() call cleares error code for OLE storages, must be changed in future long nTmpErr = pStorage->GetError(); pStorage->SetError( nTmpErr ); m_bIsOLEStorage = !nTmpErr; return static_cast< BaseStorage* > ( pStorage ); } sal_Int16 UCBStorageStream_Impl::Commit() { // send stream to the original content // the parent storage is responsible for the correct handling of deleted contents if ( m_bCommited || m_bIsOLEStorage || m_bDirect ) { // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage // was commited as well ( if not opened in direct mode ) if ( m_bModified ) { try { CopySourceToTemporary(); // release all stream handles Free(); // the temporary file does not exist only for truncated streams DBG_ASSERT( m_aTempURL.Len() || ( m_nMode & STREAM_TRUNC ), "No temporary file to read from!"); if ( !m_aTempURL.Len() && !( m_nMode & STREAM_TRUNC ) ) throw RuntimeException(); // create wrapper to stream that is only used while reading inside package component Reference < XInputStream > xStream = new FileStreamWrapper_Impl( m_aTempURL ); Any aAny; InsertCommandArgument aArg; aArg.Data = xStream; aArg.ReplaceExisting = sal_True; aAny <<= aArg; m_pContent->executeCommand( ::rtl::OUString::createFromAscii("insert"), aAny ); // wrapper now controls lifetime of temporary file m_aTempURL.Erase(); INetURLObject aObj( m_aURL ); aObj.SetName( m_aName ); m_aURL = aObj.GetMainURL( INetURLObject::NO_DECODE ); m_bModified = FALSE; m_bSourceRead = TRUE; } catch ( CommandAbortedException& ) { // any command wasn't executed successfully - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( RuntimeException& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( Exception& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } m_bCommited = FALSE; return COMMIT_RESULT_SUCCESS; } } return COMMIT_RESULT_NOTHING_TO_DO; } BOOL UCBStorageStream_Impl::Revert() { // if an OLEStorage is created on this stream, no "revert" is neccessary because OLEStorages do nothing on "Revert" ! if ( m_bCommited ) { DBG_ERROR("Revert while commit is in progress!" ); return FALSE; // ??? } Free(); if ( m_aTempURL.Len() ) { ::utl::UCBContentHelper::Kill( m_aTempURL ); m_aTempURL.Erase(); } m_bSourceRead = FALSE; try { m_rSource = m_pContent->openStream(); if( m_rSource.is() ) { if ( m_pAntiImpl && ( m_nMode & STREAM_TRUNC ) ) // stream is in use and should be truncated m_bSourceRead = FALSE; else { m_nMode &= ~STREAM_TRUNC; m_bSourceRead = TRUE; } } else SetError( SVSTREAM_CANNOT_MAKE ); } catch ( ContentCreationException& ) { SetError( ERRCODE_IO_GENERAL ); } catch ( RuntimeException& ) { SetError( ERRCODE_IO_GENERAL ); } catch ( Exception& ) { } m_bModified = FALSE; m_aName = m_aOriginalName; m_aContentType = m_aOriginalContentType; return ( GetError() == ERRCODE_NONE ); } BOOL UCBStorageStream_Impl::Clear() { BOOL bRet = ( m_pAntiImpl == NULL ); DBG_ASSERT( bRet, "Removing used stream!" ); if( bRet ) { Free(); } return bRet; } void UCBStorageStream_Impl::Free() { #if OSL_DEBUG_LEVEL > 1 if ( m_pStream ) { if ( m_aTempURL.Len() ) --nOpenFiles; else --nOpenStreams; } #endif m_nRepresentMode = nonset; m_rSource = Reference< XInputStream >(); DELETEZ( m_pStream ); } void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode ) { sal_Bool isWritable = (( m_nMode & STREAM_WRITE ) != 0 ); if ( isWritable ) { // once stream was writable, never reset to readonly nMode |= STREAM_WRITE; } m_nMode = nMode; Free(); if ( nMode & STREAM_TRUNC ) { m_bSourceRead = 0; // usually it should be 0 already but just in case... if ( m_aTempURL.Len() ) { ::utl::UCBContentHelper::Kill( m_aTempURL ); m_aTempURL.Erase(); } } } UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorageStream ! pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey ); pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used StorageBase::m_nMode = pImp->m_nMode; } UCBStorageStream::UCBStorageStream( const String& rName, StreamMode nMode, BOOL bDirect, const ByteString* pKey, BOOL bRepair, Reference< XProgressHandler > xProgress ) { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorageStream ! pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, pKey, bRepair, xProgress ); pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used StorageBase::m_nMode = pImp->m_nMode; } UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl *pImpl ) : pImp( pImpl ) { pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used pImp->m_pAntiImpl = this; SetError( pImp->m_nError ); StorageBase::m_nMode = pImp->m_nMode; } UCBStorageStream::~UCBStorageStream() { if ( pImp->m_nMode & STREAM_WRITE ) pImp->Flush(); pImp->m_pAntiImpl = NULL; pImp->Free(); pImp->ReleaseRef(); } ULONG UCBStorageStream::Read( void * pData, ULONG nSize ) { //return pImp->m_pStream->Read( pData, nSize ); return pImp->GetData( pData, nSize ); } ULONG UCBStorageStream::Write( const void* pData, ULONG nSize ) { /* // mba: does occur in writer ! if ( pImp->m_bCommited ) { DBG_ERROR("Writing while commit is in progress!" ); return 0; } */ // pImp->m_bModified = TRUE; //return pImp->m_pStream->Write( pData, nSize ); return pImp->PutData( pData, nSize ); } ULONG UCBStorageStream::Seek( ULONG nPos ) { //return pImp->m_pStream->Seek( nPos ); return pImp->Seek( nPos ); } ULONG UCBStorageStream::Tell() { if( !pImp->Init() ) return 0; return pImp->m_pStream->Tell(); } void UCBStorageStream::Flush() { // streams are never really transacted, so flush also means commit ! Commit(); } BOOL UCBStorageStream::SetSize( ULONG nNewSize ) { /* if ( pImp->m_bCommited ) { DBG_ERROR("Changing stream size while commit is in progress!" ); return FALSE; } */ // pImp->m_bModified = TRUE; //return pImp->m_pStream->SetStreamSize( nNewSize ); pImp->SetSize( nNewSize ); return !pImp->GetError(); } BOOL UCBStorageStream::Validate( BOOL bWrite ) const { return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); } BOOL UCBStorageStream::ValidateMode( StreamMode m ) const { // ??? if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx return TRUE; USHORT nCurMode = 0xFFFF; if( ( m & 3 ) == STREAM_READ ) { // only SHARE_DENYWRITE or SHARE_DENYALL allowed if( ( ( m & STREAM_SHARE_DENYWRITE ) && ( nCurMode & STREAM_SHARE_DENYWRITE ) ) || ( ( m & STREAM_SHARE_DENYALL ) && ( nCurMode & STREAM_SHARE_DENYALL ) ) ) return TRUE; } else { // only SHARE_DENYALL allowed // storages open in r/o mode are OK, since only // the commit may fail if( ( m & STREAM_SHARE_DENYALL ) && ( nCurMode & STREAM_SHARE_DENYALL ) ) return TRUE; } return TRUE; } const SvStream* UCBStorageStream::GetSvStream() const { if( !pImp->Init() ) return NULL; pImp->CopySourceToTemporary(); return pImp->m_pStream; // should not live longer then pImp!!! } SvStream* UCBStorageStream::GetModifySvStream() { return (SvStream*)pImp; } Reference< XInputStream > UCBStorageStream::GetXInputStream() const { return pImp->GetXInputStream(); } BOOL UCBStorageStream::Equals( const BaseStorageStream& rStream ) const { // ??? return ((BaseStorageStream*) this ) == &rStream; } BOOL UCBStorageStream::Commit() { // mark this stream for sending it on root commit pImp->FlushData(); return TRUE; } BOOL UCBStorageStream::Revert() { return pImp->Revert(); } BOOL UCBStorageStream::CopyTo( BaseStorageStream* pDestStm ) { if( !pImp->Init() ) return FALSE; UCBStorageStream* pStg = PTR_CAST( UCBStorageStream, pDestStm ); if ( pStg ) pStg->pImp->m_aContentType = pImp->m_aContentType; pDestStm->SetSize( 0 ); Seek( STREAM_SEEK_TO_END ); INT32 n = Tell(); if( n < 0 ) return FALSE; if( pDestStm->SetSize( n ) && n ) { BYTE* p = new BYTE[ 4096 ]; Seek( 0L ); pDestStm->Seek( 0L ); while( n ) { UINT32 nn = n; if( nn > 4096 ) nn = 4096; if( Read( p, nn ) != nn ) break; if( pDestStm->Write( p, nn ) != nn ) break; n -= nn; } delete[] p; } return TRUE; } BOOL UCBStorageStream::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) { if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) return FALSE; if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL ) { ::rtl::OUString aTmp; rValue >>= aTmp; pImp->m_aContentType = aTmp; } try { if ( pImp->m_pContent ) { pImp->m_pContent->setPropertyValue( rName, rValue ); return TRUE; } } catch ( Exception& ) { } return FALSE; } BOOL UCBStorageStream::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) { try { if ( pImp->m_pContent ) { rValue = pImp->m_pContent->getPropertyValue( rName ); return TRUE; } } catch ( Exception& ) { } return FALSE; } UCBStorage::UCBStorage( SvStream& rStrm, BOOL bDirect ) { String aURL = GetLinkedFile( rStrm ); if ( aURL.Len() ) { StreamMode nMode = STREAM_READ; if( rStrm.IsWritable() ) nMode = STREAM_READ | STREAM_WRITE; ::ucbhelper::Content aContent( aURL, Reference < XCommandEnvironment >() ); pImp = new UCBStorage_Impl( aContent, aURL, nMode, this, bDirect, TRUE ); } else { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorage ! pImp = new UCBStorage_Impl( rStrm, this, bDirect ); } pImp->AddRef(); pImp->Init(); StorageBase::m_nMode = pImp->m_nMode; } UCBStorage::UCBStorage( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot ) { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorage ! pImp = new UCBStorage_Impl( rContent, rName, nMode, this, bDirect, bIsRoot ); pImp->AddRef(); pImp->Init(); StorageBase::m_nMode = pImp->m_nMode; } UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorage ! pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, bIsRepair, xProgressHandler ); pImp->AddRef(); pImp->Init(); StorageBase::m_nMode = pImp->m_nMode; } UCBStorage::UCBStorage( const String& rName, StreamMode nMode, BOOL bDirect, BOOL bIsRoot ) { // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized // to class UCBStorage ! pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, sal_False, Reference< XProgressHandler >() ); pImp->AddRef(); pImp->Init(); StorageBase::m_nMode = pImp->m_nMode; } UCBStorage::UCBStorage( UCBStorage_Impl *pImpl ) : pImp( pImpl ) { pImp->m_pAntiImpl = this; SetError( pImp->m_nError ); pImp->AddRef(); // use direct refcounting because in header file only a pointer should be used StorageBase::m_nMode = pImp->m_nMode; } UCBStorage::~UCBStorage() { if ( pImp->m_bIsRoot && pImp->m_bDirect && ( !pImp->m_pTempFile || pImp->m_pSource ) ) // DirectMode is simulated with an AutoCommit Commit(); pImp->m_pAntiImpl = NULL; pImp->ReleaseRef(); } UCBStorage_Impl::UCBStorage_Impl( const ::ucbhelper::Content& rContent, const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) : m_pAntiImpl( pStorage ) , m_pContent( new ::ucbhelper::Content( rContent ) ) , m_pTempFile( NULL ) , m_pSource( NULL ) //, m_pStream( NULL ) , m_nError( 0 ) , m_nMode( nMode ) , m_bModified( FALSE ) , m_bCommited( FALSE ) , m_bDirect( bDirect ) , m_bIsRoot( bIsRoot ) , m_bDirty( FALSE ) , m_bIsLinked( TRUE ) , m_bListCreated( FALSE ) , m_nFormat( 0 ) , m_aClassId( SvGlobalName() ) , m_bRepairPackage( bIsRepair ) , m_xProgressHandler( xProgressHandler ) , m_pUNOStorageHolderList( NULL ) { String aName( rName ); if( !aName.Len() ) { // no name given = use temporary name! DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" ); m_pTempFile = new ::utl::TempFile; m_pTempFile->EnableKillingFile( TRUE ); m_aName = m_aOriginalName = aName = m_pTempFile->GetURL(); } m_aURL = rName; } UCBStorage_Impl::UCBStorage_Impl( const String& rName, StreamMode nMode, UCBStorage* pStorage, BOOL bDirect, BOOL bIsRoot, BOOL bIsRepair, Reference< XProgressHandler > xProgressHandler ) : m_pAntiImpl( pStorage ) , m_pContent( NULL ) , m_pTempFile( NULL ) , m_pSource( NULL ) //, m_pStream( NULL ) , m_nError( 0 ) , m_nMode( nMode ) , m_bModified( FALSE ) , m_bCommited( FALSE ) , m_bDirect( bDirect ) , m_bIsRoot( bIsRoot ) , m_bDirty( FALSE ) , m_bIsLinked( FALSE ) , m_bListCreated( FALSE ) , m_nFormat( 0 ) , m_aClassId( SvGlobalName() ) , m_bRepairPackage( bIsRepair ) , m_xProgressHandler( xProgressHandler ) , m_pUNOStorageHolderList( NULL ) { String aName( rName ); if( !aName.Len() ) { // no name given = use temporary name! DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" ); m_pTempFile = new ::utl::TempFile; m_pTempFile->EnableKillingFile( TRUE ); m_aName = m_aOriginalName = aName = m_pTempFile->GetURL(); } if ( m_bIsRoot ) { // create the special package URL for the package content String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://"); aTemp += String(INetURLObject::encode( aName, INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL )); m_aURL = aTemp; if ( m_nMode & STREAM_WRITE ) { // the root storage opens the package, so make sure that there is any SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aName, STREAM_STD_READWRITE, m_pTempFile != 0 /* bFileExists */ ); delete pStream; } } else { // substorages are opened like streams: the URL is a "child URL" of the root package URL m_aURL = rName; if ( m_aURL.CompareToAscii( "vnd.sun.star.pkg://", 19 ) != 0 ) m_bIsLinked = TRUE; } } UCBStorage_Impl::UCBStorage_Impl( SvStream& rStream, UCBStorage* pStorage, BOOL bDirect ) : m_pAntiImpl( pStorage ) , m_pContent( NULL ) , m_pTempFile( new ::utl::TempFile ) , m_pSource( &rStream ) , m_nError( 0 ) , m_bModified( FALSE ) , m_bCommited( FALSE ) , m_bDirect( bDirect ) , m_bIsRoot( TRUE ) , m_bDirty( FALSE ) , m_bIsLinked( FALSE ) , m_bListCreated( FALSE ) , m_nFormat( 0 ) , m_aClassId( SvGlobalName() ) , m_bRepairPackage( FALSE ) , m_pUNOStorageHolderList( NULL ) { // opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call, // which will be called in the storages' dtor m_pTempFile->EnableKillingFile( TRUE ); DBG_ASSERT( !bDirect, "Storage on a stream must not be opened in direct mode!" ); // UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only // accessed readonly // the root storage opens the package; create the special package URL for the package content String aTemp = String::CreateFromAscii("vnd.sun.star.pkg://"); aTemp += String(INetURLObject::encode( m_pTempFile->GetURL(), INetURLObject::PART_AUTHORITY, '%', INetURLObject::ENCODE_ALL )); m_aURL = aTemp; // copy data into the temporary file SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), STREAM_STD_READWRITE, sal_True /* bFileExists */ ); if ( pStream ) { rStream.Seek(0); rStream >> *pStream; pStream->Flush(); DELETEZ( pStream ); } // close stream and let content access the file m_pSource->Seek(0); // check opening mode m_nMode = STREAM_READ; if( rStream.IsWritable() ) m_nMode = STREAM_READ | STREAM_WRITE; } void UCBStorage_Impl::Init() { // name is last segment in URL INetURLObject aObj( m_aURL ); if ( !m_aName.Len() ) // if the name was not already set to a temp name m_aName = m_aOriginalName = aObj.GetLastName(); // don't create the content for disk spanned files, avoid too early access to directory and/or manifest if ( !m_pContent && !( m_nMode & STORAGE_DISKSPANNED_MODE ) ) CreateContent(); if ( m_nMode & STORAGE_DISKSPANNED_MODE ) { // Hack! Avoid access to the manifest file until mediatype is not available in the first segment of a // disk spanned file m_aContentType = m_aOriginalContentType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/vnd.sun.xml.impress") ); } else if ( m_pContent ) { if ( m_bIsLinked ) { if( m_bIsRoot ) { ReadContent(); if ( m_nError == ERRCODE_NONE ) { // read the manifest.xml file aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("META-INF") ) ); aObj.Append( String( RTL_CONSTASCII_USTRINGPARAM("manifest.xml") ) ); // create input stream SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( aObj.GetMainURL( INetURLObject::NO_DECODE ), STREAM_STD_READ ); // no stream means no manifest.xml if ( pStream ) { if ( !pStream->GetError() ) { ::utl::OInputStreamWrapper* pHelper = new ::utl::OInputStreamWrapper( *pStream ); com::sun::star::uno::Reference < ::com::sun::star::io::XInputStream > xInputStream( pHelper ); // create a manifest reader object that will read in the manifest from the stream Reference < ::com::sun::star::packages::manifest::XManifestReader > xReader = Reference< ::com::sun::star::packages::manifest::XManifestReader > ( ::comphelper::getProcessServiceFactory()->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.packages.manifest.ManifestReader" )), UNO_QUERY) ; Sequence < Sequence < PropertyValue > > aProps = xReader->readManifestSequence( xInputStream ); // cleanup xReader = NULL; xInputStream = NULL; SetProps( aProps, String() ); } delete pStream; } } } else ReadContent(); } else { // get the manifest information from the package try { Any aAny = m_pContent->getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) ); rtl::OUString aTmp; if ( ( aAny >>= aTmp ) && aTmp.getLength() ) m_aContentType = m_aOriginalContentType = aTmp; } catch( Exception& ) { DBG_ASSERT( sal_False, "getPropertyValue has thrown an exception! Please let developers know the scenario!" ); } } } if ( m_aContentType.Len() ) { // get the clipboard format using the content type ::com::sun::star::datatransfer::DataFlavor aDataFlavor; aDataFlavor.MimeType = m_aContentType; m_nFormat = SotExchange::GetFormat( aDataFlavor ); // get the ClassId using the clipboard format ( internal table ) m_aClassId = GetClassId_Impl( m_nFormat ); // get human presentable name using the clipboard format SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor ); m_aUserTypeName = aDataFlavor.HumanPresentableName; if( m_pContent && !m_bIsLinked && m_aClassId != SvGlobalName() ) ReadContent(); } } void UCBStorage_Impl::CreateContent() { try { // create content; where to put StreamMode ?! ( already done when opening the file of the package ? ) Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv; ::rtl::OUString aTemp( m_aURL ); if ( m_bRepairPackage ) { xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(), m_xProgressHandler ); aTemp += rtl::OUString::createFromAscii("?repairpackage"); } m_pContent = new ::ucbhelper::Content( aTemp, xComEnv ); } catch ( ContentCreationException& ) { // content could not be created SetError( SVSTREAM_CANNOT_MAKE ); } catch ( RuntimeException& ) { // any other error - not specified SetError( SVSTREAM_CANNOT_MAKE ); } } void UCBStorage_Impl::ReadContent() { if ( m_bListCreated ) return; m_bListCreated = TRUE; // create cursor for access to children Sequence< ::rtl::OUString > aProps(4); ::rtl::OUString* pProps = aProps.getArray(); pProps[0] = ::rtl::OUString::createFromAscii( "Title" ); pProps[1] = ::rtl::OUString::createFromAscii( "IsFolder" ); pProps[2] = ::rtl::OUString::createFromAscii( "MediaType" ); pProps[3] = ::rtl::OUString::createFromAscii( "Size" ); ::ucbhelper::ResultSetInclude eInclude = ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS; try { GetContent(); if ( !m_pContent ) return; Reference< XResultSet > xResultSet = m_pContent->createCursor( aProps, eInclude ); Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); Reference< XRow > xRow( xResultSet, UNO_QUERY ); if ( xResultSet.is() ) { while ( xResultSet->next() ) { // insert all into the children list ::rtl::OUString aTitle( xRow->getString(1) ); ::rtl::OUString aContentType; if ( m_bIsLinked ) { // unpacked storages have to deal with the meta-inf folder by themselves if( aTitle.equalsAscii("META-INF") ) continue; } else { aContentType = xRow->getString(3); } BOOL bIsFolder( xRow->getBoolean(2) ); sal_Int64 nSize = xRow->getLong(4); UCBStorageElement_Impl* pElement = new UCBStorageElement_Impl( aTitle, bIsFolder, (ULONG) nSize ); m_aChildrenList.Insert( pElement, LIST_APPEND ); sal_Bool bIsOfficeDocument = m_bIsLinked || ( m_aClassId != SvGlobalName() ); if ( bIsFolder ) { if ( m_bIsLinked ) OpenStorage( pElement, m_nMode, m_bDirect ); if ( pElement->m_xStorage.Is() ) pElement->m_xStorage->Init(); } else if ( bIsOfficeDocument ) { // streams can be external OLE objects, so they are now folders, but storages! String aName( m_aURL ); aName += '/'; aName += String( xRow->getString(1) ); Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv; if ( m_bRepairPackage ) { xComEnv = new ::ucbhelper::CommandEnvironment( Reference< ::com::sun::star::task::XInteractionHandler >(), m_xProgressHandler ); aName += String( RTL_CONSTASCII_USTRINGPARAM( "?repairpackage" ) ); } ::ucbhelper::Content aContent( aName, xComEnv ); ::rtl::OUString aMediaType; Any aAny = aContent.getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) ); if ( ( aAny >>= aMediaType ) && ( aMediaType.compareToAscii("application/vnd.sun.star.oleobject") == 0 ) ) pElement->m_bIsStorage = TRUE; else if ( !aMediaType.getLength() ) { // older files didn't have that special content type, so they must be detected OpenStream( pElement, STREAM_STD_READ, m_bDirect ); if ( Storage::IsStorageFile( pElement->m_xStream ) ) pElement->m_bIsStorage = TRUE; else pElement->m_xStream->Free(); } } } } } catch ( InteractiveIOException& r ) { if ( r.Code != IOErrorCode_NOT_EXISTING ) SetError( ERRCODE_IO_GENERAL ); } catch ( CommandAbortedException& ) { // any command wasn't executed successfully - not specified if ( !( m_nMode & STREAM_WRITE ) ) // if the folder was just inserted and not already commited, this is not an error! SetError( ERRCODE_IO_GENERAL ); } catch ( RuntimeException& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); } catch ( ResultSetException& ) { // means that the package file is broken SetError( ERRCODE_IO_BROKENPACKAGE ); } catch ( SQLException& ) { // means that the file can be broken SetError( ERRCODE_IO_WRONGFORMAT ); } catch ( Exception& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); } } void UCBStorage_Impl::SetError( long nError ) { if ( !m_nError ) { m_nError = nError; if ( m_pAntiImpl ) m_pAntiImpl->SetError( nError ); } } sal_Int32 UCBStorage_Impl::GetObjectCount() { sal_Int32 nCount = m_aChildrenList.Count(); UCBStorageElement_Impl* pElement = m_aChildrenList.First(); while ( pElement ) { DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) nCount += pElement->m_xStorage->GetObjectCount(); pElement = m_aChildrenList.Next(); } return nCount; } ::rtl::OUString Find_Impl( const Sequence < Sequence < PropertyValue > >& rSequence, const ::rtl::OUString& rPath ) { BOOL bFound = FALSE; for ( sal_Int32 nSeqs=0; nSeqs& rMyProps = rSequence[nSeqs]; ::rtl::OUString aType; for ( sal_Int32 nProps=0; nProps>= aTmp ) && aTmp == rPath ) bFound = TRUE; if ( aType.getLength() ) break; } else if ( rAny.Name.equalsAscii("MediaType") ) { if ( ( rAny.Value >>= aType ) && aType.getLength() && bFound ) break; } } if ( bFound ) return aType; } return ::rtl::OUString(); } void UCBStorage_Impl::SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const String& rPath ) { String aPath( rPath ); if ( !m_bIsRoot ) aPath += m_aName; aPath += '/'; m_aContentType = m_aOriginalContentType = Find_Impl( rSequence, aPath ); if ( m_bIsRoot ) // the "FullPath" of a child always starts without '/' aPath.Erase(); UCBStorageElement_Impl* pElement = m_aChildrenList.First(); while ( pElement ) { DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) pElement->m_xStorage->SetProps( rSequence, aPath ); else { String aElementPath( aPath ); aElementPath += pElement->m_aName; pElement->SetContentType( Find_Impl( rSequence, aElementPath ) ); } pElement = m_aChildrenList.Next(); } if ( m_aContentType.Len() ) { // get the clipboard format using the content type ::com::sun::star::datatransfer::DataFlavor aDataFlavor; aDataFlavor.MimeType = m_aContentType; m_nFormat = SotExchange::GetFormat( aDataFlavor ); // get the ClassId using the clipboard format ( internal table ) m_aClassId = GetClassId_Impl( m_nFormat ); // get human presentable name using the clipboard format SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor ); m_aUserTypeName = aDataFlavor.HumanPresentableName; } } void UCBStorage_Impl::GetProps( sal_Int32& nProps, Sequence < Sequence < PropertyValue > >& rSequence, const String& rPath ) { // first my own properties Sequence < PropertyValue > aProps(2); // first property is the "FullPath" name // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder String aPath( rPath ); if ( !m_bIsRoot ) aPath += m_aName; aPath += '/'; aProps[0].Name = ::rtl::OUString::createFromAscii("MediaType"); aProps[0].Value <<= (::rtl::OUString ) m_aContentType; aProps[1].Name = ::rtl::OUString::createFromAscii("FullPath"); aProps[1].Value <<= (::rtl::OUString ) aPath; rSequence[ nProps++ ] = aProps; if ( m_bIsRoot ) // the "FullPath" of a child always starts without '/' aPath.Erase(); // now the properties of my elements UCBStorageElement_Impl* pElement = m_aChildrenList.First(); while ( pElement ) { DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.Is(), "Storage should be open!" ); if ( pElement->m_bIsFolder && pElement->m_xStorage.Is() ) // storages add there properties by themselves ( see above ) pElement->m_xStorage->GetProps( nProps, rSequence, aPath ); else { // properties of streams String aElementPath( aPath ); aElementPath += pElement->m_aName; aProps[0].Name = ::rtl::OUString::createFromAscii("MediaType"); aProps[0].Value <<= (::rtl::OUString ) pElement->GetContentType(); aProps[1].Name = ::rtl::OUString::createFromAscii("FullPath"); aProps[1].Value <<= (::rtl::OUString ) aElementPath; rSequence[ nProps++ ] = aProps; } pElement = m_aChildrenList.Next(); } } UCBStorage_Impl::~UCBStorage_Impl() { if ( m_pUNOStorageHolderList ) { for ( UNOStorageHolderList::iterator aIter = m_pUNOStorageHolderList->begin(); aIter != m_pUNOStorageHolderList->end(); aIter++ ) if ( *aIter ) { (*aIter)->InternalDispose(); (*aIter)->release(); (*aIter) = NULL; } m_pUNOStorageHolderList->clear(); DELETEZ( m_pUNOStorageHolderList ); } // first delete elements! UCBStorageElement_Impl* pElement = m_aChildrenList.First(); while ( pElement ) { delete pElement; pElement = m_aChildrenList.Next(); } m_aChildrenList.Clear(); delete m_pContent; delete m_pTempFile; } BOOL UCBStorage_Impl::Insert( ::ucbhelper::Content *pContent ) { // a new substorage is inserted into a UCBStorage ( given by the parameter pContent ) // it must be inserted with a title and a type BOOL bRet = FALSE; try { Sequence< ContentInfo > aInfo = pContent->queryCreatableContentsInfo(); sal_Int32 nCount = aInfo.getLength(); if ( nCount == 0 ) return sal_False; for ( sal_Int32 i = 0; i < nCount; ++i ) { // Simply look for the first KIND_FOLDER... const ContentInfo & rCurr = aInfo[i]; if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER ) { // Make sure the only required bootstrap property is "Title", const Sequence< Property > & rProps = rCurr.Properties; if ( rProps.getLength() != 1 ) continue; if ( !rProps[ 0 ].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) continue; Sequence < ::rtl::OUString > aNames(1); ::rtl::OUString* pNames = aNames.getArray(); pNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ); Sequence < Any > aValues(1); Any* pValues = aValues.getArray(); pValues[0] = makeAny( ::rtl::OUString( m_aName ) ); Content aNewFolder; if ( !pContent->insertNewContent( rCurr.Type, aNames, aValues, aNewFolder ) ) continue; // remove old content, create an "empty" new one and initialize it with the new inserted DELETEZ( m_pContent ); m_pContent = new ::ucbhelper::Content( aNewFolder ); bRet = TRUE; } } } catch ( CommandAbortedException& ) { // any command wasn't executed successfully - not specified SetError( ERRCODE_IO_GENERAL ); } catch ( RuntimeException& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); } catch ( Exception& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); } return bRet; } sal_Int16 UCBStorage_Impl::Commit() { // send all changes to the package UCBStorageElement_Impl* pElement = m_aChildrenList.First(); sal_Int16 nRet = COMMIT_RESULT_NOTHING_TO_DO; // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no // commit command has been sent if ( ( m_nMode & STREAM_WRITE ) && ( m_bCommited || m_bDirect ) ) { try { // all errors will be caught in the "catch" statement outside the loop while ( pElement && nRet ) { ::ucbhelper::Content* pContent = pElement->GetContent(); BOOL bDeleteContent = FALSE; if ( !pContent && pElement->IsModified() ) { // if the element has never been opened, no content has been created until now bDeleteContent = TRUE; // remember to delete it later String aName( m_aURL ); aName += '/'; aName += pElement->m_aOriginalName; pContent = new ::ucbhelper::Content( aName, Reference< ::com::sun::star::ucb::XCommandEnvironment > () ); } if ( pElement->m_bIsRemoved ) { // was it inserted, then removed (so there would be nothing to do!) if ( !pElement->m_bIsInserted ) { // first remove all open stream handles if( !pElement->m_xStream.Is() || pElement->m_xStream->Clear() ) { pContent->executeCommand( ::rtl::OUString::createFromAscii("delete"), makeAny( sal_Bool( sal_True ) ) ); nRet = COMMIT_RESULT_SUCCESS; } else // couldn't release stream because there are external references to it nRet = COMMIT_RESULT_FAILURE; } } else { sal_Int16 nLocalRet = COMMIT_RESULT_NOTHING_TO_DO; if ( pElement->m_xStorage.Is() ) { // element is a storage // do a commit in the following cases: // - if storage is already inserted, and changed // - storage is not in a package // - it's a new storage, try to insert and commit if successful inserted if ( !pElement->m_bIsInserted || m_bIsLinked || pElement->m_xStorage->Insert( m_pContent ) ) { nLocalRet = pElement->m_xStorage->Commit(); pContent = pElement->GetContent(); } } else if ( pElement->m_xStream.Is() ) { // element is a stream nLocalRet = pElement->m_xStream->Commit(); if ( pElement->m_xStream->m_bIsOLEStorage ) { // OLE storage should be stored encrytped, if the storage uses encryption pElement->m_xStream->m_aContentType = String::CreateFromAscii("application/vnd.sun.star.oleobject"); Any aValue; aValue <<= (BOOL) TRUE; pElement->m_xStream->m_pContent->setPropertyValue(String::CreateFromAscii("Encrypted"), aValue ); } pContent = pElement->GetContent(); } if ( pElement->m_aName != pElement->m_aOriginalName ) { // name ( title ) of the element was changed nLocalRet = COMMIT_RESULT_SUCCESS; Any aAny; aAny <<= (rtl::OUString) pElement->m_aName; pContent->setPropertyValue( ::rtl::OUString::createFromAscii("Title"), aAny ); } if ( pElement->IsLoaded() && pElement->GetContentType() != pElement->GetOriginalContentType() ) { // mediatype of the element was changed nLocalRet = COMMIT_RESULT_SUCCESS; Any aAny; aAny <<= (rtl::OUString) pElement->GetContentType(); pContent->setPropertyValue( ::rtl::OUString::createFromAscii("MediaType"), aAny ); } if ( nLocalRet != COMMIT_RESULT_NOTHING_TO_DO ) nRet = nLocalRet; } if ( bDeleteContent ) // content was created inside the loop delete pContent; if ( nRet == COMMIT_RESULT_FAILURE ) break; pElement = m_aChildrenList.Next(); } } catch ( ContentCreationException& ) { // content could not be created SetError( ERRCODE_IO_NOTEXISTS ); return COMMIT_RESULT_FAILURE; } catch ( CommandAbortedException& ) { // any command wasn't executed successfully - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( RuntimeException& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( Exception& ) { // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } if ( m_bIsRoot && m_pContent ) { // the root storage must flush the root package content if ( nRet == COMMIT_RESULT_SUCCESS ) { try { // commit the media type to the JAR file // clipboard format and ClassId will be retrieved from the media type when the file is loaded again Any aType; aType <<= (rtl::OUString) m_aContentType; m_pContent->setPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ), aType ); if ( m_bIsLinked ) { // write a manifest file // first create a subfolder "META-inf" Content aNewSubFolder; BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, String::CreateFromAscii("META-INF"), aNewSubFolder ); if ( bRet ) { // create a stream to write the manifest file - use a temp file String aURL( aNewSubFolder.getURL() ); ::utl::TempFile* pTempFile = new ::utl::TempFile( &aURL ); // get the stream from the temp file and create an output stream wrapper SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE ); ::utl::OOutputStreamWrapper* pHelper = new ::utl::OOutputStreamWrapper( *pStream ); com::sun::star::uno::Reference < ::com::sun::star::io::XOutputStream > xOutputStream( pHelper ); // create a manifest writer object that will fill the stream Reference < ::com::sun::star::packages::manifest::XManifestWriter > xWriter = Reference< ::com::sun::star::packages::manifest::XManifestWriter > ( ::comphelper::getProcessServiceFactory()->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.packages.manifest.ManifestWriter" )), UNO_QUERY) ; sal_Int32 nCount = GetObjectCount() + 1; Sequence < Sequence < PropertyValue > > aProps( nCount ); sal_Int32 nProps = 0; GetProps( nProps, aProps, String() ); xWriter->writeManifestSequence( xOutputStream, aProps ); // move the stream to its desired location Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >() ); xWriter = NULL; xOutputStream = NULL; DELETEZ( pTempFile ); aNewSubFolder.transferContent( aSource, InsertOperation_MOVE, ::rtl::OUString::createFromAscii("manifest.xml"), NameClash::OVERWRITE ); } } else { #if OSL_DEBUG_LEVEL > 1 fprintf ( stderr, "Files: %i\n", nOpenFiles ); fprintf ( stderr, "Streams: %i\n", nOpenStreams ); #endif // force writing Any aAny; m_pContent->executeCommand( ::rtl::OUString::createFromAscii("flush"), aAny ); if ( m_pSource != 0 ) { SvStream* pStream = ::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), STREAM_STD_READ ); m_pSource->SetStreamSize(0); // m_pSource->Seek(0); *pStream >> *m_pSource; DELETEZ( pStream ); m_pSource->Seek(0); } } } catch ( CommandAbortedException& ) { // how to tell the content : forget all changes ?! // or should we assume that the content does it by itself because he throwed an exception ?! // any command wasn't executed successfully - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( RuntimeException& ) { // how to tell the content : forget all changes ?! // or should we assume that the content does it by itself because he throwed an exception ?! // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( InteractiveIOException& r ) { if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION ) SetError( ERRCODE_IO_ACCESSDENIED ); else if ( r.Code == IOErrorCode_NOT_EXISTING ) SetError( ERRCODE_IO_NOTEXISTS ); else if ( r.Code == IOErrorCode_CANT_READ ) SetError( ERRCODE_IO_CANTREAD ); else if ( r.Code == IOErrorCode_CANT_WRITE ) SetError( ERRCODE_IO_CANTWRITE ); else SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } catch ( Exception& ) { // how to tell the content : forget all changes ?! // or should we assume that the content does it by itself because he throwed an exception ?! // any other error - not specified SetError( ERRCODE_IO_GENERAL ); return COMMIT_RESULT_FAILURE; } } else if ( nRet != COMMIT_RESULT_NOTHING_TO_DO ) { // how to tell the content : forget all changes ?! Should we ?! SetError( ERRCODE_IO_GENERAL ); return nRet; } // after successfull root commit all elements names and types are adjusted and all removed elements // are also removed from the lists UCBStorageElement_Impl* pInnerElement = m_aChildrenList.First(); BOOL bRet = TRUE; while ( pInnerElement && bRet ) { UCBStorageElement_Impl* pNext = m_aChildrenList.Next(); if ( pInnerElement->m_bIsRemoved ) { // is this correct use of our list class ?! m_aChildrenList.Remove( pInnerElement ); } else { pInnerElement->m_aOriginalName = pInnerElement->m_aName; pInnerElement->m_bIsInserted = FALSE; } pInnerElement = pNext; } } m_bCommited = FALSE; } return nRet; } BOOL UCBStorage_Impl::Revert() { UCBStorageElement_Impl* pElement = m_aChildrenList.First(); BOOL bRet = TRUE; while ( pElement && bRet ) { pElement->m_bIsRemoved = FALSE; if ( pElement->m_bIsInserted ) { m_aChildrenList.Remove( pElement ); // correct usage of list ??? } else { if ( pElement->m_xStream.Is() ) { pElement->m_xStream->m_bCommited = sal_False; pElement->m_xStream->Revert(); } else if ( pElement->m_xStorage.Is() ) { pElement->m_xStorage->m_bCommited = sal_False; pElement->m_xStorage->Revert(); } pElement->m_aName = pElement->m_aOriginalName; pElement->m_bIsRemoved = FALSE; } pElement = m_aChildrenList.Next(); } return bRet; } const String& UCBStorage::GetName() const { return pImp->m_aName; // pImp->m_aURL ?! } BOOL UCBStorage::IsRoot() const { return pImp->m_bIsRoot; } void UCBStorage::SetDirty() { pImp->m_bDirty = TRUE; } void UCBStorage::SetClass( const SvGlobalName & rClass, ULONG nOriginalClipFormat, const String & rUserTypeName ) { pImp->m_aClassId = rClass; pImp->m_nFormat = nOriginalClipFormat; pImp->m_aUserTypeName = rUserTypeName; // in UCB storages only the content type will be stored, all other information can be reconstructed // ( see the UCBStorage_Impl::Init() method ) ::com::sun::star::datatransfer::DataFlavor aDataFlavor; SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor ); pImp->m_aContentType = aDataFlavor.MimeType; } void UCBStorage::SetClassId( const ClsId& rClsId ) { pImp->m_aClassId = SvGlobalName( (const CLSID&) rClsId ); if ( pImp->m_aClassId == SvGlobalName() ) return; // in OLE storages the clipboard format an the user name will be transferred when a storage is copied because both are // stored in one the substreams // UCB storages store the content type information as content type in the manifest file and so this information must be // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from // the content type pImp->m_nFormat = GetFormatId_Impl( pImp->m_aClassId ); if ( pImp->m_nFormat ) { ::com::sun::star::datatransfer::DataFlavor aDataFlavor; SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor ); pImp->m_aUserTypeName = aDataFlavor.HumanPresentableName; pImp->m_aContentType = aDataFlavor.MimeType; } } const ClsId& UCBStorage::GetClassId() const { return ( const ClsId& ) pImp->m_aClassId.GetCLSID(); } void UCBStorage::SetConvertClass( const SvGlobalName & /*rConvertClass*/, ULONG /*nOriginalClipFormat*/, const String & /*rUserTypeName*/ ) { // ??? } BOOL UCBStorage::ShouldConvert() { // ??? return FALSE; } SvGlobalName UCBStorage::GetClassName() { return pImp->m_aClassId; } ULONG UCBStorage::GetFormat() { return pImp->m_nFormat; } String UCBStorage::GetUserName() { DBG_ERROR("UserName is not implemented in UCB storages!" ); return pImp->m_aUserTypeName; } void UCBStorage::FillInfoList( SvStorageInfoList* pList ) const { // put information in childrenlist into StorageInfoList UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); while ( pElement ) { if ( !pElement->m_bIsRemoved ) { // problem: what about the size of a substorage ?! ULONG nSize = pElement->m_nSize; if ( pElement->m_xStream.Is() ) nSize = pElement->m_xStream->GetSize(); SvStorageInfo aInfo( pElement->m_aName, nSize, pElement->m_bIsStorage ); pList->Append( aInfo ); } pElement = pImp->m_aChildrenList.Next(); } } BOOL UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl& rElement, BaseStorage* pDest, const String& rNew ) const { // insert stream or storage into the list or stream of the destination storage // not into the content, this will be done on commit ! // be aware of name changes ! if ( !rElement.m_bIsStorage ) { // copy the streams data // the destination stream must not be open BaseStorageStream* pOtherStream = pDest->OpenStream( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ); BaseStorageStream* pStream = NULL; BOOL bDeleteStream = FALSE; // if stream is already open, it is allowed to copy it, so be aware of this if ( rElement.m_xStream.Is() ) pStream = rElement.m_xStream->m_pAntiImpl; if ( !pStream ) { pStream = ( const_cast < UCBStorage* > (this) )->OpenStream( rElement.m_aName, STREAM_STD_READ, pImp->m_bDirect ); bDeleteStream = TRUE; } pStream->CopyTo( pOtherStream ); SetError( pStream->GetError() ); if( pOtherStream->GetError() ) pDest->SetError( pOtherStream->GetError() ); else pOtherStream->Commit(); if ( bDeleteStream ) delete pStream; delete pOtherStream; } else { // copy the storage content // the destination storage must not be open BaseStorage* pStorage = NULL; // if stream is already open, it is allowed to copy it, so be aware of this BOOL bDeleteStorage = FALSE; if ( rElement.m_xStorage.Is() ) pStorage = rElement.m_xStorage->m_pAntiImpl; if ( !pStorage ) { pStorage = ( const_cast < UCBStorage* > (this) )->OpenStorage( rElement.m_aName, pImp->m_nMode, pImp->m_bDirect ); bDeleteStorage = TRUE; } UCBStorage* pUCBDest = PTR_CAST( UCBStorage, pDest ); UCBStorage* pUCBCopy = PTR_CAST( UCBStorage, pStorage ); BOOL bOpenUCBStorage = pUCBDest && pUCBCopy; BaseStorage* pOtherStorage = bOpenUCBStorage ? pDest->OpenUCBStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ) : pDest->OpenOLEStorage( rNew, STREAM_WRITE | STREAM_SHARE_DENYALL, pImp->m_bDirect ); // For UCB storages, the class id and the format id may differ, // do passing the class id is not sufficient. if( bOpenUCBStorage ) pOtherStorage->SetClass( pStorage->GetClassName(), pStorage->GetFormat(), pUCBCopy->pImp->m_aUserTypeName ); else pOtherStorage->SetClassId( pStorage->GetClassId() ); pStorage->CopyTo( pOtherStorage ); SetError( pStorage->GetError() ); if( pOtherStorage->GetError() ) pDest->SetError( pOtherStorage->GetError() ); else pOtherStorage->Commit(); if ( bDeleteStorage ) delete pStorage; delete pOtherStorage; } return BOOL( Good() && pDest->Good() ); } UCBStorageElement_Impl* UCBStorage::FindElement_Impl( const String& rName ) const { DBG_ASSERT( rName.Len(), "Name is empty!" ); UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); while ( pElement ) { if ( pElement->m_aName == rName && !pElement->m_bIsRemoved ) break; pElement = pImp->m_aChildrenList.Next(); } return pElement; } BOOL UCBStorage::CopyTo( BaseStorage* pDestStg ) const { DBG_ASSERT( pDestStg != ((BaseStorage*)this), "Self-Copying is not possible!" ); if ( pDestStg == ((BaseStorage*)this) ) return FALSE; // perhaps it's also a problem if one storage is a parent of the other ?! // or if not: could be optimized ?! // For UCB storages, the class id and the format id may differ, // do passing the class id is not sufficient. if( pDestStg->ISA( UCBStorage ) ) pDestStg->SetClass( pImp->m_aClassId, pImp->m_nFormat, pImp->m_aUserTypeName ); else pDestStg->SetClassId( GetClassId() ); pDestStg->SetDirty(); BOOL bRet = TRUE; UCBStorageElement_Impl* pElement = pImp->GetChildrenList().First(); while ( pElement && bRet ) { if ( !pElement->m_bIsRemoved ) bRet = CopyStorageElement_Impl( *pElement, pDestStg, pElement->m_aName ); pElement = pImp->m_aChildrenList.Next(); } if( !bRet ) SetError( pDestStg->GetError() ); return BOOL( Good() && pDestStg->Good() ); } BOOL UCBStorage::CopyTo( const String& rElemName, BaseStorage* pDest, const String& rNew ) { if( !rElemName.Len() ) return FALSE; if ( pDest == ((BaseStorage*) this) ) { // can't double an element return FALSE; } else { // for copying no optimization is usefull, because in every case the stream data must be copied UCBStorageElement_Impl* pElement = FindElement_Impl( rElemName ); if ( pElement ) return CopyStorageElement_Impl( *pElement, pDest, rNew ); else { SetError( SVSTREAM_FILE_NOT_FOUND ); return FALSE; } } } BOOL UCBStorage::Commit() { // mark this storage for sending it on root commit pImp->m_bCommited = TRUE; if ( pImp->m_bIsRoot ) // the root storage coordinates commiting by sending a Commit command to its content return ( pImp->Commit() != COMMIT_RESULT_FAILURE ); else return TRUE; } BOOL UCBStorage::Revert() { return pImp->Revert(); } BaseStorageStream* UCBStorage::OpenStream( const String& rEleName, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) { if( !rEleName.Len() ) return NULL; // try to find the storage element UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); if ( !pElement ) { // element does not exist, check if creation is allowed if( ( nMode & STREAM_NOCREATE ) ) { SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); String aName( pImp->m_aURL ); aName += '/'; aName += rEleName; UCBStorageStream* pStream = new UCBStorageStream( aName, nMode, bDirect, pKey, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); pStream->SetError( GetError() ); pStream->pImp->m_aName = rEleName; return pStream; } else { // create a new UCBStorageElement and insert it into the list pElement = new UCBStorageElement_Impl( rEleName ); pElement->m_bIsInserted = TRUE; pImp->m_aChildrenList.Insert( pElement, LIST_APPEND ); } } if ( pElement && !pElement->m_bIsFolder ) { // check if stream is already created if ( pElement->m_xStream.Is() ) { // stream has already been created; if it has no external reference, it may be opened another time if ( pElement->m_xStream->m_pAntiImpl ) { DBG_ERROR("Stream is already open!" ); SetError( SVSTREAM_ACCESS_DENIED ); // ??? return NULL; } else { // check if stream is opened with the same keyword as before // if not, generate a new stream because it could be encrypted vs. decrypted! ByteString aKey; if ( pKey ) aKey = *pKey; if ( pElement->m_xStream->m_aKey == aKey ) { pElement->m_xStream->PrepareCachedForReopen( nMode ); // DBG_ASSERT( bDirect == pElement->m_xStream->m_bDirect, "Wrong DirectMode!" ); return new UCBStorageStream( pElement->m_xStream ); } } } // stream is opened the first time pImp->OpenStream( pElement, nMode, bDirect, pKey ); // if name has been changed before creating the stream: set name! pElement->m_xStream->m_aName = rEleName; return new UCBStorageStream( pElement->m_xStream ); } return NULL; } UCBStorageStream_Impl* UCBStorage_Impl::OpenStream( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect, const ByteString* pKey ) { String aName( m_aURL ); aName += '/'; aName += pElement->m_aOriginalName; pElement->m_xStream = new UCBStorageStream_Impl( aName, nMode, NULL, bDirect, pKey, m_bRepairPackage, m_xProgressHandler ); return pElement->m_xStream; } BaseStorage* UCBStorage::OpenUCBStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) { if( !rEleName.Len() ) return NULL; return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE ); } BaseStorage* UCBStorage::OpenOLEStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) { if( !rEleName.Len() ) return NULL; return OpenStorage_Impl( rEleName, nMode, bDirect, FALSE ); } BaseStorage* UCBStorage::OpenStorage( const String& rEleName, StreamMode nMode, BOOL bDirect ) { if( !rEleName.Len() ) return NULL; return OpenStorage_Impl( rEleName, nMode, bDirect, TRUE ); } BaseStorage* UCBStorage::OpenStorage_Impl( const String& rEleName, StreamMode nMode, BOOL bDirect, BOOL bForceUCBStorage ) { // try to find the storage element UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); if ( !pElement ) { // element does not exist, check if creation is allowed if( ( nMode & STREAM_NOCREATE ) ) { SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); String aName( pImp->m_aURL ); aName += '/'; aName += rEleName; // ??? UCBStorage *pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); pStorage->pImp->m_bIsRoot = FALSE; pStorage->pImp->m_bListCreated = sal_True; // the storage is pretty new, nothing to read pStorage->SetError( GetError() ); return pStorage; } // create a new UCBStorageElement and insert it into the list // problem: perhaps an OLEStorage should be created ?! // Because nothing is known about the element that should be created, an external parameter is needed ! pElement = new UCBStorageElement_Impl( rEleName ); pElement->m_bIsInserted = TRUE; pImp->m_aChildrenList.Insert( pElement, LIST_APPEND ); } if ( !pElement->m_bIsFolder && ( pElement->m_bIsStorage || !bForceUCBStorage ) ) { // create OLE storages on a stream ( see ctor of SotStorage ) // Such a storage will be created on a UCBStorageStream; it will write into the stream // if it is opened in direct mode or when it is committed. In this case the stream will be // modified and then it MUST be treated as commited. if ( !pElement->m_xStream.Is() ) { BaseStorageStream* pStr = OpenStream( rEleName, nMode, bDirect ); UCBStorageStream* pStream = PTR_CAST( UCBStorageStream, pStr ); if ( !pStream ) { SetError( ( nMode & STREAM_WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND ); return NULL; } pElement->m_xStream = pStream->pImp; delete pStream; } pElement->m_xStream->PrepareCachedForReopen( nMode ); pElement->m_xStream->Init(); pElement->m_bIsStorage = TRUE; return pElement->m_xStream->CreateStorage(); // can only be created in transacted mode } else if ( pElement->m_xStorage.Is() ) { // storage has already been opened; if it has no external reference, it may be opened another time if ( pElement->m_xStorage->m_pAntiImpl ) { DBG_ERROR("Storage is already open!" ); SetError( SVSTREAM_ACCESS_DENIED ); // ??? } else { BOOL bIsWritable = (( pElement->m_xStorage->m_nMode & STREAM_WRITE ) != 0); if ( !bIsWritable && (( nMode & STREAM_WRITE ) != 0 )) { String aName( pImp->m_aURL ); aName += '/'; aName += pElement->m_aOriginalName; UCBStorage* pStorage = new UCBStorage( aName, nMode, bDirect, FALSE, pImp->m_bRepairPackage, pImp->m_xProgressHandler ); pElement->m_xStorage = pStorage->pImp; return pStorage; } else { // DBG_ASSERT( bDirect == pElement->m_xStorage->m_bDirect, "Wrong DirectMode!" ); return new UCBStorage( pElement->m_xStorage ); } } } else if ( !pElement->m_xStream.Is() ) { // storage is opened the first time BOOL bIsWritable = (( pImp->m_nMode & STREAM_WRITE ) != 0 ); if ( pImp->m_bIsLinked && pImp->m_bIsRoot && bIsWritable ) { // make sure that the root storage object has been created before substorages will be created INetURLObject aFolderObj( pImp->m_aURL ); String aName = aFolderObj.GetName(); aFolderObj.removeSegment(); Content aFolder( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ), Reference < XCommandEnvironment >() ); pImp->m_pContent = new Content; BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, pImp->m_aName, *pImp->m_pContent ); if ( !bRet ) { SetError( SVSTREAM_CANNOT_MAKE ); return NULL; } } UCBStorage_Impl* pStor = pImp->OpenStorage( pElement, nMode, bDirect ); if ( pStor ) { if ( pElement->m_bIsInserted ) pStor->m_bListCreated = sal_True; // the storage is pretty new, nothing to read return new UCBStorage( pStor ); } } return NULL; } UCBStorage_Impl* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, BOOL bDirect ) { UCBStorage_Impl* pRet = NULL; String aName( m_aURL ); aName += '/'; aName += pElement->m_aOriginalName; // ??? pElement->m_bIsStorage = pElement->m_bIsFolder = TRUE; if ( m_bIsLinked && !::utl::UCBContentHelper::Exists( aName ) ) { Content aNewFolder; BOOL bRet = ::utl::UCBContentHelper::MakeFolder( *m_pContent, pElement->m_aOriginalName, aNewFolder ); if ( bRet ) pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler ); } else { pRet = new UCBStorage_Impl( aName, nMode, NULL, bDirect, FALSE, m_bRepairPackage, m_xProgressHandler ); } if ( pRet ) { pRet->m_bIsLinked = m_bIsLinked; pRet->m_bIsRoot = FALSE; // if name has been changed before creating the stream: set name! pRet->m_aName = pElement->m_aOriginalName; pElement->m_xStorage = pRet; } if ( pRet ) pRet->Init(); return pRet; } BOOL UCBStorage::IsStorage( const String& rEleName ) const { if( !rEleName.Len() ) return FALSE; const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); return ( pElement && pElement->m_bIsStorage ); } BOOL UCBStorage::IsStream( const String& rEleName ) const { if( !rEleName.Len() ) return FALSE; const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); return ( pElement && !pElement->m_bIsStorage ); } BOOL UCBStorage::IsContained( const String & rEleName ) const { if( !rEleName.Len() ) return FALSE; const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); return ( pElement != NULL ); } BOOL UCBStorage::Remove( const String& rEleName ) { if( !rEleName.Len() ) return FALSE; UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); if ( pElement ) { pElement->m_bIsRemoved = TRUE; } else SetError( SVSTREAM_FILE_NOT_FOUND ); return ( pElement != NULL ); } BOOL UCBStorage::Rename( const String& rEleName, const String& rNewName ) { if( !rEleName.Len()|| !rNewName.Len() ) return FALSE; UCBStorageElement_Impl *pAlreadyExisting = FindElement_Impl( rNewName ); if ( pAlreadyExisting ) { SetError( SVSTREAM_ACCESS_DENIED ); return FALSE; // can't change to a name that is already used } UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName ); if ( pElement ) { pElement->m_aName = rNewName; } else SetError( SVSTREAM_FILE_NOT_FOUND ); return pElement != NULL; } BOOL UCBStorage::MoveTo( const String& rEleName, BaseStorage* pNewSt, const String& rNewName ) { if( !rEleName.Len() || !rNewName.Len() ) return FALSE; if ( pNewSt == ((BaseStorage*) this) && !FindElement_Impl( rNewName ) ) { return Rename( rEleName, rNewName ); } else { /* if ( PTR_CAST( UCBStorage, pNewSt ) ) { // because the element is moved, not copied, a special optimization is possible : // first copy the UCBStorageElement; flag old element as "Removed" and new as "Inserted", // clear original name/type of the new element // if moved element is open: copy content, but change absolute URL ( and those of all children of the element! ), // clear original name/type of new content, keep the old original stream/storage, but forget its working streams, // close original UCBContent and original stream, only the TempFile and its stream may remain unchanged, but now // belong to the new content // if original and editable stream are identical ( readonly element ), it has to be copied to the editable // stream of the destination object // Not implemented at the moment ( risky?! ), perhaps later } */ // MoveTo is done by first copying to the new destination and then removing the old element BOOL bRet = CopyTo( rEleName, pNewSt, rNewName ); if ( bRet ) bRet = Remove( rEleName ); return bRet; } } BOOL UCBStorage::ValidateFAT() { // ??? return TRUE; } BOOL UCBStorage::Validate( BOOL bWrite ) const { // ??? return ( !bWrite || ( pImp->m_nMode & STREAM_WRITE ) ); } BOOL UCBStorage::ValidateMode( StreamMode m ) const { // ??? if( m == ( STREAM_READ | STREAM_TRUNC ) ) // from stg.cxx return TRUE; USHORT nCurMode = 0xFFFF; if( ( m & 3 ) == STREAM_READ ) { // only SHARE_DENYWRITE or SHARE_DENYALL allowed if( ( ( m & STREAM_SHARE_DENYWRITE ) && ( nCurMode & STREAM_SHARE_DENYWRITE ) ) || ( ( m & STREAM_SHARE_DENYALL ) && ( nCurMode & STREAM_SHARE_DENYALL ) ) ) return TRUE; } else { // only SHARE_DENYALL allowed // storages open in r/o mode are OK, since only // the commit may fail if( ( m & STREAM_SHARE_DENYALL ) && ( nCurMode & STREAM_SHARE_DENYALL ) ) return TRUE; } return TRUE; } const SvStream* UCBStorage::GetSvStream() const { // this would cause a complete download of the file // as it looks, this method is NOT used inside of SOT, only exported by class SotStorage - but for what ??? return pImp->m_pSource; } BOOL UCBStorage::Equals( const BaseStorage& rStorage ) const { // ??? return ((BaseStorage*)this) == &rStorage; } BOOL UCBStorage::IsStorageFile( const String& rFileName ) { String aFileURL = rFileName; INetURLObject aObj( aFileURL ); if ( aObj.GetProtocol() == INET_PROT_NOT_VALID ) { ::utl::LocalFileHelper::ConvertPhysicalNameToURL( rFileName, aFileURL ); aObj.SetURL( aFileURL ); aFileURL = aObj.GetMainURL( INetURLObject::NO_DECODE ); } SvStream * pStm = ::utl::UcbStreamHelper::CreateStream( aFileURL, STREAM_STD_READ ); BOOL bRet = UCBStorage::IsStorageFile( pStm ); delete pStm; return bRet; } BOOL UCBStorage::IsStorageFile( SvStream* pFile ) { if ( !pFile ) return FALSE; ULONG nPos = pFile->Tell(); pFile->Seek( STREAM_SEEK_TO_END ); if ( pFile->Tell() < 4 ) return FALSE; pFile->Seek(0); UINT32 nBytes; *pFile >> nBytes; // search for the magic bytes BOOL bRet = ( nBytes == 0x04034b50 ); if ( !bRet ) { // disk spanned file have an additional header in front of the usual one bRet = ( nBytes == 0x08074b50 ); if ( bRet ) { *pFile >> nBytes; bRet = ( nBytes == 0x04034b50 ); } } pFile->Seek( nPos ); return bRet; } BOOL UCBStorage::IsDiskSpannedFile( SvStream* pFile ) { if ( !pFile ) return FALSE; ULONG nPos = pFile->Tell(); pFile->Seek( STREAM_SEEK_TO_END ); if ( !pFile->Tell() ) return FALSE; pFile->Seek(0); UINT32 nBytes; *pFile >> nBytes; // disk spanned file have an additional header in front of the usual one BOOL bRet = ( nBytes == 0x08074b50 ); if ( bRet ) { *pFile >> nBytes; bRet = ( nBytes == 0x04034b50 ); } pFile->Seek( nPos ); return bRet; } String UCBStorage::GetLinkedFile( SvStream &rStream ) { String aString; ULONG nPos = rStream.Tell(); rStream.Seek( STREAM_SEEK_TO_END ); if ( !rStream.Tell() ) return aString; rStream.Seek(0); UINT32 nBytes; rStream >> nBytes; if( nBytes == 0x04034b50 ) { ByteString aTmp; rStream.ReadByteString( aTmp ); if ( aTmp.CompareTo( "ContentURL=", 11 ) == COMPARE_EQUAL ) { aTmp.Erase( 0, 11 ); aString = String( aTmp, RTL_TEXTENCODING_UTF8 ); } } rStream.Seek( nPos ); return aString; } String UCBStorage::CreateLinkFile( const String& rName ) { // create a stream to write the link file - use a temp file, because it may be no file content INetURLObject aFolderObj( rName ); String aName = aFolderObj.GetName(); aFolderObj.removeSegment(); String aFolderURL( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ); ::utl::TempFile* pTempFile = new ::utl::TempFile( &aFolderURL ); // get the stream from the temp file SvStream* pStream = pTempFile->GetStream( STREAM_STD_READWRITE | STREAM_TRUNC ); // write header *pStream << ( UINT32 ) 0x04034b50; // assemble a new folder name in the destination folder INetURLObject aObj( rName ); String aTmpName = aObj.GetName(); String aTitle = String::CreateFromAscii( "content." ); aTitle += aTmpName; // create a folder and store its URL Content aFolder( aFolderURL, Reference < XCommandEnvironment >() ); Content aNewFolder; BOOL bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTitle, aNewFolder ); if ( !bRet ) { aFolderObj.insertName( aTitle ); if ( ::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) ) { // Hack, because already existing files give the same CommandAbortedException as any other error ! // append a number until the name can be used for a new folder aTitle += '.'; for ( sal_Int32 i=0; !bRet; i++ ) { String aTmp( aTitle ); aTmp += String::CreateFromInt32( i ); bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, aTmp, aNewFolder ); if ( bRet ) aTitle = aTmp; else { aFolderObj.SetName( aTmp ); if ( !::utl::UCBContentHelper::Exists( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) ) ) // Hack, because already existing files give the same CommandAbortedException as any other error ! break; } } } } if ( bRet ) { // get the URL aObj.SetName( aTitle ); String aURL = aObj.GetMainURL( INetURLObject::NO_DECODE ); // store it as key/value pair String aLink = String::CreateFromAscii("ContentURL="); aLink += aURL; pStream->WriteByteString( aLink, RTL_TEXTENCODING_UTF8 ); pStream->Flush(); // move the stream to its desired location Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >() ); DELETEZ( pTempFile ); aFolder.transferContent( aSource, InsertOperation_MOVE, aName, NameClash::OVERWRITE ); return aURL; } pTempFile->EnableKillingFile( TRUE ); delete pTempFile; return String(); } BOOL UCBStorage::SetProperty( const String& rName, const ::com::sun::star::uno::Any& rValue ) { if ( rName.CompareToAscii("Title") == COMPARE_EQUAL ) return FALSE; if ( rName.CompareToAscii("MediaType") == COMPARE_EQUAL ) { ::rtl::OUString aTmp; rValue >>= aTmp; pImp->m_aContentType = aTmp; } try { if ( pImp->GetContent() ) { pImp->m_pContent->setPropertyValue( rName, rValue ); return TRUE; } } catch ( Exception& ) { } return FALSE; } BOOL UCBStorage::GetProperty( const String& rName, ::com::sun::star::uno::Any& rValue ) { try { if ( pImp->GetContent() ) { rValue = pImp->m_pContent->getPropertyValue( rName ); return TRUE; } } catch ( Exception& ) { } return FALSE; } BOOL UCBStorage::GetProperty( const String& rEleName, const String& rName, ::com::sun::star::uno::Any& rValue ) { UCBStorageElement_Impl *pEle = FindElement_Impl( rEleName ); if ( !pEle ) return FALSE; if ( !pEle->m_bIsFolder ) { if ( !pEle->m_xStream.Is() ) pImp->OpenStream( pEle, pImp->m_nMode, pImp->m_bDirect ); if ( pEle->m_xStream->m_nError ) { pEle->m_xStream.Clear(); return FALSE; } try { if ( pEle->m_xStream->m_pContent ) { rValue = pEle->m_xStream->m_pContent->getPropertyValue( rName ); return TRUE; } } catch ( Exception& ) { } } else { if ( !pEle->m_xStorage.Is() ) pImp->OpenStorage( pEle, pImp->m_nMode, pImp->m_bDirect ); if ( pEle->m_xStorage->m_nError ) { pEle->m_xStorage.Clear(); return FALSE; } try { if ( pEle->m_xStorage->GetContent() ) { rValue = pEle->m_xStorage->m_pContent->getPropertyValue( rName ); return TRUE; } } catch ( Exception& ) { } } return FALSE; } UNOStorageHolderList* UCBStorage::GetUNOStorageHolderList() { if ( !pImp->m_pUNOStorageHolderList ) pImp->m_pUNOStorageHolderList = new UNOStorageHolderList; return pImp->m_pUNOStorageHolderList; }