1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "oox/ole/vbainputstream.hxx" 25 #include <osl/diagnose.h> 26 27 namespace oox { 28 namespace ole { 29 30 // ============================================================================ 31 32 namespace { 33 34 const sal_uInt8 VBASTREAM_SIGNATURE = 1; 35 36 const sal_uInt16 VBACHUNK_SIGMASK = 0x7000; 37 const sal_uInt16 VBACHUNK_SIG = 0x3000; 38 const sal_uInt16 VBACHUNK_COMPRESSED = 0x8000; 39 const sal_uInt16 VBACHUNK_LENMASK = 0x0FFF; 40 41 } // namespace 42 43 // ============================================================================ 44 45 VbaInputStream::VbaInputStream( BinaryInputStream& rInStrm ) : 46 BinaryStreamBase( false ), 47 mpInStrm( &rInStrm ), 48 mnChunkPos( 0 ) 49 { 50 maChunk.reserve( 4096 ); 51 52 sal_uInt8 nSig = rInStrm.readuInt8(); 53 OSL_ENSURE( nSig == VBASTREAM_SIGNATURE, "VbaInputStream::VbaInputStream - wrong signature" ); 54 mbEof = mbEof || rInStrm.isEof() || (nSig != VBASTREAM_SIGNATURE); 55 } 56 57 sal_Int64 VbaInputStream::size() const 58 { 59 return -1; 60 } 61 62 sal_Int64 VbaInputStream::tell() const 63 { 64 return -1; 65 } 66 67 void VbaInputStream::seek( sal_Int64 ) 68 { 69 } 70 71 void VbaInputStream::close() 72 { 73 mpInStrm = 0; 74 mbEof = true; 75 } 76 77 sal_Int32 VbaInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize ) 78 { 79 sal_Int32 nRet = 0; 80 if( !mbEof ) 81 { 82 orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) ); 83 if( nBytes > 0 ) 84 { 85 nRet = readMemory( orData.getArray(), nBytes, nAtomSize ); 86 if( nRet < nBytes ) 87 orData.realloc( nRet ); 88 } 89 } 90 return nRet; 91 } 92 93 sal_Int32 VbaInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t /*nAtomSize*/ ) 94 { 95 sal_Int32 nRet = 0; 96 sal_uInt8* opnMem = reinterpret_cast< sal_uInt8* >( opMem ); 97 while( (nBytes > 0) && updateChunk() ) 98 { 99 sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); 100 sal_Int32 nReadBytes = ::std::min( nBytes, nChunkLeft ); 101 memcpy( opnMem, &*(maChunk.begin() + mnChunkPos), nReadBytes ); 102 opnMem += nReadBytes; 103 mnChunkPos += static_cast< size_t >( nReadBytes ); 104 nBytes -= nReadBytes; 105 nRet += nReadBytes; 106 } 107 return nRet; 108 } 109 110 void VbaInputStream::skip( sal_Int32 nBytes, size_t /*nAtomSize*/ ) 111 { 112 while( (nBytes > 0) && updateChunk() ) 113 { 114 sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); 115 sal_Int32 nSkipBytes = ::std::min( nBytes, nChunkLeft ); 116 mnChunkPos += static_cast< size_t >( nSkipBytes ); 117 nBytes -= nSkipBytes; 118 } 119 } 120 121 // private -------------------------------------------------------------------- 122 123 bool VbaInputStream::updateChunk() 124 { 125 if( mbEof || (mnChunkPos < maChunk.size()) ) return !mbEof; 126 127 // try to read next chunk header, this may trigger EOF 128 sal_uInt16 nHeader = mpInStrm->readuInt16(); 129 mbEof = mpInStrm->isEof(); 130 if( mbEof ) return false; 131 132 // check header signature 133 OSL_ENSURE( (nHeader & VBACHUNK_SIGMASK) == VBACHUNK_SIG, "VbaInputStream::updateChunk - invalid chunk signature" ); 134 mbEof = (nHeader & VBACHUNK_SIGMASK) != VBACHUNK_SIG; 135 if( mbEof ) return false; 136 137 // decode length of chunk data and compression flag 138 bool bCompressed = getFlag( nHeader, VBACHUNK_COMPRESSED ); 139 sal_uInt16 nChunkLen = (nHeader & VBACHUNK_LENMASK) + 1; 140 OSL_ENSURE( bCompressed || (nChunkLen == 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" ); 141 if( bCompressed ) 142 { 143 maChunk.clear(); 144 sal_uInt8 nBitCount = 4; 145 sal_uInt16 nChunkPos = 0; 146 while( !mbEof && !mpInStrm->isEof() && (nChunkPos < nChunkLen) ) 147 { 148 sal_uInt8 nTokenFlags = mpInStrm->readuInt8(); 149 ++nChunkPos; 150 for( int nBit = 0; !mbEof && !mpInStrm->isEof() && (nBit < 8) && (nChunkPos < nChunkLen); ++nBit, nTokenFlags >>= 1 ) 151 { 152 if( nTokenFlags & 1 ) 153 { 154 sal_uInt16 nCopyToken = mpInStrm->readuInt16(); 155 nChunkPos = nChunkPos + 2; 156 // update bit count used for offset/length in the token 157 while( static_cast< size_t >( 1 << nBitCount ) < maChunk.size() ) ++nBitCount; 158 // extract length from lower (16-nBitCount) bits, plus 3 159 sal_uInt16 nLength = extractValue< sal_uInt16 >( nCopyToken, 0, 16 - nBitCount ) + 3; 160 // extract offset from high nBitCount bits, plus 1 161 sal_uInt16 nOffset = extractValue< sal_uInt16 >( nCopyToken, 16 - nBitCount, nBitCount ) + 1; 162 mbEof = (nOffset > maChunk.size()) || (maChunk.size() + nLength > 4096); 163 OSL_ENSURE( !mbEof, "VbaInputStream::updateChunk - invalid offset or size in copy token" ); 164 if( !mbEof ) 165 { 166 // append data to buffer 167 maChunk.resize( maChunk.size() + nLength ); 168 sal_uInt8* pnTo = &*(maChunk.end() - nLength); 169 const sal_uInt8* pnEnd = pnTo + nLength; 170 const sal_uInt8* pnFrom = pnTo - nOffset; 171 // offset may be less than length, effectively duplicating source data several times 172 size_t nRunLen = ::std::min< size_t >( nLength, nOffset ); 173 while( pnTo < pnEnd ) 174 { 175 size_t nStepLen = ::std::min< size_t >( nRunLen, pnEnd - pnTo ); 176 memcpy( pnTo, pnFrom, nStepLen ); 177 pnTo += nStepLen; 178 } 179 } 180 } 181 else 182 { 183 maChunk.resize( maChunk.size() + 1 ); 184 *mpInStrm >> maChunk.back(); 185 ++nChunkPos; 186 } 187 } 188 } 189 } 190 else 191 { 192 maChunk.resize( nChunkLen ); 193 mpInStrm->readMemory( &maChunk.front(), nChunkLen ); 194 } 195 196 mnChunkPos = 0; 197 return !mbEof; 198 } 199 200 // ============================================================================ 201 202 } // namespace ole 203 } // namespace oox 204 205