/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "oox/xls/biffinputstream.hxx" #include #include namespace oox { namespace xls { // ============================================================================ using ::rtl::OString; using ::rtl::OStringToOUString; using ::rtl::OUString; using ::rtl::OUStringBuffer; // ============================================================================ namespace prv { BiffInputRecordBuffer::BiffInputRecordBuffer( BinaryInputStream& rInStrm ) : mrInStrm( rInStrm ), mpCurrentData( 0 ), mnHeaderPos( -1 ), mnBodyPos( 0 ), mnBufferBodyPos( 0 ), mnNextHeaderPos( 0 ), mnRecId( BIFF_ID_UNKNOWN ), mnRecSize( 0 ), mnRecPos( 0 ), mbValidHeader( false ) { OSL_ENSURE( mrInStrm.isSeekable(), "BiffInputRecordBuffer::BiffInputRecordBuffer - stream must be seekable" ); mrInStrm.seekToStart(); maOriginalData.reserve( SAL_MAX_UINT16 ); maDecodedData.reserve( SAL_MAX_UINT16 ); enableDecoder( false ); // updates mpCurrentData } void BiffInputRecordBuffer::restartAt( sal_Int64 nPos ) { mnHeaderPos = -1; mnBodyPos = mnBufferBodyPos = 0; mnNextHeaderPos = nPos; mnRecId = BIFF_ID_UNKNOWN; mnRecSize = mnRecPos = 0; mbValidHeader = false; } void BiffInputRecordBuffer::setDecoder( const BiffDecoderRef& rxDecoder ) { mxDecoder = rxDecoder; enableDecoder( true ); updateDecoded(); } void BiffInputRecordBuffer::enableDecoder( bool bEnable ) { mpCurrentData = (bEnable && mxDecoder.get() && mxDecoder->isValid()) ? &maDecodedData : &maOriginalData; } bool BiffInputRecordBuffer::startRecord( sal_Int64 nHeaderPos ) { mbValidHeader = (0 <= nHeaderPos) && (nHeaderPos + 4 <= mrInStrm.size()); if( mbValidHeader ) { mnHeaderPos = nHeaderPos; mrInStrm.seek( nHeaderPos ); mrInStrm >> mnRecId >> mnRecSize; mnBodyPos = mrInStrm.tell(); mnNextHeaderPos = mnBodyPos + mnRecSize; mbValidHeader = !mrInStrm.isEof() && (mnNextHeaderPos <= mrInStrm.size()); } if( !mbValidHeader ) { mnHeaderPos = mnBodyPos = -1; mnNextHeaderPos = 0; mnRecId = BIFF_ID_UNKNOWN; mnRecSize = 0; } mnRecPos = 0; return mbValidHeader; } bool BiffInputRecordBuffer::startNextRecord() { return startRecord( mnNextHeaderPos ); } sal_uInt16 BiffInputRecordBuffer::getNextRecId() { sal_uInt16 nRecId = BIFF_ID_UNKNOWN; if( mbValidHeader && (mnNextHeaderPos + 4 <= mrInStrm.size()) ) { mrInStrm.seek( mnNextHeaderPos ); mrInStrm >> nRecId; } return nRecId; } void BiffInputRecordBuffer::read( void* opData, sal_uInt16 nBytes ) { updateBuffer(); OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::read - nothing to read" ); OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::read - buffer overflow" ); memcpy( opData, &(*mpCurrentData)[ mnRecPos ], nBytes ); mnRecPos = mnRecPos + nBytes; } void BiffInputRecordBuffer::skip( sal_uInt16 nBytes ) { OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::skip - nothing to skip" ); OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::skip - buffer overflow" ); mnRecPos = mnRecPos + nBytes; } void BiffInputRecordBuffer::updateBuffer() { OSL_ENSURE( mbValidHeader, "BiffInputRecordBuffer::updateBuffer - invalid access" ); if( mnBodyPos != mnBufferBodyPos ) { mrInStrm.seek( mnBodyPos ); maOriginalData.resize( mnRecSize ); if( mnRecSize > 0 ) mrInStrm.readMemory( &maOriginalData.front(), static_cast< sal_Int32 >( mnRecSize ) ); mnBufferBodyPos = mnBodyPos; updateDecoded(); } } void BiffInputRecordBuffer::updateDecoded() { if( mxDecoder.get() && mxDecoder->isValid() ) { maDecodedData.resize( mnRecSize ); if( mnRecSize > 0 ) mxDecoder->decode( &maDecodedData.front(), &maOriginalData.front(), mnBodyPos, mnRecSize ); } } } // namespace prv // ============================================================================ BiffInputStream::BiffInputStream( BinaryInputStream& rInStream, bool bContLookup ) : BinaryStreamBase( true ), maRecBuffer( rInStream ), mnRecHandle( -1 ), mnRecId( BIFF_ID_UNKNOWN ), mnAltContId( BIFF_ID_UNKNOWN ), mnCurrRecSize( 0 ), mnComplRecSize( 0 ), mbHasComplRec( false ), mbCont( bContLookup ) { mbEof = true; // EOF will be true if stream is not inside a record } // record control ------------------------------------------------------------- bool BiffInputStream::startNextRecord() { bool bValidRec = false; /* #i4266# ignore zero records (id==len==0) (e.g. the application "Crystal Report" writes zero records between other records) */ bool bIsZeroRec = false; do { // record header is never encrypted maRecBuffer.enableDecoder( false ); // read header of next raw record, returns false at end of stream bValidRec = maRecBuffer.startNextRecord(); // ignore record, if identifier and size are zero bIsZeroRec = (maRecBuffer.getRecId() == 0) && (maRecBuffer.getRecSize() == 0); } while( bValidRec && ((mbCont && isContinueId( maRecBuffer.getRecId() )) || bIsZeroRec) ); // setup other class members setupRecord(); return isInRecord(); } bool BiffInputStream::startRecordByHandle( sal_Int64 nRecHandle ) { rewindToRecord( nRecHandle ); return startNextRecord(); } void BiffInputStream::resetRecord( bool bContLookup, sal_uInt16 nAltContId ) { if( isInRecord() ) { mbCont = bContLookup; mnAltContId = nAltContId; restartRecord( true ); maRecBuffer.enableDecoder( true ); } } void BiffInputStream::rewindRecord() { rewindToRecord( mnRecHandle ); } // decoder -------------------------------------------------------------------- void BiffInputStream::setDecoder( const BiffDecoderRef& rxDecoder ) { maRecBuffer.setDecoder( rxDecoder ); } void BiffInputStream::enableDecoder( bool bEnable ) { maRecBuffer.enableDecoder( bEnable ); } // stream/record state and info ----------------------------------------------- sal_uInt16 BiffInputStream::getNextRecId() { sal_uInt16 nRecId = BIFF_ID_UNKNOWN; if( isInRecord() ) { sal_Int64 nCurrPos = tell(); // save current position in record while( jumpToNextContinue() ) {} // skip following CONTINUE records if( maRecBuffer.startNextRecord() ) // read header of next record nRecId = maRecBuffer.getRecId(); seek( nCurrPos ); // restore position, seek() resets old mbValid state } return nRecId; } // BinaryStreamBase interface (seeking) --------------------------------------- sal_Int64 BiffInputStream::size() const { if( !mbHasComplRec ) const_cast< BiffInputStream* >( this )->calcRecordLength(); return mnComplRecSize; } sal_Int64 BiffInputStream::tell() const { return mbEof ? -1 : (mnCurrRecSize - maRecBuffer.getRecLeft()); } void BiffInputStream::seek( sal_Int64 nRecPos ) { if( isInRecord() ) { if( mbEof || (nRecPos < tell()) ) restartRecord( false ); if( !mbEof && (nRecPos > tell()) ) skip( static_cast< sal_Int32 >( nRecPos - tell() ) ); } } void BiffInputStream::close() { } sal_Int64 BiffInputStream::tellBase() const { return maRecBuffer.getBaseStream().tell(); } sal_Int64 BiffInputStream::sizeBase() const { return maRecBuffer.getBaseStream().size(); } // BinaryInputStream interface (stream read access) --------------------------- sal_Int32 BiffInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize ) { sal_Int32 nRet = 0; if( !mbEof ) { orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) ); if( nBytes > 0 ) nRet = readMemory( orData.getArray(), nBytes, nAtomSize ); } return nRet; } sal_Int32 BiffInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t nAtomSize ) { sal_Int32 nRet = 0; if( !mbEof && opMem && (nBytes > 0) ) { sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( opMem ); sal_Int32 nBytesLeft = nBytes; while( !mbEof && (nBytesLeft > 0) ) { sal_uInt16 nReadSize = getMaxRawReadSize( nBytesLeft, nAtomSize ); // check nReadSize, stream may already be located at end of a raw record if( nReadSize > 0 ) { maRecBuffer.read( pnBuffer, nReadSize ); nRet += nReadSize; pnBuffer += nReadSize; nBytesLeft -= nReadSize; } if( nBytesLeft > 0 ) jumpToNextContinue(); OSL_ENSURE( !mbEof, "BiffInputStream::readMemory - record overread" ); } } return nRet; } void BiffInputStream::skip( sal_Int32 nBytes, size_t nAtomSize ) { sal_Int32 nBytesLeft = nBytes; while( !mbEof && (nBytesLeft > 0) ) { sal_uInt16 nSkipSize = getMaxRawReadSize( nBytesLeft, nAtomSize ); // check nSkipSize, stream may already be located at end of a raw record if( nSkipSize > 0 ) { maRecBuffer.skip( nSkipSize ); nBytesLeft -= nSkipSize; } if( nBytesLeft > 0 ) jumpToNextContinue(); OSL_ENSURE( !mbEof, "BiffInputStream::skip - record overread" ); } } // byte strings --------------------------------------------------------------- OString BiffInputStream::readByteString( bool b16BitLen, bool bAllowNulChars ) { sal_Int32 nStrLen = b16BitLen ? readuInt16() : readuInt8(); return readCharArray( nStrLen, bAllowNulChars ); } OUString BiffInputStream::readByteStringUC( bool b16BitLen, rtl_TextEncoding eTextEnc, bool bAllowNulChars ) { return OStringToOUString( readByteString( b16BitLen, bAllowNulChars ), eTextEnc ); } void BiffInputStream::skipByteString( bool b16BitLen ) { skip( b16BitLen ? readuInt16() : readuInt8() ); } // Unicode strings ------------------------------------------------------------ OUString BiffInputStream::readUniStringChars( sal_uInt16 nChars, bool b16BitChars, bool bAllowNulChars ) { OUStringBuffer aBuffer; aBuffer.ensureCapacity( nChars ); /* This function has to react on CONTINUE records which repeat the flags field in their first byte and may change the 8bit/16bit character mode, thus a plain call to readCompressedUnicodeArray() cannot be used here. */ sal_Int32 nCharsLeft = nChars; while( !mbEof && (nCharsLeft > 0) ) { /* Read the character array from the remaining part of the current raw record. First, calculate the maximum number of characters that can be read without triggering to start a following CONTINUE record. */ sal_Int32 nRawChars = b16BitChars ? (getMaxRawReadSize( nCharsLeft * 2, 2 ) / 2) : getMaxRawReadSize( nCharsLeft, 1 ); aBuffer.append( readCompressedUnicodeArray( nRawChars, !b16BitChars, bAllowNulChars ) ); /* Prepare for next CONTINUE record. Calling jumpToNextStringContinue() reads the leading byte in the following CONTINUE record and updates the b16BitChars flag. */ nCharsLeft -= nRawChars; if( nCharsLeft > 0 ) jumpToNextStringContinue( b16BitChars ); } return aBuffer.makeStringAndClear(); } OUString BiffInputStream::readUniStringBody( sal_uInt16 nChars, bool bAllowNulChars ) { bool b16BitChars; sal_Int32 nAddSize; readUniStringHeader( b16BitChars, nAddSize ); OUString aString = readUniStringChars( nChars, b16BitChars, bAllowNulChars ); skip( nAddSize ); return aString; } OUString BiffInputStream::readUniString( bool bAllowNulChars ) { return readUniStringBody( readuInt16(), bAllowNulChars ); } void BiffInputStream::skipUniStringChars( sal_uInt16 nChars, bool b16BitChars ) { sal_Int32 nCharsLeft = nChars; while( !mbEof && (nCharsLeft > 0) ) { // skip the character array sal_Int32 nSkipSize = b16BitChars ? getMaxRawReadSize( 2 * nCharsLeft, 2 ) : getMaxRawReadSize( nCharsLeft, 1 ); skip( nSkipSize ); // prepare for next CONTINUE record nCharsLeft -= (b16BitChars ? (nSkipSize / 2) : nSkipSize); if( nCharsLeft > 0 ) jumpToNextStringContinue( b16BitChars ); } } void BiffInputStream::skipUniStringBody( sal_uInt16 nChars ) { bool b16BitChars; sal_Int32 nAddSize; readUniStringHeader( b16BitChars, nAddSize ); skipUniStringChars( nChars, b16BitChars ); skip( nAddSize ); } void BiffInputStream::skipUniString() { skipUniStringBody( readuInt16() ); } // private -------------------------------------------------------------------- void BiffInputStream::setupRecord() { // initialize class members mnRecHandle = maRecBuffer.getRecHeaderPos(); mnRecId = maRecBuffer.getRecId(); mnAltContId = BIFF_ID_UNKNOWN; mnCurrRecSize = mnComplRecSize = maRecBuffer.getRecSize(); mbHasComplRec = !mbCont; mbEof = !isInRecord(); // enable decoder in new record enableDecoder( true ); } void BiffInputStream::restartRecord( bool bInvalidateRecSize ) { if( isInRecord() ) { maRecBuffer.startRecord( getRecHandle() ); mnCurrRecSize = maRecBuffer.getRecSize(); if( bInvalidateRecSize ) { mnComplRecSize = mnCurrRecSize; mbHasComplRec = !mbCont; } mbEof = false; } } void BiffInputStream::rewindToRecord( sal_Int64 nRecHandle ) { if( nRecHandle >= 0 ) { maRecBuffer.restartAt( nRecHandle ); mnRecHandle = -1; mbEof = true; // as long as the record is not started } } bool BiffInputStream::isContinueId( sal_uInt16 nRecId ) const { return (nRecId == BIFF_ID_CONT) || (nRecId == mnAltContId); } bool BiffInputStream::jumpToNextContinue() { mbEof = mbEof || !mbCont || !isContinueId( maRecBuffer.getNextRecId() ) || !maRecBuffer.startNextRecord(); if( !mbEof ) mnCurrRecSize += maRecBuffer.getRecSize(); return !mbEof; } bool BiffInputStream::jumpToNextStringContinue( bool& rb16BitChars ) { OSL_ENSURE( maRecBuffer.getRecLeft() == 0, "BiffInputStream::jumpToNextStringContinue - alignment error" ); if( mbCont && (getRemaining() > 0) ) { jumpToNextContinue(); } else if( mnRecId == BIFF_ID_CONT ) { /* CONTINUE handling is off, but we have started reading in a CONTINUE record -> start next CONTINUE for TXO import. We really start a new record here - no chance to return to string origin. */ mbEof = mbEof || (maRecBuffer.getNextRecId() != BIFF_ID_CONT) || !maRecBuffer.startNextRecord(); if( !mbEof ) setupRecord(); } // trying to read the flags invalidates stream, if no CONTINUE record has been found sal_uInt8 nFlags; readValue( nFlags ); rb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT ); return !mbEof; } void BiffInputStream::calcRecordLength() { sal_Int64 nCurrPos = tell(); // save current position in record while( jumpToNextContinue() ) {} // jumpToNextContinue() adds up mnCurrRecSize mnComplRecSize = mnCurrRecSize; mbHasComplRec = true; seek( nCurrPos ); // restore position, seek() resets old mbValid state } sal_uInt16 BiffInputStream::getMaxRawReadSize( sal_Int32 nBytes, size_t nAtomSize ) const { sal_uInt16 nMaxSize = getLimitedValue< sal_uInt16, sal_Int32 >( nBytes, 0, maRecBuffer.getRecLeft() ); if( (0 < nMaxSize) && (nMaxSize < nBytes) && (nAtomSize > 1) ) { // check that remaining data in record buffer is a multiple of the passed atom size sal_uInt16 nPadding = static_cast< sal_uInt16 >( nMaxSize % nAtomSize ); OSL_ENSURE( nPadding == 0, "BiffInputStream::getMaxRawReadSize - alignment error" ); nMaxSize = nMaxSize - nPadding; } return nMaxSize; } void BiffInputStream::readUniStringHeader( bool& orb16BitChars, sal_Int32& ornAddSize ) { sal_uInt8 nFlags = readuInt8(); OSL_ENSURE( !getFlag( nFlags, BIFF_STRF_UNKNOWN ), "BiffInputStream::readUniStringHeader - unknown flags" ); orb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT ); sal_uInt16 nFontCount = getFlag( nFlags, BIFF_STRF_RICH ) ? readuInt16() : 0; sal_Int32 nPhoneticSize = getFlag( nFlags, BIFF_STRF_PHONETIC ) ? readInt32() : 0; ornAddSize = 4 * nFontCount + ::std::max< sal_Int32 >( 0, nPhoneticSize ); } // ============================================================================ BiffInputStreamPos::BiffInputStreamPos( BiffInputStream& rStrm ) : mrStrm( rStrm ), mnRecHandle( rStrm.getRecHandle() ), mnRecPos( rStrm.tell() ) { } bool BiffInputStreamPos::restorePosition() { bool bValidRec = mrStrm.startRecordByHandle( mnRecHandle ); if( bValidRec ) mrStrm.seek( mnRecPos ); return bValidRec && !mrStrm.isEof(); } // ============================================================================ BiffInputStreamPosGuard::BiffInputStreamPosGuard( BiffInputStream& rStrm ) : BiffInputStreamPos( rStrm ) { } BiffInputStreamPosGuard::~BiffInputStreamPosGuard() { restorePosition(); } // ============================================================================ } // namespace xls } // namespace oox