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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26
27 #include <comphelper/docpasswordhelper.hxx>
28 #include <comphelper/sequenceashashmap.hxx>
29
30 #include "xistream.hxx"
31 #include "xlstring.hxx"
32 #include "xiroot.hxx"
33
34 #include <vector>
35
36 using ::rtl::OString;
37 using ::rtl::OUString;
38 using ::rtl::OUStringToOString;
39
40 using namespace ::com::sun::star;
41
42 // ============================================================================
43 // Decryption
44 // ============================================================================
45
XclImpDecrypter()46 XclImpDecrypter::XclImpDecrypter() :
47 mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ),
48 mnOldPos( STREAM_SEEK_TO_END ),
49 mnRecSize( 0 )
50 {
51 }
52
XclImpDecrypter(const XclImpDecrypter & rSrc)53 XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) :
54 ::comphelper::IDocPasswordVerifier(),
55 mnError( rSrc.mnError ),
56 mnOldPos( STREAM_SEEK_TO_END ),
57 mnRecSize( 0 )
58 {
59 }
60
~XclImpDecrypter()61 XclImpDecrypter::~XclImpDecrypter()
62 {
63 }
64
Clone() const65 XclImpDecrypterRef XclImpDecrypter::Clone() const
66 {
67 XclImpDecrypterRef xNewDecr;
68 if( IsValid() )
69 xNewDecr.reset( OnClone() );
70 return xNewDecr;
71 }
72
verifyPassword(const::rtl::OUString & rPassword,uno::Sequence<beans::NamedValue> & o_rEncryptionData)73 ::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const ::rtl::OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
74 {
75 o_rEncryptionData = OnVerifyPassword( rPassword );
76 mnError = o_rEncryptionData.getLength() ? ERRCODE_NONE : ERRCODE_ABORT;
77 return o_rEncryptionData.getLength() ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
78 }
79
verifyEncryptionData(const uno::Sequence<beans::NamedValue> & rEncryptionData)80 ::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
81 {
82 bool bValid = OnVerifyEncryptionData( rEncryptionData );
83 mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT;
84 return bValid ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
85 }
86
Update(SvStream & rStrm,sal_uInt16 nRecSize)87 void XclImpDecrypter::Update( SvStream& rStrm, sal_uInt16 nRecSize )
88 {
89 if( IsValid() )
90 {
91 sal_Size nNewPos = rStrm.Tell();
92 if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) )
93 {
94 OnUpdate( mnOldPos, nNewPos, nRecSize );
95 mnOldPos = nNewPos;
96 mnRecSize = nRecSize;
97 }
98 }
99 }
100
Read(SvStream & rStrm,void * pData,sal_uInt16 nBytes)101 sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes )
102 {
103 sal_uInt16 nRet = 0;
104 if( pData && nBytes )
105 {
106 if( IsValid() )
107 {
108 Update( rStrm, mnRecSize );
109 nRet = OnRead( rStrm, reinterpret_cast< sal_uInt8* >( pData ), nBytes );
110 mnOldPos = rStrm.Tell();
111 }
112 else
113 nRet = static_cast< sal_uInt16 >( rStrm.Read( pData, nBytes ) );
114 }
115 return nRet;
116 }
117
118 // ----------------------------------------------------------------------------
119
XclImpBiff5Decrypter(sal_uInt16 nKey,sal_uInt16 nHash)120 XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) :
121 mnKey( nKey ),
122 mnHash( nHash )
123 {
124 }
125
XclImpBiff5Decrypter(const XclImpBiff5Decrypter & rSrc)126 XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) :
127 XclImpDecrypter( rSrc ),
128 maEncryptionData( rSrc.maEncryptionData ),
129 mnKey( rSrc.mnKey ),
130 mnHash( rSrc.mnHash )
131 {
132 if( IsValid() )
133 maCodec.InitCodec( maEncryptionData );
134 }
135
OnClone() const136 XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const
137 {
138 return new XclImpBiff5Decrypter( *this );
139 }
140
OnVerifyPassword(const::rtl::OUString & rPassword)141 uno::Sequence< beans::NamedValue > XclImpBiff5Decrypter::OnVerifyPassword( const ::rtl::OUString& rPassword )
142 {
143 maEncryptionData.realloc( 0 );
144
145 /* Convert password to a byte string. TODO: this needs some finetuning
146 according to the spec... */
147 OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() );
148 sal_Int32 nLen = aBytePassword.getLength();
149 if( (0 < nLen) && (nLen < 16) )
150 {
151 // init codec
152 maCodec.InitKey( (sal_uInt8*)aBytePassword.getStr() );
153
154 if ( maCodec.VerifyKey( mnKey, mnHash ) )
155 {
156 maEncryptionData = maCodec.GetEncryptionData();
157
158 // since the export uses Std97 encryption always we have to request it here
159 ::std::vector< sal_uInt16 > aPassVect( 16 );
160 ::std::vector< sal_uInt16 >::iterator aIt = aPassVect.begin();
161 for( sal_Int32 nInd = 0; nInd < nLen; ++nInd, ++aIt )
162 *aIt = static_cast< sal_uInt16 >( rPassword.getStr()[nInd] );
163
164 uno::Sequence< sal_Int8 > aDocId = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
165 OSL_ENSURE( aDocId.getLength() == 16, "Unexpected length of the senquence!" );
166
167 ::msfilter::MSCodec_Std97 aCodec97;
168 aCodec97.InitKey( &aPassVect.front(), (sal_uInt8*)aDocId.getConstArray() );
169
170 // merge the EncryptionData, there should be no conflicts
171 ::comphelper::SequenceAsHashMap aEncryptionHash( maEncryptionData );
172 aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
173 aEncryptionHash >> maEncryptionData;
174 }
175 }
176
177 return maEncryptionData;
178 }
179
OnVerifyEncryptionData(const uno::Sequence<beans::NamedValue> & rEncryptionData)180 bool XclImpBiff5Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
181 {
182 maEncryptionData.realloc( 0 );
183
184 if( rEncryptionData.getLength() )
185 {
186 // init codec
187 maCodec.InitCodec( rEncryptionData );
188
189 if ( maCodec.VerifyKey( mnKey, mnHash ) )
190 maEncryptionData = rEncryptionData;
191 }
192
193 return maEncryptionData.getLength();
194 }
195
OnUpdate(sal_Size,sal_Size nNewStrmPos,sal_uInt16 nRecSize)196 void XclImpBiff5Decrypter::OnUpdate( sal_Size /*nOldStrmPos*/, sal_Size nNewStrmPos, sal_uInt16 nRecSize )
197 {
198 maCodec.InitCipher();
199 maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F );
200 }
201
OnRead(SvStream & rStrm,sal_uInt8 * pnData,sal_uInt16 nBytes)202 sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
203 {
204 sal_uInt16 nRet = static_cast< sal_uInt16 >( rStrm.Read( pnData, nBytes ) );
205 maCodec.Decode( pnData, nRet );
206 return nRet;
207 }
208
209 // ----------------------------------------------------------------------------
210
XclImpBiff8Decrypter(sal_uInt8 pnSalt[16],sal_uInt8 pnVerifier[16],sal_uInt8 pnVerifierHash[16])211 XclImpBiff8Decrypter::XclImpBiff8Decrypter( sal_uInt8 pnSalt[ 16 ],
212 sal_uInt8 pnVerifier[ 16 ], sal_uInt8 pnVerifierHash[ 16 ] ) :
213 maSalt( pnSalt, pnSalt + 16 ),
214 maVerifier( pnVerifier, pnVerifier + 16 ),
215 maVerifierHash( pnVerifierHash, pnVerifierHash + 16 )
216 {
217 }
218
XclImpBiff8Decrypter(const XclImpBiff8Decrypter & rSrc)219 XclImpBiff8Decrypter::XclImpBiff8Decrypter( const XclImpBiff8Decrypter& rSrc ) :
220 XclImpDecrypter( rSrc ),
221 maEncryptionData( rSrc.maEncryptionData ),
222 maSalt( rSrc.maSalt ),
223 maVerifier( rSrc.maVerifier ),
224 maVerifierHash( rSrc.maVerifierHash )
225 {
226 if( IsValid() )
227 maCodec.InitCodec( maEncryptionData );
228 }
229
OnClone() const230 XclImpBiff8Decrypter* XclImpBiff8Decrypter::OnClone() const
231 {
232 return new XclImpBiff8Decrypter( *this );
233 }
234
OnVerifyPassword(const::rtl::OUString & rPassword)235 uno::Sequence< beans::NamedValue > XclImpBiff8Decrypter::OnVerifyPassword( const ::rtl::OUString& rPassword )
236 {
237 maEncryptionData.realloc( 0 );
238
239 sal_Int32 nLen = rPassword.getLength();
240 if( (0 < nLen) && (nLen < 16) )
241 {
242 // copy string to sal_uInt16 array
243 ::std::vector< sal_uInt16 > aPassVect( 16 );
244 const sal_Unicode* pcChar = rPassword.getStr();
245 const sal_Unicode* pcCharEnd = pcChar + nLen;
246 ::std::vector< sal_uInt16 >::iterator aIt = aPassVect.begin();
247 for( ; pcChar < pcCharEnd; ++pcChar, ++aIt )
248 *aIt = static_cast< sal_uInt16 >( *pcChar );
249
250 // init codec
251 maCodec.InitKey( &aPassVect.front(), &maSalt.front() );
252 if ( maCodec.VerifyKey( &maVerifier.front(), &maVerifierHash.front() ) )
253 maEncryptionData = maCodec.GetEncryptionData();
254 }
255
256 return maEncryptionData;
257 }
258
OnVerifyEncryptionData(const uno::Sequence<beans::NamedValue> & rEncryptionData)259 bool XclImpBiff8Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
260 {
261 maEncryptionData.realloc( 0 );
262
263 if( rEncryptionData.getLength() )
264 {
265 // init codec
266 maCodec.InitCodec( rEncryptionData );
267
268 if ( maCodec.VerifyKey( &maVerifier.front(), &maVerifierHash.front() ) )
269 maEncryptionData = rEncryptionData;
270 }
271
272 return maEncryptionData.getLength();
273 }
274
OnUpdate(sal_Size nOldStrmPos,sal_Size nNewStrmPos,sal_uInt16)275 void XclImpBiff8Decrypter::OnUpdate( sal_Size nOldStrmPos, sal_Size nNewStrmPos, sal_uInt16 /*nRecSize*/ )
276 {
277 if( nNewStrmPos != nOldStrmPos )
278 {
279 sal_uInt32 nOldBlock = GetBlock( nOldStrmPos );
280 sal_uInt16 nOldOffset = GetOffset( nOldStrmPos );
281
282 sal_uInt32 nNewBlock = GetBlock( nNewStrmPos );
283 sal_uInt16 nNewOffset = GetOffset( nNewStrmPos );
284
285 /* Rekey cipher, if block changed or if previous offset in same block. */
286 if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) )
287 {
288 maCodec.InitCipher( nNewBlock );
289 nOldOffset = 0; // reset nOldOffset for next if() statement
290 }
291
292 /* Seek to correct offset. */
293 if( nNewOffset > nOldOffset )
294 maCodec.Skip( nNewOffset - nOldOffset );
295 }
296 }
297
OnRead(SvStream & rStrm,sal_uInt8 * pnData,sal_uInt16 nBytes)298 sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
299 {
300 sal_uInt16 nRet = 0;
301
302 sal_uInt8* pnCurrData = pnData;
303 sal_uInt16 nBytesLeft = nBytes;
304 while( nBytesLeft )
305 {
306 sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() );
307 sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft );
308
309 // read the block from stream
310 nRet = nRet + static_cast< sal_uInt16 >( rStrm.Read( pnCurrData, nDecBytes ) );
311 // decode the block inplace
312 maCodec.Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes );
313 if( GetOffset( rStrm.Tell() ) == 0 )
314 maCodec.InitCipher( GetBlock( rStrm.Tell() ) );
315
316 pnCurrData += nDecBytes;
317 nBytesLeft = nBytesLeft - nDecBytes;
318 }
319
320 return nRet;
321 }
322
GetBlock(sal_Size nStrmPos) const323 sal_uInt32 XclImpBiff8Decrypter::GetBlock( sal_Size nStrmPos ) const
324 {
325 return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
326 }
327
GetOffset(sal_Size nStrmPos) const328 sal_uInt16 XclImpBiff8Decrypter::GetOffset( sal_Size nStrmPos ) const
329 {
330 return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
331 }
332
333 // ============================================================================
334 // Stream
335 // ============================================================================
336
XclImpStreamPos()337 XclImpStreamPos::XclImpStreamPos() :
338 mnPos( STREAM_SEEK_TO_BEGIN ),
339 mnNextPos( STREAM_SEEK_TO_BEGIN ),
340 mnCurrSize( 0 ),
341 mnRawRecId( EXC_ID_UNKNOWN ),
342 mnRawRecSize( 0 ),
343 mnRawRecLeft( 0 ),
344 mbValid( false )
345 {
346 }
347
Set(const SvStream & rStrm,sal_Size nNextPos,sal_Size nCurrSize,sal_uInt16 nRawRecId,sal_uInt16 nRawRecSize,sal_uInt16 nRawRecLeft,bool bValid)348 void XclImpStreamPos::Set(
349 const SvStream& rStrm, sal_Size nNextPos, sal_Size nCurrSize,
350 sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
351 bool bValid )
352 {
353 mnPos = rStrm.Tell();
354 mnNextPos = nNextPos;
355 mnCurrSize = nCurrSize;
356 mnRawRecId = nRawRecId;
357 mnRawRecSize = nRawRecSize;
358 mnRawRecLeft = nRawRecLeft;
359 mbValid = bValid;
360 }
361
Get(SvStream & rStrm,sal_Size & rnNextPos,sal_Size & rnCurrSize,sal_uInt16 & rnRawRecId,sal_uInt16 & rnRawRecSize,sal_uInt16 & rnRawRecLeft,bool & rbValid) const362 void XclImpStreamPos::Get(
363 SvStream& rStrm, sal_Size& rnNextPos, sal_Size& rnCurrSize,
364 sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
365 bool& rbValid ) const
366 {
367 rStrm.Seek( mnPos );
368 rnNextPos = mnNextPos;
369 rnCurrSize = mnCurrSize;
370 rnRawRecId = mnRawRecId;
371 rnRawRecSize = mnRawRecSize;
372 rnRawRecLeft = mnRawRecLeft;
373 rbValid = mbValid;
374 }
375
376 // ============================================================================
377
DetectBiffVersion(SvStream & rStrm)378 XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm )
379 {
380 XclBiff eBiff = EXC_BIFF_UNKNOWN;
381
382 rStrm.Seek( STREAM_SEEK_TO_BEGIN );
383 sal_uInt16 nBofId, nBofSize;
384 rStrm >> nBofId >> nBofSize;
385
386 if( (4 <= nBofSize) && (nBofSize <= 16) ) switch( nBofId )
387 {
388 case EXC_ID2_BOF:
389 eBiff = EXC_BIFF2;
390 break;
391 case EXC_ID3_BOF:
392 eBiff = EXC_BIFF3;
393 break;
394 case EXC_ID4_BOF:
395 eBiff = EXC_BIFF4;
396 break;
397 case EXC_ID5_BOF:
398 {
399 sal_uInt16 nVersion;
400 rStrm >> nVersion;
401 // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
402 switch( nVersion & 0xFF00 )
403 {
404 case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752#
405 case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break;
406 case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break;
407 case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break;
408 case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break;
409 case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break;
410 default: DBG_ERROR1( "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x%04hX", nVersion );
411 }
412 }
413 break;
414 }
415 return eBiff;
416 }
417
XclImpStream(SvStream & rInStrm,const XclImpRoot & rRoot,bool bContLookup)418 XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot, bool bContLookup ) :
419 mrStrm( rInStrm ),
420 mrRoot( rRoot ),
421 mnGlobRecId( EXC_ID_UNKNOWN ),
422 mbGlobValidRec( false ),
423 mbHasGlobPos( false ),
424 mnNextRecPos( STREAM_SEEK_TO_BEGIN ),
425 mnCurrRecSize( 0 ),
426 mnComplRecSize( 0 ),
427 mbHasComplRec( false ),
428 mnRecId( EXC_ID_UNKNOWN ),
429 mnAltContId( EXC_ID_UNKNOWN ),
430 mnRawRecId( EXC_ID_UNKNOWN ),
431 mnRawRecSize( 0 ),
432 mnRawRecLeft( 0 ),
433 mcNulSubst( '?' ),
434 mbCont( bContLookup ),
435 mbUseDecr( false ),
436 mbValidRec( false ),
437 mbValid( false )
438 {
439 mrStrm.Seek( STREAM_SEEK_TO_END );
440 mnStreamSize = mrStrm.Tell();
441 mrStrm.Seek( STREAM_SEEK_TO_BEGIN );
442 DBG_ASSERT( mnStreamSize < STREAM_SEEK_TO_END, "XclImpStream::XclImpStream - stream error" );
443 }
444
~XclImpStream()445 XclImpStream::~XclImpStream()
446 {
447 }
448
StartNextRecord()449 bool XclImpStream::StartNextRecord()
450 {
451 maPosStack.clear();
452
453 /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application
454 "Crystal Report" writes zero records between other records) */
455 sal_Size nZeroRecCount = 5;
456 bool bIsZeroRec = false;
457
458 do
459 {
460 mbValidRec = ReadNextRawRecHeader();
461 bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0);
462 if( bIsZeroRec ) --nZeroRecCount;
463 mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
464 }
465 while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) );
466
467 mbValidRec = mbValidRec && !bIsZeroRec;
468 mbValid = mbValidRec;
469 SetupRecord();
470
471 return mbValidRec;
472 }
473
StartNextRecord(sal_Size nNextRecPos)474 bool XclImpStream::StartNextRecord( sal_Size nNextRecPos )
475 {
476 mnNextRecPos = nNextRecPos;
477 return StartNextRecord();
478 }
479
ResetRecord(bool bContLookup,sal_uInt16 nAltContId)480 void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId )
481 {
482 if( mbValidRec )
483 {
484 maPosStack.clear();
485 RestorePosition( maFirstRec );
486 mnCurrRecSize = mnComplRecSize = mnRawRecSize;
487 mbHasComplRec = !bContLookup;
488 mbCont = bContLookup;
489 mnAltContId = nAltContId;
490 EnableDecryption();
491 }
492 }
493
RewindRecord()494 void XclImpStream::RewindRecord()
495 {
496 mnNextRecPos = maFirstRec.GetPos();
497 mbValid = mbValidRec = false;
498 }
499
SetDecrypter(XclImpDecrypterRef xDecrypter)500 void XclImpStream::SetDecrypter( XclImpDecrypterRef xDecrypter )
501 {
502 mxDecrypter = xDecrypter;
503 EnableDecryption();
504 SetupDecrypter();
505 }
506
CopyDecrypterFrom(const XclImpStream & rStrm)507 void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm )
508 {
509 XclImpDecrypterRef xNewDecr;
510 if( rStrm.mxDecrypter.is() )
511 xNewDecr = rStrm.mxDecrypter->Clone();
512 SetDecrypter( xNewDecr );
513 }
514
HasValidDecrypter() const515 bool XclImpStream::HasValidDecrypter() const
516 {
517 return mxDecrypter.is() && mxDecrypter->IsValid();
518 }
519
EnableDecryption(bool bEnable)520 void XclImpStream::EnableDecryption( bool bEnable )
521 {
522 mbUseDecr = bEnable && HasValidDecrypter();
523 }
524
525 // ----------------------------------------------------------------------------
526
PushPosition()527 void XclImpStream::PushPosition()
528 {
529 maPosStack.push_back( XclImpStreamPos() );
530 StorePosition( maPosStack.back() );
531 }
532
PopPosition()533 void XclImpStream::PopPosition()
534 {
535 DBG_ASSERT( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" );
536 if( !maPosStack.empty() )
537 {
538 RestorePosition( maPosStack.back() );
539 maPosStack.pop_back();
540 }
541 }
542
543 //UNUSED2008-05 void XclImpStream::RejectPosition()
544 //UNUSED2008-05 {
545 //UNUSED2008-05 DBG_ASSERT( !maPosStack.empty(), "XclImpStream::RejectPosition - stack empty" );
546 //UNUSED2008-05 if( !maPosStack.empty() )
547 //UNUSED2008-05 maPosStack.pop_back();
548 //UNUSED2008-05 }
549
StoreGlobalPosition()550 void XclImpStream::StoreGlobalPosition()
551 {
552 StorePosition( maGlobPos );
553 mnGlobRecId = mnRecId;
554 mbGlobValidRec = mbValidRec;
555 mbHasGlobPos = true;
556 }
557
SeekGlobalPosition()558 void XclImpStream::SeekGlobalPosition()
559 {
560 DBG_ASSERT( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" );
561 if( mbHasGlobPos )
562 {
563 RestorePosition( maGlobPos );
564 mnRecId = mnGlobRecId;
565 mnComplRecSize = mnCurrRecSize;
566 mbHasComplRec = !mbCont;
567 mbValidRec = mbGlobValidRec;
568 }
569 }
570
GetRecPos() const571 sal_Size XclImpStream::GetRecPos() const
572 {
573 return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END;
574 }
575
GetRecSize()576 sal_Size XclImpStream::GetRecSize()
577 {
578 if( !mbHasComplRec )
579 {
580 PushPosition();
581 while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize
582 mnComplRecSize = mnCurrRecSize;
583 mbHasComplRec = true;
584 PopPosition();
585 }
586 return mnComplRecSize;
587 }
588
GetRecLeft()589 sal_Size XclImpStream::GetRecLeft()
590 {
591 return mbValid ? (GetRecSize() - GetRecPos()) : 0;
592 }
593
GetNextRecId()594 sal_uInt16 XclImpStream::GetNextRecId()
595 {
596 sal_uInt16 nRecId = EXC_ID_UNKNOWN;
597 if( mbValidRec )
598 {
599 PushPosition();
600 while( JumpToNextContinue() ) ; // skip following CONTINUE records
601 if( mnNextRecPos < mnStreamSize )
602 {
603 mrStrm.Seek( mnNextRecPos );
604 mrStrm >> nRecId;
605 }
606 PopPosition();
607 }
608 return nRecId;
609 }
610
611 // ----------------------------------------------------------------------------
612
operator >>(sal_Int8 & rnValue)613 XclImpStream& XclImpStream::operator>>( sal_Int8& rnValue )
614 {
615 if( EnsureRawReadSize( 1 ) )
616 {
617 if( mbUseDecr )
618 mxDecrypter->Read( mrStrm, &rnValue, 1 );
619 else
620 mrStrm >> rnValue;
621 --mnRawRecLeft;
622 }
623 return *this;
624 }
625
operator >>(sal_uInt8 & rnValue)626 XclImpStream& XclImpStream::operator>>( sal_uInt8& rnValue )
627 {
628 if( EnsureRawReadSize( 1 ) )
629 {
630 if( mbUseDecr )
631 mxDecrypter->Read( mrStrm, &rnValue, 1 );
632 else
633 mrStrm >> rnValue;
634 --mnRawRecLeft;
635 }
636 return *this;
637 }
638
operator >>(sal_Int16 & rnValue)639 XclImpStream& XclImpStream::operator>>( sal_Int16& rnValue )
640 {
641 if( EnsureRawReadSize( 2 ) )
642 {
643 if( mbUseDecr )
644 {
645 SVBT16 pnBuffer;
646 mxDecrypter->Read( mrStrm, pnBuffer, 2 );
647 rnValue = static_cast< sal_Int16 >( SVBT16ToShort( pnBuffer ) );
648 }
649 else
650 mrStrm >> rnValue;
651 mnRawRecLeft -= 2;
652 }
653 return *this;
654 }
655
operator >>(sal_uInt16 & rnValue)656 XclImpStream& XclImpStream::operator>>( sal_uInt16& rnValue )
657 {
658 if( EnsureRawReadSize( 2 ) )
659 {
660 if( mbUseDecr )
661 {
662 SVBT16 pnBuffer;
663 mxDecrypter->Read( mrStrm, pnBuffer, 2 );
664 rnValue = SVBT16ToShort( pnBuffer );
665 }
666 else
667 mrStrm >> rnValue;
668 mnRawRecLeft -= 2;
669 }
670 return *this;
671 }
672
operator >>(sal_Int32 & rnValue)673 XclImpStream& XclImpStream::operator>>( sal_Int32& rnValue )
674 {
675 if( EnsureRawReadSize( 4 ) )
676 {
677 if( mbUseDecr )
678 {
679 SVBT32 pnBuffer;
680 mxDecrypter->Read( mrStrm, pnBuffer, 4 );
681 rnValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) );
682 }
683 else
684 mrStrm >> rnValue;
685 mnRawRecLeft -= 4;
686 }
687 return *this;
688 }
689
operator >>(sal_uInt32 & rnValue)690 XclImpStream& XclImpStream::operator>>( sal_uInt32& rnValue )
691 {
692 if( EnsureRawReadSize( 4 ) )
693 {
694 if( mbUseDecr )
695 {
696 SVBT32 pnBuffer;
697 mxDecrypter->Read( mrStrm, pnBuffer, 4 );
698 rnValue = SVBT32ToUInt32( pnBuffer );
699 }
700 else
701 mrStrm >> rnValue;
702 mnRawRecLeft -= 4;
703 }
704 return *this;
705 }
706
operator >>(float & rfValue)707 XclImpStream& XclImpStream::operator>>( float& rfValue )
708 {
709 if( EnsureRawReadSize( 4 ) )
710 {
711 if( mbUseDecr )
712 {
713 SVBT32 pnBuffer;
714 mxDecrypter->Read( mrStrm, pnBuffer, 4 );
715 sal_uInt32 nValue = SVBT32ToUInt32( pnBuffer );
716 memcpy( &rfValue, &nValue, 4 );
717 }
718 else
719 mrStrm >> rfValue;
720 mnRawRecLeft -= 4;
721 }
722 return *this;
723 }
724
operator >>(double & rfValue)725 XclImpStream& XclImpStream::operator>>( double& rfValue )
726 {
727 if( EnsureRawReadSize( 8 ) )
728 {
729 if( mbUseDecr )
730 {
731 SVBT64 pnBuffer;
732 mxDecrypter->Read( mrStrm, pnBuffer, 8 );
733 rfValue = SVBT64ToDouble( pnBuffer );
734 }
735 else
736 mrStrm >> rfValue;
737 mnRawRecLeft -= 8;
738 }
739 return *this;
740 }
741
ReadInt8()742 sal_Int8 XclImpStream::ReadInt8()
743 {
744 sal_Int8 nValue(0);
745 operator>>( nValue );
746 return nValue;
747 }
748
ReaduInt8()749 sal_uInt8 XclImpStream::ReaduInt8()
750 {
751 sal_uInt8 nValue(0);
752 operator>>( nValue );
753 return nValue;
754 }
755
ReadInt16()756 sal_Int16 XclImpStream::ReadInt16()
757 {
758 sal_Int16 nValue(0);
759 operator>>( nValue );
760 return nValue;
761 }
762
ReaduInt16()763 sal_uInt16 XclImpStream::ReaduInt16()
764 {
765 sal_uInt16 nValue(0);
766 operator>>( nValue );
767 return nValue;
768 }
769
ReadInt32()770 sal_Int32 XclImpStream::ReadInt32()
771 {
772 sal_Int32 nValue(0);
773 operator>>( nValue );
774 return nValue;
775 }
776
ReaduInt32()777 sal_uInt32 XclImpStream::ReaduInt32()
778 {
779 sal_uInt32 nValue(0);
780 operator>>( nValue );
781 return nValue;
782 }
783
ReadFloat()784 float XclImpStream::ReadFloat()
785 {
786 float fValue(0.0);
787 operator>>( fValue );
788 return fValue;
789 }
790
ReadDouble()791 double XclImpStream::ReadDouble()
792 {
793 double fValue(0.0);
794 operator>>( fValue );
795 return fValue;
796 }
797
Read(void * pData,sal_Size nBytes)798 sal_Size XclImpStream::Read( void* pData, sal_Size nBytes )
799 {
800 sal_Size nRet = 0;
801 if( mbValid && pData && (nBytes > 0) )
802 {
803 sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( pData );
804 sal_Size nBytesLeft = nBytes;
805
806 while( mbValid && (nBytesLeft > 0) )
807 {
808 sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
809 sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize );
810 nRet += nReadRet;
811 mbValid = (nReadSize == nReadRet);
812 DBG_ASSERT( mbValid, "XclImpStream::Read - stream read error" );
813 pnBuffer += nReadRet;
814 nBytesLeft -= nReadRet;
815 if( mbValid && (nBytesLeft > 0) )
816 JumpToNextContinue();
817 DBG_ASSERT( mbValid, "XclImpStream::Read - record overread" );
818 }
819 }
820 return nRet;
821 }
822
CopyToStream(SvStream & rOutStrm,sal_Size nBytes)823 sal_Size XclImpStream::CopyToStream( SvStream& rOutStrm, sal_Size nBytes )
824 {
825 sal_Size nRet = 0;
826 if( mbValid && (nBytes > 0) )
827 {
828 const sal_Size nMaxBuffer = 4096;
829 sal_uInt8* pnBuffer = new sal_uInt8[ ::std::min( nBytes, nMaxBuffer ) ];
830 sal_Size nBytesLeft = nBytes;
831
832 while( mbValid && (nBytesLeft > 0) )
833 {
834 sal_Size nReadSize = ::std::min( nBytesLeft, nMaxBuffer );
835 nRet += Read( pnBuffer, nReadSize );
836 rOutStrm.Write( pnBuffer, nReadSize );
837 nBytesLeft -= nReadSize;
838 }
839
840 delete[] pnBuffer;
841 }
842 return nRet;
843 }
844
CopyRecordToStream(SvStream & rOutStrm)845 sal_Size XclImpStream::CopyRecordToStream( SvStream& rOutStrm )
846 {
847 sal_Size nRet = 0;
848 if( mbValidRec )
849 {
850 PushPosition();
851 RestorePosition( maFirstRec );
852 nRet = CopyToStream( rOutStrm, GetRecSize() );
853 PopPosition();
854 }
855 return nRet;
856 }
857
Seek(sal_Size nPos)858 void XclImpStream::Seek( sal_Size nPos )
859 {
860 if( mbValidRec )
861 {
862 sal_Size nCurrPos = GetRecPos();
863 if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward
864 {
865 RestorePosition( maFirstRec );
866 Ignore( nPos );
867 }
868 else if( nPos > nCurrPos ) // forward
869 {
870 Ignore( nPos - nCurrPos );
871 }
872 }
873 }
874
Ignore(sal_Size nBytes)875 void XclImpStream::Ignore( sal_Size nBytes )
876 {
877 // implementation similar to Read(), but without really reading anything
878 sal_Size nBytesLeft = nBytes;
879 while( mbValid && (nBytesLeft > 0) )
880 {
881 sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
882 mrStrm.SeekRel( nReadSize );
883 mnRawRecLeft = mnRawRecLeft - nReadSize;
884 nBytesLeft -= nReadSize;
885 if( nBytesLeft > 0 )
886 JumpToNextContinue();
887 DBG_ASSERT( mbValid, "XclImpStream::Ignore - record overread" );
888 }
889 }
890
891 // ----------------------------------------------------------------------------
892
ReadUniStringExtHeader(bool & rb16Bit,bool & rbRich,bool & rbFareast,sal_uInt16 & rnFormatRuns,sal_uInt32 & rnExtInf,sal_uInt8 nFlags)893 sal_Size XclImpStream::ReadUniStringExtHeader(
894 bool& rb16Bit, bool& rbRich, bool& rbFareast,
895 sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags )
896 {
897 DBG_ASSERT( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" );
898 rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT );
899 rbRich = ::get_flag( nFlags, EXC_STRF_RICH );
900 rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST );
901 rnFormatRuns = rbRich ? ReaduInt16() : 0;
902 rnExtInf = rbFareast ? ReaduInt32() : 0;
903 return rnExtInf + 4 * rnFormatRuns;
904 }
905
ReadUniStringExtHeader(bool & rb16Bit,sal_uInt8 nFlags)906 sal_Size XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags )
907 {
908 bool bRich, bFareast;
909 sal_uInt16 nCrun;
910 sal_uInt32 nExtInf;
911 return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags );
912 }
913
914 // ----------------------------------------------------------------------------
915
ReadRawUniString(sal_uInt16 nChars,bool b16Bit)916 String XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit )
917 {
918 String aRet;
919 sal_uInt16 nCharsLeft = nChars;
920 sal_uInt16 nReadSize;
921
922 sal_Unicode* pcBuffer = new sal_Unicode[ nCharsLeft + 1 ];
923
924 while( IsValid() && (nCharsLeft > 0) )
925 {
926 if( b16Bit )
927 {
928 nReadSize = ::std::min< sal_uInt16 >( nCharsLeft, mnRawRecLeft / 2 );
929 DBG_ASSERT( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
930 "XclImpStream::ReadRawUniString - missing a byte" );
931 }
932 else
933 nReadSize = GetMaxRawReadSize( nCharsLeft );
934
935 sal_Unicode* pcUniChar = pcBuffer;
936 sal_Unicode* pcEndChar = pcBuffer + nReadSize;
937
938 if( b16Bit )
939 {
940 sal_uInt16 nReadChar;
941 for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
942 {
943 operator>>( nReadChar );
944 (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
945 }
946 }
947 else
948 {
949 sal_uInt8 nReadChar;
950 for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
951 {
952 operator>>( nReadChar );
953 (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
954 }
955 }
956
957 *pcEndChar = '\0';
958 aRet.Append( pcBuffer );
959
960 nCharsLeft = nCharsLeft - nReadSize;
961 if( nCharsLeft > 0 )
962 JumpToNextStringContinue( b16Bit );
963 }
964
965 delete[] pcBuffer;
966 return aRet;
967 }
968
ReadUniString(sal_uInt16 nChars,sal_uInt8 nFlags)969 String XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
970 {
971 bool b16Bit;
972 sal_Size nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
973 String aRet( ReadRawUniString( nChars, b16Bit ) );
974 Ignore( nExtSize );
975 return aRet;
976 }
977
ReadUniString(sal_uInt16 nChars)978 String XclImpStream::ReadUniString( sal_uInt16 nChars )
979 {
980 return ReadUniString( nChars, ReaduInt8() );
981 }
982
ReadUniString()983 String XclImpStream::ReadUniString()
984 {
985 return ReadUniString( ReaduInt16() );
986 }
987
IgnoreRawUniString(sal_uInt16 nChars,bool b16Bit)988 void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit )
989 {
990 sal_uInt16 nCharsLeft = nChars;
991 sal_uInt16 nReadSize;
992
993 while( IsValid() && (nCharsLeft > 0) )
994 {
995 if( b16Bit )
996 {
997 nReadSize = ::std::min< sal_uInt16 >( nCharsLeft, mnRawRecLeft / 2 );
998 DBG_ASSERT( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
999 "XclImpStream::IgnoreRawUniString - missing a byte" );
1000 Ignore( nReadSize * 2 );
1001 }
1002 else
1003 {
1004 nReadSize = GetMaxRawReadSize( nCharsLeft );
1005 Ignore( nReadSize );
1006 }
1007
1008 nCharsLeft = nCharsLeft - nReadSize;
1009 if( nCharsLeft > 0 )
1010 JumpToNextStringContinue( b16Bit );
1011 }
1012 }
1013
IgnoreUniString(sal_uInt16 nChars,sal_uInt8 nFlags)1014 void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
1015 {
1016 bool b16Bit;
1017 sal_Size nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
1018 IgnoreRawUniString( nChars, b16Bit );
1019 Ignore( nExtSize );
1020 }
1021
IgnoreUniString(sal_uInt16 nChars)1022 void XclImpStream::IgnoreUniString( sal_uInt16 nChars )
1023 {
1024 IgnoreUniString( nChars, ReaduInt8() );
1025 }
1026
IgnoreUniString()1027 void XclImpStream::IgnoreUniString()
1028 {
1029 IgnoreUniString( ReaduInt16() );
1030 }
1031
1032 // ----------------------------------------------------------------------------
1033
ReadRawByteString(sal_uInt16 nChars)1034 String XclImpStream::ReadRawByteString( sal_uInt16 nChars )
1035 {
1036 sal_Char* pcBuffer = new sal_Char[ nChars + 1 ];
1037 sal_uInt16 nCharsRead = ReadRawData( pcBuffer, nChars );
1038 pcBuffer[ nCharsRead ] = '\0';
1039 String aRet( pcBuffer, mrRoot.GetTextEncoding() );
1040 delete[] pcBuffer;
1041 return aRet;
1042 }
1043
ReadByteString(bool b16BitLen)1044 String XclImpStream::ReadByteString( bool b16BitLen )
1045 {
1046 return ReadRawByteString( ReadByteStrLen( b16BitLen ) );
1047 }
1048
1049 // private --------------------------------------------------------------------
1050
StorePosition(XclImpStreamPos & rPos)1051 void XclImpStream::StorePosition( XclImpStreamPos& rPos )
1052 {
1053 rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
1054 }
1055
RestorePosition(const XclImpStreamPos & rPos)1056 void XclImpStream::RestorePosition( const XclImpStreamPos& rPos )
1057 {
1058 rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
1059 SetupDecrypter();
1060 }
1061
ReadNextRawRecHeader()1062 bool XclImpStream::ReadNextRawRecHeader()
1063 {
1064 mrStrm.Seek( mnNextRecPos );
1065 bool bRet = mnNextRecPos + 4 <= mnStreamSize;
1066 if( bRet )
1067 mrStrm >> mnRawRecId >> mnRawRecSize;
1068 return bRet;
1069 }
1070
SetupDecrypter()1071 void XclImpStream::SetupDecrypter()
1072 {
1073 if( mxDecrypter.is() )
1074 mxDecrypter->Update( mrStrm, mnRawRecSize );
1075 }
1076
SetupRawRecord()1077 void XclImpStream::SetupRawRecord()
1078 {
1079 // pre: mnRawRecSize contains current raw record size
1080 // pre: mrStrm points to start of raw record data
1081 mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
1082 mnRawRecLeft = mnRawRecSize;
1083 mnCurrRecSize += mnRawRecSize;
1084 SetupDecrypter(); // decrypter works on raw record level
1085 }
1086
SetupRecord()1087 void XclImpStream::SetupRecord()
1088 {
1089 mnRecId = mnRawRecId;
1090 mnAltContId = EXC_ID_UNKNOWN;
1091 mnCurrRecSize = 0;
1092 mnComplRecSize = mnRawRecSize;
1093 mbHasComplRec = !mbCont;
1094 SetupRawRecord();
1095 SetNulSubstChar();
1096 EnableDecryption();
1097 StorePosition( maFirstRec );
1098 }
1099
IsContinueId(sal_uInt16 nRecId) const1100 bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const
1101 {
1102 return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId);
1103 }
1104
JumpToNextContinue()1105 bool XclImpStream::JumpToNextContinue()
1106 {
1107 mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId );
1108 if( mbValid ) // do not setup a following non-CONTINUE record
1109 SetupRawRecord();
1110 return mbValid;
1111 }
1112
JumpToNextStringContinue(bool & rb16Bit)1113 bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit )
1114 {
1115 DBG_ASSERT( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" );
1116
1117 if( mbCont && (GetRecLeft() > 0) )
1118 {
1119 JumpToNextContinue();
1120 }
1121 else if( mnRecId == EXC_ID_CONT )
1122 {
1123 // CONTINUE handling is off, but we have started reading in a CONTINUE record
1124 // -> start next CONTINUE for TXO import
1125 mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0));
1126 mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT);
1127 // we really start a new record here - no chance to return to string origin
1128 if( mbValid )
1129 SetupRecord();
1130 }
1131 else
1132 mbValid = false;
1133
1134 if( mbValid )
1135 rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT );
1136 return mbValid;
1137 }
1138
EnsureRawReadSize(sal_uInt16 nBytes)1139 bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes )
1140 {
1141 if( mbValid && nBytes )
1142 {
1143 while( mbValid && !mnRawRecLeft ) JumpToNextContinue();
1144 mbValid = mbValid && (nBytes <= mnRawRecLeft);
1145 DBG_ASSERT( mbValid, "XclImpStream::EnsureRawReadSize - record overread" );
1146 }
1147 return mbValid;
1148 }
1149
GetMaxRawReadSize(sal_Size nBytes) const1150 sal_uInt16 XclImpStream::GetMaxRawReadSize( sal_Size nBytes ) const
1151 {
1152 return static_cast< sal_uInt16 >( ::std::min< sal_Size >( nBytes, mnRawRecLeft ) );
1153 }
1154
ReadRawData(void * pData,sal_uInt16 nBytes)1155 sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes )
1156 {
1157 DBG_ASSERT( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" );
1158 sal_uInt16 nRet = 0;
1159 if( mbUseDecr )
1160 nRet = mxDecrypter->Read( mrStrm, pData, nBytes );
1161 else
1162 nRet = static_cast< sal_uInt16 >( mrStrm.Read( pData, nBytes ) );
1163 mnRawRecLeft = mnRawRecLeft - nRet;
1164 return nRet;
1165 }
1166
1167 // ============================================================================
1168
1169