summaryrefslogtreecommitdiffstats
path: root/sc/source/filter/excel/xistream.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/filter/excel/xistream.cxx')
-rw-r--r--sc/source/filter/excel/xistream.cxx1084
1 files changed, 1084 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xistream.cxx b/sc/source/filter/excel/xistream.cxx
new file mode 100644
index 000000000..0a6c24aca
--- /dev/null
+++ b/sc/source/filter/excel/xistream.cxx
@@ -0,0 +1,1084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <ftools.hxx>
+#include <xistream.hxx>
+#include <xlstring.hxx>
+#include <xiroot.hxx>
+
+#include <vector>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+// Decryption
+XclImpDecrypter::XclImpDecrypter() :
+ mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) :
+ ::comphelper::IDocPasswordVerifier(),
+ mnError( rSrc.mnError ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::~XclImpDecrypter()
+{
+}
+
+XclImpDecrypterRef XclImpDecrypter::Clone() const
+{
+ XclImpDecrypterRef xNewDecr;
+ if( IsValid() )
+ xNewDecr.reset( OnClone() );
+ return xNewDecr;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ o_rEncryptionData = OnVerifyPassword( rPassword );
+ mnError = o_rEncryptionData.hasElements() ? ERRCODE_NONE : ERRCODE_ABORT;
+ return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ bool bValid = OnVerifyEncryptionData( rEncryptionData );
+ mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT;
+ return bValid ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+void XclImpDecrypter::Update( const SvStream& rStrm, sal_uInt16 nRecSize )
+{
+ if( IsValid() )
+ {
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) )
+ {
+ OnUpdate( mnOldPos, nNewPos, nRecSize );
+ mnOldPos = nNewPos;
+ mnRecSize = nRecSize;
+ }
+ }
+}
+
+sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+ if( pData && nBytes )
+ {
+ if( IsValid() )
+ {
+ Update( rStrm, mnRecSize );
+ nRet = OnRead( rStrm, static_cast< sal_uInt8* >( pData ), nBytes );
+ mnOldPos = rStrm.Tell();
+ }
+ else
+ nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pData, nBytes));
+ }
+ return nRet;
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) :
+ mnKey( nKey ),
+ mnHash( nHash )
+{
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) :
+ XclImpDecrypter( rSrc ),
+ maEncryptionData( rSrc.maEncryptionData ),
+ mnKey( rSrc.mnKey ),
+ mnHash( rSrc.mnHash )
+{
+ if( IsValid() )
+ maCodec.InitCodec( maEncryptionData );
+}
+
+XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const
+{
+ return new XclImpBiff5Decrypter( *this );
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff5Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ /* Convert password to a byte string. TODO: this needs some fine tuning
+ according to the spec... */
+ OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() );
+ sal_Int32 nLen = aBytePassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // init codec
+ maCodec.InitKey( reinterpret_cast<sal_uInt8 const *>(aBytePassword.getStr()) );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ {
+ maEncryptionData = maCodec.GetEncryptionData();
+
+ // since the export uses Std97 encryption always we have to request it here
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ sal_Int32 nInd = 0;
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&rPassword, &nInd](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( rPassword[nInd] );
+ ++nInd;
+ });
+
+ uno::Sequence< sal_Int8 > aDocId = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+ OSL_ENSURE( aDocId.getLength() == 16, "Unexpected length of the sequence!" );
+
+ ::msfilter::MSCodec_Std97 aCodec97;
+ aCodec97.InitKey(aPassVect.data(), reinterpret_cast<sal_uInt8 const *>(aDocId.getConstArray()));
+
+ // merge the EncryptionData, there should be no conflicts
+ ::comphelper::SequenceAsHashMap aEncryptionHash( maEncryptionData );
+ aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
+ aEncryptionHash >> maEncryptionData;
+ }
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff5Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ maCodec.InitCodec( rEncryptionData );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff5Decrypter::OnUpdate( std::size_t /*nOldStrmPos*/, std::size_t nNewStrmPos, sal_uInt16 nRecSize )
+{
+ maCodec.InitCipher();
+ maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F );
+}
+
+sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pnData, nBytes));
+ maCodec.Decode( pnData, nRet );
+ return nRet;
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : maSalt(std::move(rSalt))
+ , maVerifier(std::move(rVerifier))
+ , maVerifierHash(std::move(rVerifierHash))
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc)
+ : XclImpDecrypter(rSrc)
+ , maEncryptionData(rSrc.maEncryptionData)
+ , maSalt(rSrc.maSalt)
+ , maVerifier(rSrc.maVerifier)
+ , maVerifierHash(rSrc.maVerifierHash)
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8StdDecrypter::XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8StdDecrypter* XclImpBiff8StdDecrypter::OnClone() const
+{
+ return new XclImpBiff8StdDecrypter(*this);
+}
+
+XclImpBiff8CryptoAPIDecrypter::XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8CryptoAPIDecrypter* XclImpBiff8CryptoAPIDecrypter::OnClone() const
+{
+ return new XclImpBiff8CryptoAPIDecrypter(*this);
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff8Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ sal_Int32 nLen = rPassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // copy string to sal_uInt16 array
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ const sal_Unicode* pcChar = rPassword.getStr();
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&pcChar](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( *pcChar );
+ ++pcChar;
+ });
+
+ // init codec
+ mpCodec->InitKey(aPassVect.data(), maSalt.data());
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = mpCodec->GetEncryptionData();
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff8Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ mpCodec->InitCodec( rEncryptionData );
+
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff8Decrypter::OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 /*nRecSize*/ )
+{
+ if( nNewStrmPos == nOldStrmPos )
+ return;
+
+ sal_uInt32 nOldBlock = GetBlock( nOldStrmPos );
+ sal_uInt16 nOldOffset = GetOffset( nOldStrmPos );
+
+ sal_uInt32 nNewBlock = GetBlock( nNewStrmPos );
+ sal_uInt16 nNewOffset = GetOffset( nNewStrmPos );
+
+ /* Rekey cipher, if block changed or if previous offset in same block. */
+ if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) )
+ {
+ mpCodec->InitCipher( nNewBlock );
+ nOldOffset = 0; // reset nOldOffset for next if() statement
+ }
+
+ /* Seek to correct offset. */
+ if( nNewOffset > nOldOffset )
+ mpCodec->Skip( nNewOffset - nOldOffset );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+
+ sal_uInt8* pnCurrData = pnData;
+ sal_uInt16 nBytesLeft = nBytes;
+ while( nBytesLeft )
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() );
+ sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft );
+
+ // read the block from stream
+ nRet = nRet + static_cast<sal_uInt16>(rStrm.ReadBytes(pnCurrData, nDecBytes));
+ // decode the block inplace
+ mpCodec->Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes );
+ if( GetOffset( rStrm.Tell() ) == 0 )
+ mpCodec->InitCipher( GetBlock( rStrm.Tell() ) );
+
+ pnCurrData += nDecBytes;
+ nBytesLeft = nBytesLeft - nDecBytes;
+ }
+
+ return nRet;
+}
+
+sal_uInt32 XclImpBiff8Decrypter::GetBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::GetOffset( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+// Stream
+XclImpStreamPos::XclImpStreamPos() :
+ mnPos( STREAM_SEEK_TO_BEGIN ),
+ mnNextPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrSize( 0 ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mbValid( false )
+{
+}
+
+void XclImpStreamPos::Set(
+ const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid )
+{
+ mnPos = rStrm.Tell();
+ mnNextPos = nNextPos;
+ mnCurrSize = nCurrSize;
+ mnRawRecId = nRawRecId;
+ mnRawRecSize = nRawRecSize;
+ mnRawRecLeft = nRawRecLeft;
+ mbValid = bValid;
+}
+
+void XclImpStreamPos::Get(
+ SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const
+{
+ rStrm.Seek( mnPos );
+ rnNextPos = mnNextPos;
+ rnCurrSize = mnCurrSize;
+ rnRawRecId = mnRawRecId;
+ rnRawRecSize = mnRawRecSize;
+ rnRawRecLeft = mnRawRecLeft;
+ rbValid = mbValid;
+}
+
+XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm )
+{
+ XclBiff eBiff = EXC_BIFF_UNKNOWN;
+
+ rStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt16 nBofId(0), nBofSize(0);
+ rStrm.ReadUInt16( nBofId ).ReadUInt16( nBofSize );
+
+ if (rStrm.good() && 4 <= nBofSize && nBofSize <= 16) switch( nBofId )
+ {
+ case EXC_ID2_BOF:
+ eBiff = EXC_BIFF2;
+ break;
+ case EXC_ID3_BOF:
+ eBiff = EXC_BIFF3;
+ break;
+ case EXC_ID4_BOF:
+ eBiff = EXC_BIFF4;
+ break;
+ case EXC_ID5_BOF:
+ {
+ sal_uInt16 nVersion(0);
+ rStrm.ReadUInt16( nVersion );
+ // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
+ switch( nVersion & 0xFF00 )
+ {
+ case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752#
+ case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break;
+ case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break;
+ case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break;
+ case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break;
+ case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break;
+ default: SAL_WARN("sc", "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x" << std::hex << nVersion );
+ }
+ }
+ break;
+ }
+ return eBiff;
+}
+
+XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot ) :
+ mrStrm( rInStrm ),
+ mrRoot( rRoot ),
+ mnGlobRecId( EXC_ID_UNKNOWN ),
+ mbGlobValidRec( false ),
+ mbHasGlobPos( false ),
+ mnNextRecPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrRecSize( 0 ),
+ mnComplRecSize( 0 ),
+ mbHasComplRec( false ),
+ mnRecId( EXC_ID_UNKNOWN ),
+ mnAltContId( EXC_ID_UNKNOWN ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mcNulSubst( '?' ),
+ mbCont( true ),
+ mbUseDecr( false ),
+ mbValidRec( false ),
+ mbValid( false )
+{
+ mnStreamSize = mrStrm.TellEnd();
+ mrStrm.Seek( STREAM_SEEK_TO_BEGIN );
+}
+
+XclImpStream::~XclImpStream()
+{
+}
+
+bool XclImpStream::StartNextRecord()
+{
+ maPosStack.clear();
+
+ /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application
+ "Crystal Report" writes zero records between other records) */
+ std::size_t nZeroRecCount = 5;
+ bool bIsZeroRec = false;
+
+ do
+ {
+ mbValidRec = ReadNextRawRecHeader();
+ bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0);
+ if( bIsZeroRec ) --nZeroRecCount;
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ }
+ while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) );
+
+ mbValidRec = mbValidRec && !bIsZeroRec;
+ mbValid = mbValidRec;
+ SetupRecord();
+
+ return mbValidRec;
+}
+
+bool XclImpStream::StartNextRecord( std::size_t nNextRecPos )
+{
+ mnNextRecPos = nNextRecPos;
+ return StartNextRecord();
+}
+
+void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId )
+{
+ if( mbValidRec )
+ {
+ maPosStack.clear();
+ RestorePosition( maFirstRec );
+ mnCurrRecSize = mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !bContLookup;
+ mbCont = bContLookup;
+ mnAltContId = nAltContId;
+ EnableDecryption();
+ }
+}
+
+void XclImpStream::RewindRecord()
+{
+ mnNextRecPos = maFirstRec.GetPos();
+ mbValid = mbValidRec = false;
+}
+
+void XclImpStream::SetDecrypter( XclImpDecrypterRef const & xDecrypter )
+{
+ mxDecrypter = xDecrypter;
+ EnableDecryption();
+ SetupDecrypter();
+}
+
+void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xNewDecr;
+ if( rStrm.mxDecrypter )
+ xNewDecr = rStrm.mxDecrypter->Clone();
+ SetDecrypter( xNewDecr );
+}
+
+void XclImpStream::EnableDecryption( bool bEnable )
+{
+ mbUseDecr = bEnable && mxDecrypter && mxDecrypter->IsValid();
+}
+
+void XclImpStream::PushPosition()
+{
+ maPosStack.emplace_back( );
+ StorePosition( maPosStack.back() );
+}
+
+void XclImpStream::PopPosition()
+{
+ OSL_ENSURE( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" );
+ if( !maPosStack.empty() )
+ {
+ RestorePosition( maPosStack.back() );
+ maPosStack.pop_back();
+ }
+}
+
+void XclImpStream::StoreGlobalPosition()
+{
+ StorePosition( maGlobPos );
+ mnGlobRecId = mnRecId;
+ mbGlobValidRec = mbValidRec;
+ mbHasGlobPos = true;
+}
+
+void XclImpStream::SeekGlobalPosition()
+{
+ OSL_ENSURE( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" );
+ if( mbHasGlobPos )
+ {
+ RestorePosition( maGlobPos );
+ mnRecId = mnGlobRecId;
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = !mbCont;
+ mbValidRec = mbGlobValidRec;
+ }
+}
+
+std::size_t XclImpStream::GetRecPos() const
+{
+ return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END;
+}
+
+std::size_t XclImpStream::GetRecSize()
+{
+ if( !mbHasComplRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = true;
+ PopPosition();
+ }
+ return mnComplRecSize;
+}
+
+std::size_t XclImpStream::GetRecLeft()
+{
+ return mbValid ? (GetRecSize() - GetRecPos()) : 0;
+}
+
+sal_uInt16 XclImpStream::GetNextRecId()
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if( mbValidRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // skip following CONTINUE records
+ if( mnNextRecPos < mnStreamSize )
+ {
+ mrStrm.Seek( mnNextRecPos );
+ mrStrm.ReadUInt16( nRecId );
+ }
+ PopPosition();
+ }
+ return nRecId;
+}
+
+sal_uInt16 XclImpStream::PeekRecId( std::size_t nPos )
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if (mbValidRec && nPos < mnStreamSize)
+ {
+ sal_uInt64 const nCurPos = mrStrm.Tell();
+ mrStrm.Seek(nPos);
+ mrStrm.ReadUInt16( nRecId );
+ mrStrm.Seek(nCurPos);
+ }
+ return nRecId;
+}
+
+sal_uInt8 XclImpStream::ReaduInt8()
+{
+ sal_uInt8 nValue = 0;
+ if( EnsureRawReadSize( 1 ) )
+ {
+ if( mbUseDecr )
+ mxDecrypter->Read( mrStrm, &nValue, 1 );
+ else
+ mrStrm.ReadUChar( nValue );
+ --mnRawRecLeft;
+ }
+ return nValue;
+}
+
+sal_Int16 XclImpStream::ReadInt16()
+{
+ sal_Int16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = static_cast< sal_Int16 >( SVBT16ToUInt16( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_uInt16 XclImpStream::ReaduInt16()
+{
+ sal_uInt16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = SVBT16ToUInt16( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_Int32 XclImpStream::ReadInt32()
+{
+ sal_Int32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+sal_uInt32 XclImpStream::ReaduInt32()
+{
+ sal_uInt32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = SVBT32ToUInt32( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+double XclImpStream::ReadDouble()
+{
+ double nValue = 0;
+ if( EnsureRawReadSize( 8 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT64 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 8 );
+ nValue = SVBT64ToDouble( pnBuffer );
+ }
+ else
+ mrStrm.ReadDouble( nValue );
+ mnRawRecLeft -= 8;
+ }
+ return nValue;
+}
+
+std::size_t XclImpStream::Read( void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( mbValid && pData && (nBytes > 0) )
+ {
+ sal_uInt8* pnBuffer = static_cast< sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+
+ while( mbValid && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize );
+ nRet += nReadRet;
+ mbValid = (nReadSize == nReadRet);
+ OSL_ENSURE( mbValid, "XclImpStream::Read - stream read error" );
+ pnBuffer += nReadRet;
+ nBytesLeft -= nReadRet;
+ if( mbValid && (nBytesLeft > 0) )
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Read - record overread" );
+ }
+ }
+ return nRet;
+}
+
+std::size_t XclImpStream::CopyToStream( SvStream& rOutStrm, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if (mbValid && nBytes)
+ {
+ const std::size_t nMaxBuffer = 4096;
+ std::vector<sal_uInt8> aBuffer(o3tl::sanitizing_min(nBytes, nMaxBuffer));
+ std::size_t nBytesLeft = nBytes;
+
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadSize = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ nRet += Read(aBuffer.data(), nReadSize);
+ // writing more bytes than read results in invalid memory access
+ SAL_WARN_IF(nRet != nReadSize, "sc", "read less bytes than requested");
+ rOutStrm.WriteBytes(aBuffer.data(), nReadSize);
+ nBytesLeft -= nReadSize;
+ }
+ }
+ return nRet;
+}
+
+void XclImpStream::CopyRecordToStream( SvStream& rOutStrm )
+{
+ if( mbValidRec )
+ {
+ PushPosition();
+ RestorePosition( maFirstRec );
+ CopyToStream( rOutStrm, GetRecSize() );
+ PopPosition();
+ }
+}
+
+void XclImpStream::Seek( std::size_t nPos )
+{
+ if( !mbValidRec )
+ return;
+
+ std::size_t nCurrPos = GetRecPos();
+ if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward
+ {
+ RestorePosition( maFirstRec );
+ Ignore( nPos );
+ }
+ else if( nPos > nCurrPos ) // forward
+ {
+ Ignore( nPos - nCurrPos );
+ }
+}
+
+void XclImpStream::Ignore( std::size_t nBytes )
+{
+ // implementation similar to Read(), but without really reading anything
+ std::size_t nBytesLeft = nBytes;
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ mbValid = checkSeek(mrStrm, mrStrm.Tell() + nReadSize);
+ mnRawRecLeft = mnRawRecLeft - nReadSize;
+ nBytesLeft -= nReadSize;
+ if (mbValid && nBytesLeft > 0)
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Ignore - record overread" );
+ }
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags )
+{
+ OSL_ENSURE( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" );
+ rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT );
+ rbRich = ::get_flag( nFlags, EXC_STRF_RICH );
+ rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST );
+ rnFormatRuns = rbRich ? ReaduInt16() : 0;
+ rnExtInf = rbFareast ? ReaduInt32() : 0;
+ return rnExtInf + 4 * rnFormatRuns;
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags )
+{
+ bool bRich, bFareast;
+ sal_uInt16 nCrun;
+ sal_uInt32 nExtInf;
+ return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags );
+}
+
+OUString XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ OUStringBuffer aRet(o3tl::sanitizing_min<sal_uInt16>(nChars, mnRawRecLeft / (b16Bit ? 2 : 1)));
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while (IsValid() && nCharsLeft)
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::ReadRawUniString - missing a byte" );
+ }
+ else
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+
+ std::unique_ptr<sal_Unicode[]> pcBuffer(new sal_Unicode[nReadSize + 1]);
+
+ sal_Unicode* pcUniChar = pcBuffer.get();
+ sal_Unicode* pcEndChar = pcBuffer.get() + nReadSize;
+
+ if( b16Bit )
+ {
+ sal_uInt16 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt16();
+ (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+ else
+ {
+ sal_uInt8 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt8();
+ (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+
+ *pcEndChar = '\0';
+ // this has the side-effect of only copying as far as the first null, which appears to be intentional. e.g.
+ // see tdf#124318
+ aRet.append( pcBuffer.get() );
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ OUString aRet( ReadRawUniString( nChars, b16Bit ) );
+ Ignore( nExtSize );
+ return aRet;
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars )
+{
+ return ReadUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadUniString()
+{
+ return ReadUniString( ReaduInt16() );
+}
+
+void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while( IsValid() && (nCharsLeft > 0) )
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::IgnoreRawUniString - missing a byte" );
+ Ignore( nReadSize * 2 );
+ }
+ else
+ {
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+ Ignore( nReadSize );
+ }
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ IgnoreRawUniString( nChars, b16Bit );
+ Ignore( nExtSize );
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars )
+{
+ IgnoreUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadRawByteString( sal_uInt16 nChars )
+{
+ nChars = GetMaxRawReadSize(nChars);
+ std::unique_ptr<char[]> pcBuffer(new char[ nChars + 1 ]);
+ sal_uInt16 nCharsRead = ReadRawData( pcBuffer.get(), nChars );
+ pcBuffer[ nCharsRead ] = '\0';
+ OUString aRet( pcBuffer.get(), strlen(pcBuffer.get()), mrRoot.GetTextEncoding() );
+ return aRet;
+}
+
+OUString XclImpStream::ReadByteString( bool b16BitLen )
+{
+ return ReadRawByteString( b16BitLen ? ReaduInt16() : ReaduInt8() );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpStream::StorePosition( XclImpStreamPos& rPos )
+{
+ rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+}
+
+void XclImpStream::RestorePosition( const XclImpStreamPos& rPos )
+{
+ rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+ SetupDecrypter();
+}
+
+bool XclImpStream::ReadNextRawRecHeader()
+{
+ bool bRet = checkSeek(mrStrm, mnNextRecPos) && (mnNextRecPos + 4 <= mnStreamSize);
+ if (bRet)
+ {
+ mrStrm.ReadUInt16( mnRawRecId ).ReadUInt16( mnRawRecSize );
+ bRet = mrStrm.good();
+ }
+ return bRet;
+}
+
+void XclImpStream::SetupDecrypter()
+{
+ if( mxDecrypter )
+ mxDecrypter->Update( mrStrm, mnRawRecSize );
+}
+
+void XclImpStream::SetupRawRecord()
+{
+ // pre: mnRawRecSize contains current raw record size
+ // pre: mrStrm points to start of raw record data
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ mnRawRecLeft = mnRawRecSize;
+ mnCurrRecSize += mnRawRecSize;
+ SetupDecrypter(); // decrypter works on raw record level
+}
+
+void XclImpStream::SetupRecord()
+{
+ mnRecId = mnRawRecId;
+ mnAltContId = EXC_ID_UNKNOWN;
+ mnCurrRecSize = 0;
+ mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !mbCont;
+ SetupRawRecord();
+ SetNulSubstChar();
+ EnableDecryption();
+ StorePosition( maFirstRec );
+}
+
+bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const
+{
+ return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId);
+}
+
+bool XclImpStream::JumpToNextContinue()
+{
+ mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId );
+ if( mbValid ) // do not setup a following non-CONTINUE record
+ SetupRawRecord();
+ return mbValid;
+}
+
+bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit )
+{
+ OSL_ENSURE( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" );
+
+ if( mbCont && (GetRecLeft() > 0) )
+ {
+ JumpToNextContinue();
+ }
+ else if( mnRecId == EXC_ID_CONT )
+ {
+ // CONTINUE handling is off, but we have started reading in a CONTINUE record
+ // -> start next CONTINUE for TXO import
+ mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0));
+ mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT);
+ // we really start a new record here - no chance to return to string origin
+ if( mbValid )
+ SetupRecord();
+ }
+ else
+ mbValid = false;
+
+ if( mbValid )
+ rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT );
+ return mbValid;
+}
+
+bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes )
+{
+ if( mbValid && nBytes )
+ {
+ while( mbValid && !mnRawRecLeft ) JumpToNextContinue();
+ mbValid = mbValid && (nBytes <= mnRawRecLeft);
+ OSL_ENSURE( mbValid, "XclImpStream::EnsureRawReadSize - record overread" );
+ }
+ return mbValid;
+}
+
+sal_uInt16 XclImpStream::GetMaxRawReadSize( std::size_t nBytes ) const
+{
+ return static_cast<sal_uInt16>(o3tl::sanitizing_min<std::size_t>(nBytes, mnRawRecLeft));
+}
+
+sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes )
+{
+ OSL_ENSURE( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" );
+ sal_uInt16 nRet = 0;
+ if( mbUseDecr )
+ nRet = mxDecrypter->Read( mrStrm, pData, nBytes );
+ else
+ nRet = static_cast<sal_uInt16>(mrStrm.ReadBytes(pData, nBytes));
+ mnRawRecLeft = mnRawRecLeft - nRet;
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */