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
VbaInputStream(BinaryInputStream & rInStrm)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
size() const57 sal_Int64 VbaInputStream::size() const
58 {
59 return -1;
60 }
61
tell() const62 sal_Int64 VbaInputStream::tell() const
63 {
64 return -1;
65 }
66
seek(sal_Int64)67 void VbaInputStream::seek( sal_Int64 )
68 {
69 }
70
close()71 void VbaInputStream::close()
72 {
73 mpInStrm = 0;
74 mbEof = true;
75 }
76
readData(StreamDataSequence & orData,sal_Int32 nBytes,size_t nAtomSize)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
readMemory(void * opMem,sal_Int32 nBytes,size_t)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
skip(sal_Int32 nBytes,size_t)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
updateChunk()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