bump product version to 4.1.6.2
[LibreOffice.git] / package / source / zipapi / ZipFile.cxx
blobf9da68b88fe5ba3e6549b98d5c5cfdc097e634ae
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
22 #include <com/sun/star/ucb/XProgressHandler.hpp>
23 #include <com/sun/star/packages/zip/ZipConstants.hpp>
24 #include <com/sun/star/xml/crypto/XCipherContext.hpp>
25 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
26 #include <com/sun/star/xml/crypto/XCipherContextSupplier.hpp>
27 #include <com/sun/star/xml/crypto/XDigestContextSupplier.hpp>
28 #include <com/sun/star/xml/crypto/CipherID.hpp>
29 #include <com/sun/star/xml/crypto/DigestID.hpp>
30 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
32 #include <comphelper/storagehelper.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <rtl/digest.h>
36 #include <vector>
38 #include "blowfishcontext.hxx"
39 #include "sha1context.hxx"
40 #include <ZipFile.hxx>
41 #include <ZipEnumeration.hxx>
42 #include <XUnbufferedStream.hxx>
43 #include <PackageConstants.hxx>
44 #include <EncryptedDataHeader.hxx>
45 #include <EncryptionData.hxx>
46 #include <MemoryByteGrabber.hxx>
48 #include <CRC32.hxx>
50 using namespace com::sun::star;
51 using namespace com::sun::star::io;
52 using namespace com::sun::star::uno;
53 using namespace com::sun::star::ucb;
54 using namespace com::sun::star::lang;
55 using namespace com::sun::star::packages;
56 using namespace com::sun::star::packages::zip;
57 using namespace com::sun::star::packages::zip::ZipConstants;
59 using ZipUtils::Inflater;
61 /** This class is used to read entries from a zip file
63 ZipFile::ZipFile( uno::Reference < XInputStream > &xInput, const uno::Reference < XComponentContext > & rxContext, sal_Bool bInitialise )
64 throw(IOException, ZipException, RuntimeException)
65 : aGrabber(xInput)
66 , aInflater (sal_True)
67 , xStream(xInput)
68 , xSeek(xInput, UNO_QUERY)
69 , m_xContext ( rxContext )
70 , bRecoveryMode( sal_False )
72 if (bInitialise)
74 if ( readCEN() == -1 )
76 aEntries.clear();
77 throw ZipException( "stream data looks to be broken", uno::Reference < XInterface > () );
84 ZipFile::ZipFile( uno::Reference < XInputStream > &xInput, const uno::Reference < XComponentContext > & rxContext, sal_Bool bInitialise, sal_Bool bForceRecovery, uno::Reference < XProgressHandler > xProgress )
85 throw(IOException, ZipException, RuntimeException)
86 : aGrabber(xInput)
87 , aInflater (sal_True)
88 , xStream(xInput)
89 , xSeek(xInput, UNO_QUERY)
90 , m_xContext ( rxContext )
91 , xProgressHandler( xProgress )
92 , bRecoveryMode( bForceRecovery )
94 if (bInitialise)
96 if ( bForceRecovery )
98 recover();
100 else if ( readCEN() == -1 )
102 aEntries.clear();
103 throw ZipException("stream data looks to be broken", uno::Reference < XInterface > () );
108 ZipFile::~ZipFile()
110 aEntries.clear();
113 void ZipFile::setInputStream ( uno::Reference < XInputStream > xNewStream )
115 ::osl::MutexGuard aGuard( m_aMutex );
117 xStream = xNewStream;
118 xSeek = uno::Reference < XSeekable > ( xStream, UNO_QUERY );
119 aGrabber.setInputStream ( xStream );
122 uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
124 uno::Reference< xml::crypto::XDigestContext > xDigestContext;
125 if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K )
127 uno::Reference< uno::XComponentContext > xContext = xArgContext;
128 if ( !xContext.is() )
129 xContext = comphelper::getProcessComponentContext();
131 uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
133 xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
135 else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K )
136 xDigestContext.set( SHA1DigestContext::Create(), uno::UNO_SET_THROW );
138 return xDigestContext;
141 uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
143 uno::Reference< xml::crypto::XCipherContext > xResult;
147 if (xEncryptionData->m_nDerivedKeySize < 0)
149 throw ZipIOException("Invalid derived key length!",
150 uno::Reference< XInterface >() );
153 uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
154 if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
155 aDerivedKey.getLength(),
156 reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
157 xEncryptionData->m_aKey.getLength(),
158 reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
159 xEncryptionData->m_aSalt.getLength(),
160 xEncryptionData->m_nIterationCount ) )
162 throw ZipIOException("Can not create derived key!",
163 uno::Reference< XInterface >() );
166 if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
168 uno::Reference< uno::XComponentContext > xContext = xArgContext;
169 if ( !xContext.is() )
170 xContext = comphelper::getProcessComponentContext();
172 uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
174 xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
176 else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
178 xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
180 else
182 throw ZipIOException("Unknown cipher algorithm is requested!",
183 uno::Reference< XInterface >() );
186 catch( ... )
188 OSL_ENSURE( sal_False, "Can not create cipher context!" );
191 return xResult;
194 void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
195 sal_Int64 nSize,
196 const OUString& aMediaType,
197 sal_Int8 * & pHeader )
199 // I think it's safe to restrict vector and salt length to 2 bytes !
200 sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
201 sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
202 sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
203 sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
205 // First the header
206 *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
207 *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
208 *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
209 *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
211 // Then the version
212 *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
213 *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
215 // Then the iteration Count
216 sal_Int32 nIterationCount = rData->m_nIterationCount;
217 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
218 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
219 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
220 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
222 // FIXME64: need to handle larger sizes
223 // Then the size:
224 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
225 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
226 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
227 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
229 // Then the encryption algorithm
230 sal_Int32 nEncAlgID = rData->m_nEncAlg;
231 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
232 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
233 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
234 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
236 // Then the checksum algorithm
237 sal_Int32 nChecksumAlgID = rData->m_nCheckAlg;
238 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
239 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
240 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
241 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
243 // Then the derived key size
244 sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
245 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
246 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
247 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
248 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
250 // Then the start key generation algorithm
251 sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
252 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
253 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
254 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
255 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
257 // Then the salt length
258 *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
259 *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
261 // Then the IV length
262 *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
263 *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
265 // Then the digest length
266 *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
267 *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
269 // Then the mediatype length
270 *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
271 *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
273 // Then the salt content
274 memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
275 pHeader += nSaltLength;
277 // Then the IV content
278 memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
279 pHeader += nIVLength;
281 // Then the digest content
282 memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
283 pHeader += nDigestLength;
285 // Then the mediatype itself
286 memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
287 pHeader += nMediaTypeLength;
290 sal_Bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > & rData,
291 sal_Int32 &rEncAlg,
292 sal_Int32 &rChecksumAlg,
293 sal_Int32 &rDerivedKeySize,
294 sal_Int32 &rStartKeyGenID,
295 sal_Int32 &rSize,
296 OUString& aMediaType,
297 const uno::Reference< XInputStream >& rStream )
299 sal_Bool bOk = sal_False;
300 const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
301 Sequence < sal_Int8 > aBuffer ( nHeaderSize );
302 if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
304 sal_Int16 nPos = 0;
305 sal_Int8 *pBuffer = aBuffer.getArray();
306 sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
307 nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
308 if ( nVersion == n_ConstCurrentVersion )
310 sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
311 nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
312 nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
313 nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
314 rData->m_nIterationCount = nCount;
316 rSize = pBuffer[nPos++] & 0xFF;
317 rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
318 rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
319 rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
321 rEncAlg = pBuffer[nPos++] & 0xFF;
322 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
323 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
324 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
326 rChecksumAlg = pBuffer[nPos++] & 0xFF;
327 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
328 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
329 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
331 rDerivedKeySize = pBuffer[nPos++] & 0xFF;
332 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 8;
333 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 16;
334 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 24;
336 rStartKeyGenID = pBuffer[nPos++] & 0xFF;
337 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 8;
338 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 16;
339 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 24;
341 sal_Int16 nSaltLength = pBuffer[nPos++] & 0xFF;
342 nSaltLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
343 sal_Int16 nIVLength = ( pBuffer[nPos++] & 0xFF );
344 nIVLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
345 sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
346 nDigestLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
348 sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
349 nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
351 if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
353 rData->m_aSalt.realloc ( nSaltLength );
354 memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
355 if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
357 rData->m_aInitVector.realloc ( nIVLength );
358 memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
359 if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
361 rData->m_aDigest.realloc ( nDigestLength );
362 memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
364 if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
366 aMediaType = OUString( (sal_Unicode*)aBuffer.getConstArray(),
367 nMediaTypeLength / sizeof( sal_Unicode ) );
368 bOk = sal_True;
375 return bOk;
378 uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream( const uno::Reference< uno::XComponentContext >& rxContext,
379 const uno::Reference< XInputStream >& xStream,
380 const ::rtl::Reference< EncryptionData > &rData )
381 throw ( packages::WrongPasswordException, ZipIOException, RuntimeException )
383 if ( !rData.is() )
384 throw ZipIOException("Encrypted stream without encryption data!\n",
385 uno::Reference< XInterface >() );
387 if ( !rData->m_aKey.getLength() )
388 throw packages::WrongPasswordException(OSL_LOG_PREFIX, uno::Reference< uno::XInterface >() );
390 uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY );
391 if ( !xSeek.is() )
392 throw ZipIOException("The stream must be seekable!\n",
393 uno::Reference< XInterface >() );
396 // if we have a digest, then this file is an encrypted one and we should
397 // check if we can decrypt it or not
398 OSL_ENSURE( rData->m_aDigest.getLength(), "Can't detect password correctness without digest!\n" );
399 if ( rData->m_aDigest.getLength() )
401 sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() );
402 if ( nSize > n_ConstDigestLength + 32 )
403 nSize = n_ConstDigestLength + 32;
405 // skip header
406 xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() +
407 rData->m_aSalt.getLength() + rData->m_aDigest.getLength() );
409 // Only want to read enough to verify the digest
410 Sequence < sal_Int8 > aReadBuffer ( nSize );
412 xStream->readBytes( aReadBuffer, nSize );
414 if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) )
415 throw packages::WrongPasswordException(OSL_LOG_PREFIX, uno::Reference< uno::XInterface >() );
418 return new XUnbufferedStream( rxContext, xStream, rData );
421 #if 0
422 // for debugging purposes
423 void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
425 if ( aSequence.getLength() )
427 sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
428 sal_Int32 nSize = *( pPointer + 1 );
429 sal_Int32 nMemSize = *( pPointer - 2 );
430 sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
431 OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
434 #endif
436 sal_Bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
438 if ( !rData.is() || !rData->m_aKey.getLength() )
439 return sal_False;
441 sal_Bool bRet = sal_False;
443 uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
445 uno::Sequence< sal_Int8 > aDecryptBuffer;
446 uno::Sequence< sal_Int8 > aDecryptBuffer2;
449 aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
450 aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
452 catch( uno::Exception& )
454 // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
455 // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
458 if ( aDecryptBuffer2.getLength() )
460 sal_Int32 nOldLen = aDecryptBuffer.getLength();
461 aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
462 memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getArray(), aDecryptBuffer2.getLength() );
465 if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
466 aDecryptBuffer.realloc( n_ConstDigestLength );
468 uno::Sequence< sal_Int8 > aDigestSeq;
469 uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
471 xDigestContext->updateDigest( aDecryptBuffer );
472 aDigestSeq = xDigestContext->finalizeDigestAndDispose();
474 // If we don't have a digest, then we have to assume that the password is correct
475 if ( rData->m_aDigest.getLength() != 0 &&
476 ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
477 0 != memcmp ( aDigestSeq.getConstArray(),
478 rData->m_aDigest.getConstArray(),
479 aDigestSeq.getLength() ) ) )
481 // We should probably tell the user that the password they entered was wrong
483 else
484 bRet = sal_True;
486 return bRet;
489 sal_Bool ZipFile::hasValidPassword ( ZipEntry & rEntry, const ::rtl::Reference< EncryptionData >& rData )
491 ::osl::MutexGuard aGuard( m_aMutex );
493 sal_Bool bRet = sal_False;
494 if ( rData.is() && rData->m_aKey.getLength() )
496 xSeek->seek( rEntry.nOffset );
497 sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
499 // Only want to read enough to verify the digest
500 if ( nSize > n_ConstDigestDecrypt )
501 nSize = n_ConstDigestDecrypt;
503 Sequence < sal_Int8 > aReadBuffer ( nSize );
505 xStream->readBytes( aReadBuffer, nSize );
507 bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData );
510 return bRet;
513 uno::Reference< XInputStream > ZipFile::createUnbufferedStream(
514 SotMutexHolderRef aMutexHolder,
515 ZipEntry & rEntry,
516 const ::rtl::Reference< EncryptionData > &rData,
517 sal_Int8 nStreamMode,
518 sal_Bool bIsEncrypted,
519 OUString aMediaType )
521 ::osl::MutexGuard aGuard( m_aMutex );
523 return new XUnbufferedStream ( m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode );
527 ZipEnumeration * SAL_CALL ZipFile::entries( )
529 return new ZipEnumeration ( aEntries );
532 uno::Reference< XInputStream > SAL_CALL ZipFile::getInputStream( ZipEntry& rEntry,
533 const ::rtl::Reference< EncryptionData > &rData,
534 sal_Bool bIsEncrypted,
535 SotMutexHolderRef aMutexHolder )
536 throw(IOException, ZipException, RuntimeException)
538 ::osl::MutexGuard aGuard( m_aMutex );
540 if ( rEntry.nOffset <= 0 )
541 readLOC( rEntry );
543 // We want to return a rawStream if we either don't have a key or if the
544 // key is wrong
546 sal_Bool bNeedRawStream = rEntry.nMethod == STORED;
548 // if we have a digest, then this file is an encrypted one and we should
549 // check if we can decrypt it or not
550 if ( bIsEncrypted && rData.is() && rData->m_aDigest.getLength() )
551 bNeedRawStream = !hasValidPassword ( rEntry, rData );
553 return createUnbufferedStream ( aMutexHolder,
554 rEntry,
555 rData,
556 bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
557 bIsEncrypted );
560 uno::Reference< XInputStream > SAL_CALL ZipFile::getDataStream( ZipEntry& rEntry,
561 const ::rtl::Reference< EncryptionData > &rData,
562 sal_Bool bIsEncrypted,
563 SotMutexHolderRef aMutexHolder )
564 throw ( packages::WrongPasswordException,
565 IOException,
566 ZipException,
567 RuntimeException )
569 ::osl::MutexGuard aGuard( m_aMutex );
571 if ( rEntry.nOffset <= 0 )
572 readLOC( rEntry );
574 // An exception must be thrown in case stream is encrypted and
575 // there is no key or the key is wrong
576 sal_Bool bNeedRawStream = sal_False;
577 if ( bIsEncrypted )
579 // in case no digest is provided there is no way
580 // to detect password correctness
581 if ( !rData.is() )
582 throw ZipException("Encrypted stream without encryption data!\n",
583 uno::Reference< XInterface >() );
585 // if we have a digest, then this file is an encrypted one and we should
586 // check if we can decrypt it or not
587 OSL_ENSURE( rData->m_aDigest.getLength(), "Can't detect password correctness without digest!\n" );
588 if ( rData->m_aDigest.getLength() && !hasValidPassword ( rEntry, rData ) )
589 throw packages::WrongPasswordException(OSL_LOG_PREFIX, uno::Reference< uno::XInterface >() );
591 else
592 bNeedRawStream = ( rEntry.nMethod == STORED );
594 return createUnbufferedStream ( aMutexHolder,
595 rEntry,
596 rData,
597 bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
598 bIsEncrypted );
601 uno::Reference< XInputStream > SAL_CALL ZipFile::getRawData( ZipEntry& rEntry,
602 const ::rtl::Reference< EncryptionData >& rData,
603 sal_Bool bIsEncrypted,
604 SotMutexHolderRef aMutexHolder )
605 throw(IOException, ZipException, RuntimeException)
607 ::osl::MutexGuard aGuard( m_aMutex );
609 if ( rEntry.nOffset <= 0 )
610 readLOC( rEntry );
612 return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted );
615 uno::Reference< XInputStream > SAL_CALL ZipFile::getWrappedRawStream(
616 ZipEntry& rEntry,
617 const ::rtl::Reference< EncryptionData >& rData,
618 const OUString& aMediaType,
619 SotMutexHolderRef aMutexHolder )
620 throw ( packages::NoEncryptionException,
621 IOException,
622 ZipException,
623 RuntimeException )
625 ::osl::MutexGuard aGuard( m_aMutex );
627 if ( !rData.is() )
628 throw packages::NoEncryptionException(OSL_LOG_PREFIX, uno::Reference< uno::XInterface >() );
630 if ( rEntry.nOffset <= 0 )
631 readLOC( rEntry );
633 return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, sal_True, aMediaType );
636 sal_Bool ZipFile::readLOC( ZipEntry &rEntry )
637 throw(IOException, ZipException, RuntimeException)
639 ::osl::MutexGuard aGuard( m_aMutex );
641 sal_Int32 nTestSig, nTime, nCRC, nSize, nCompressedSize;
642 sal_Int16 nVersion, nFlag, nHow, nPathLen, nExtraLen;
643 sal_Int64 nPos = -rEntry.nOffset;
645 aGrabber.seek(nPos);
646 aGrabber >> nTestSig;
648 if (nTestSig != LOCSIG)
649 throw ZipIOException("Invalid LOC header (bad signature)", uno::Reference < XInterface > () );
650 aGrabber >> nVersion;
651 aGrabber >> nFlag;
652 aGrabber >> nHow;
653 aGrabber >> nTime;
654 aGrabber >> nCRC;
655 aGrabber >> nCompressedSize;
656 aGrabber >> nSize;
657 aGrabber >> nPathLen;
658 aGrabber >> nExtraLen;
659 rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
661 // FIXME64: need to read 64bit LOC
663 sal_Bool bBroken = sal_False;
667 // read always in UTF8, some tools seem not to set UTF8 bit
668 uno::Sequence < sal_Int8 > aNameBuffer( nPathLen );
669 sal_Int32 nRead = aGrabber.readBytes( aNameBuffer, nPathLen );
670 if ( nRead < aNameBuffer.getLength() )
671 aNameBuffer.realloc( nRead );
673 OUString sLOCPath = OUString::intern( (sal_Char *) aNameBuffer.getArray(),
674 aNameBuffer.getLength(),
675 RTL_TEXTENCODING_UTF8 );
677 if ( rEntry.nPathLen == -1 ) // the file was created
679 rEntry.nPathLen = nPathLen;
680 rEntry.sPath = sLOCPath;
683 // check basic local file header / entry consistency, just
684 // plain ignore bits 1 & 2 of the flag field - they are either
685 // purely informative, or even fully undefined (depending on
686 // nMethod)
687 // Do *not* compare nMethod / nHow, older versions with
688 // encrypted streams write mismatching DEFLATE/STORE pairs
689 // there.
690 // Do *not* compare timestamps, since MSO 2010 can produce documents
691 // with timestamp difference in the central directory entry and local
692 // file header.
693 bBroken = rEntry.nVersion != nVersion
694 || rEntry.nPathLen != nPathLen
695 || !rEntry.sPath.equals( sLOCPath );
697 catch(...)
699 bBroken = sal_True;
702 if ( bBroken && !bRecoveryMode )
703 throw ZipIOException("The stream seems to be broken!",
704 uno::Reference< XInterface >() );
706 return sal_True;
709 sal_Int32 ZipFile::findEND( )
710 throw(IOException, ZipException, RuntimeException)
712 // this method is called in constructor only, no need for mutex
713 sal_Int32 nLength, nPos, nEnd;
714 Sequence < sal_Int8 > aBuffer;
717 nLength = static_cast <sal_Int32 > (aGrabber.getLength());
718 if (nLength == 0 || nLength < ENDHDR)
719 return -1;
720 nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
721 nEnd = nPos >= 0 ? nPos : 0 ;
723 aGrabber.seek( nEnd );
724 aGrabber.readBytes ( aBuffer, nLength - nEnd );
726 const sal_Int8 *pBuffer = aBuffer.getConstArray();
728 nPos = nLength - nEnd - ENDHDR;
729 while ( nPos >= 0 )
731 if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
732 return nPos + nEnd;
733 nPos--;
736 catch ( IllegalArgumentException& )
738 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
740 catch ( NotConnectedException& )
742 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
744 catch ( BufferSizeExceededException& )
746 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
748 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
751 sal_Int32 ZipFile::readCEN()
752 throw(IOException, ZipException, RuntimeException)
754 // this method is called in constructor only, no need for mutex
755 sal_Int32 nCenLen, nCenPos = -1, nCenOff, nEndPos, nLocPos;
756 sal_uInt16 nCount, nTotal;
760 nEndPos = findEND();
761 if (nEndPos == -1)
762 return -1;
763 aGrabber.seek(nEndPos + ENDTOT);
764 aGrabber >> nTotal;
765 aGrabber >> nCenLen;
766 aGrabber >> nCenOff;
768 if ( nTotal * CENHDR > nCenLen )
769 throw ZipException("invalid END header (bad entry count)", uno::Reference < XInterface > () );
771 if ( nTotal > ZIP_MAXENTRIES )
772 throw ZipException("too many entries in ZIP File", uno::Reference < XInterface > () );
774 if ( nCenLen < 0 || nCenLen > nEndPos )
775 throw ZipException("Invalid END header (bad central directory size)", uno::Reference < XInterface > () );
777 nCenPos = nEndPos - nCenLen;
779 if ( nCenOff < 0 || nCenOff > nCenPos )
780 throw ZipException("Invalid END header (bad central directory size)", uno::Reference < XInterface > () );
782 nLocPos = nCenPos - nCenOff;
783 aGrabber.seek( nCenPos );
784 Sequence < sal_Int8 > aCENBuffer ( nCenLen );
785 sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
786 if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
787 throw ZipException ("Error reading CEN into memory buffer!", uno::Reference < XInterface > () );
789 MemoryByteGrabber aMemGrabber ( aCENBuffer );
791 ZipEntry aEntry;
792 sal_Int32 nTestSig;
793 sal_Int16 nCommentLen;
795 for (nCount = 0 ; nCount < nTotal; nCount++)
797 aMemGrabber >> nTestSig;
798 if ( nTestSig != CENSIG )
799 throw ZipException("Invalid CEN header (bad signature)", uno::Reference < XInterface > () );
801 aMemGrabber.skipBytes ( 2 );
802 aMemGrabber >> aEntry.nVersion;
804 if ( ( aEntry.nVersion & 1 ) == 1 )
805 throw ZipException("Invalid CEN header (encrypted entry)", uno::Reference < XInterface > () );
807 aMemGrabber >> aEntry.nFlag;
808 aMemGrabber >> aEntry.nMethod;
810 if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
811 throw ZipException("Invalid CEN header (bad compression method)", uno::Reference < XInterface > () );
813 aMemGrabber >> aEntry.nTime;
814 aMemGrabber >> aEntry.nCrc;
816 sal_uInt32 nCompressedSize, nSize, nOffset;
818 aMemGrabber >> nCompressedSize;
819 aMemGrabber >> nSize;
820 aMemGrabber >> aEntry.nPathLen;
821 aMemGrabber >> aEntry.nExtraLen;
822 aMemGrabber >> nCommentLen;
823 aMemGrabber.skipBytes ( 8 );
824 aMemGrabber >> nOffset;
826 // FIXME64: need to read the 64bit header instead
827 if ( nSize == 0xffffffff ||
828 nOffset == 0xffffffff ||
829 nCompressedSize == 0xffffffff ) {
830 throw ZipException("PK64 zip file entry", uno::Reference < XInterface > () );
831 } else {
832 aEntry.nCompressedSize = nCompressedSize;
833 aEntry.nSize = nSize;
834 aEntry.nOffset = nOffset;
837 aEntry.nOffset += nLocPos;
838 aEntry.nOffset *= -1;
840 if ( aEntry.nPathLen < 0 )
841 throw ZipException("unexpected name length", uno::Reference < XInterface > () );
843 if ( nCommentLen < 0 )
844 throw ZipException("unexpected comment length", uno::Reference < XInterface > () );
846 if ( aEntry.nExtraLen < 0 )
847 throw ZipException("unexpected extra header info length", uno::Reference < XInterface > () );
849 // read always in UTF8, some tools seem not to set UTF8 bit
850 aEntry.sPath = OUString::intern ( (sal_Char *) aMemGrabber.getCurrentPos(),
851 aEntry.nPathLen,
852 RTL_TEXTENCODING_UTF8 );
854 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, sal_True ) )
855 throw ZipException("Zip entry has an invalid name.", uno::Reference < XInterface > () );
857 aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
858 aEntries[aEntry.sPath] = aEntry;
861 if (nCount != nTotal)
862 throw ZipException("Count != Total", uno::Reference < XInterface > () );
864 catch ( IllegalArgumentException & )
866 // seek can throw this...
867 nCenPos = -1; // make sure we return -1 to indicate an error
869 return nCenPos;
872 sal_Int32 ZipFile::recover()
873 throw(IOException, ZipException, RuntimeException)
875 ::osl::MutexGuard aGuard( m_aMutex );
877 sal_Int64 nLength;
878 Sequence < sal_Int8 > aBuffer;
879 Sequence < sal_Int32 > aHeaderOffsets;
883 nLength = aGrabber.getLength();
884 if (nLength == 0 || nLength < ENDHDR)
885 return -1;
887 aGrabber.seek( 0 );
889 const sal_Int64 nToRead = 32000;
890 for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
892 const sal_Int8 *pBuffer = aBuffer.getConstArray();
893 sal_Int32 nBufSize = aBuffer.getLength();
895 sal_Int64 nPos = 0;
896 // the buffer should contain at least one header,
897 // or if it is end of the file, at least the postheader with sizes and hash
898 while( nPos < nBufSize - 30
899 || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
902 if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
904 ZipEntry aEntry;
905 MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( ((sal_Int8*)(&(pBuffer[nPos+4]))), 26 ) );
907 aMemGrabber >> aEntry.nVersion;
908 if ( ( aEntry.nVersion & 1 ) != 1 )
910 aMemGrabber >> aEntry.nFlag;
911 aMemGrabber >> aEntry.nMethod;
913 if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
915 sal_uInt32 nCompressedSize, nSize;
917 aMemGrabber >> aEntry.nTime;
918 aMemGrabber >> aEntry.nCrc;
919 aMemGrabber >> nCompressedSize;
920 aMemGrabber >> nSize;
921 aMemGrabber >> aEntry.nPathLen;
922 aMemGrabber >> aEntry.nExtraLen;
924 // FIXME64: need to read the 64bit header instead
925 if ( nSize == 0xffffffff ||
926 nCompressedSize == 0xffffffff ) {
927 throw ZipException("PK64 zip file entry", uno::Reference < XInterface > () );
928 } else {
929 aEntry.nCompressedSize = nCompressedSize;
930 aEntry.nSize = nSize;
933 sal_Int32 nDescrLength =
934 ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
936 sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
937 sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
938 if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
939 && ( nGenPos + nPos + nBlockLength ) <= nLength )
941 // read always in UTF8, some tools seem not to set UTF8 bit
942 if( nPos + 30 + aEntry.nPathLen <= nBufSize )
943 aEntry.sPath = OUString ( (sal_Char *) &pBuffer[nPos + 30],
944 aEntry.nPathLen,
945 RTL_TEXTENCODING_UTF8 );
946 else
948 Sequence < sal_Int8 > aFileName;
949 aGrabber.seek( nGenPos + nPos + 30 );
950 aGrabber.readBytes( aFileName, aEntry.nPathLen );
951 aEntry.sPath = OUString ( (sal_Char *) aFileName.getArray(),
952 aFileName.getLength(),
953 RTL_TEXTENCODING_UTF8 );
954 aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
957 aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
959 if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
961 aEntry.nCrc = 0;
962 aEntry.nCompressedSize = 0;
963 aEntry.nSize = 0;
966 if ( aEntries.find( aEntry.sPath ) == aEntries.end() )
967 aEntries[aEntry.sPath] = aEntry;
972 nPos += 4;
974 else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
976 sal_Int32 nCRC32;
977 sal_uInt32 nCompressedSize32, nSize32;
978 sal_Int64 nCompressedSize, nSize;
979 MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( ((sal_Int8*)(&(pBuffer[nPos+4]))), 12 ) );
980 aMemGrabber >> nCRC32;
981 aMemGrabber >> nCompressedSize32;
982 aMemGrabber >> nSize32;
984 // FIXME64: work to be done here ...
985 nCompressedSize = nCompressedSize32;
986 nSize = nSize32;
988 for( EntryHash::iterator aIter = aEntries.begin(); aIter != aEntries.end(); ++aIter )
990 ZipEntry aTmp = (*aIter).second;
992 // this is a broken package, accept this block not only for DEFLATED streams
993 if( (*aIter).second.nFlag & 8 )
995 sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
996 if ( nStreamOffset == (*aIter).second.nOffset && nCompressedSize > (*aIter).second.nCompressedSize )
998 // only DEFLATED blocks need to be checked
999 sal_Bool bAcceptBlock = ( (*aIter).second.nMethod == STORED && nCompressedSize == nSize );
1001 if ( !bAcceptBlock )
1003 sal_Int64 nRealSize = 0;
1004 sal_Int32 nRealCRC = 0;
1005 getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
1006 bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
1009 if ( bAcceptBlock )
1011 (*aIter).second.nCrc = nCRC32;
1012 (*aIter).second.nCompressedSize = nCompressedSize;
1013 (*aIter).second.nSize = nSize;
1016 #if 0
1017 // for now ignore clearly broken streams
1018 else if( !(*aIter).second.nCompressedSize )
1020 (*aIter).second.nCrc = nCRC32;
1021 sal_Int32 nRealStreamSize = nGenPos + nPos - (*aIter).second.nOffset;
1022 (*aIter).second.nCompressedSize = nGenPos + nPos - (*aIter).second.nOffset;
1023 (*aIter).second.nSize = nSize;
1025 #endif
1029 nPos += 4;
1031 else
1032 nPos++;
1035 nGenPos += nPos;
1036 aGrabber.seek( nGenPos );
1039 return 0;
1041 catch ( IllegalArgumentException& )
1043 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
1045 catch ( NotConnectedException& )
1047 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
1049 catch ( BufferSizeExceededException& )
1051 throw ZipException("Zip END signature not found!", uno::Reference < XInterface > () );
1055 sal_Bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
1057 ::osl::MutexGuard aGuard( m_aMutex );
1059 sal_Int32 nCRC = 0;
1060 sal_Int64 nSize = 0;
1062 if( aEntry.nMethod == STORED )
1063 return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
1065 getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
1066 return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
1069 sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
1071 ::osl::MutexGuard aGuard( m_aMutex );
1073 Sequence < sal_Int8 > aBuffer;
1074 CRC32 aCRC;
1075 sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nSize, static_cast< sal_Int64 >( 32000 ) ) );
1077 aGrabber.seek( nOffset );
1078 for ( int ind = 0;
1079 aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
1080 ind++ )
1082 sal_Int64 nLen = ::std::min( static_cast< sal_Int64 >( nBlockSize ),
1083 nSize - ind * nBlockSize );
1084 aCRC.updateSegment( aBuffer, 0, static_cast< sal_Int32 >( nLen ) );
1087 return aCRC.getValue();
1090 void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
1092 ::osl::MutexGuard aGuard( m_aMutex );
1094 Sequence < sal_Int8 > aBuffer;
1095 CRC32 aCRC;
1096 sal_Int64 nRealSize = 0;
1097 Inflater aInflaterLocal( sal_True );
1098 sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
1100 aGrabber.seek( nOffset );
1101 for ( int ind = 0;
1102 !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
1103 ind++ )
1105 Sequence < sal_Int8 > aData( nBlockSize );
1106 sal_Int32 nLastInflated = 0;
1107 sal_Int64 nInBlock = 0;
1109 aInflaterLocal.setInput( aBuffer );
1112 nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
1113 aCRC.updateSegment( aData, 0, nLastInflated );
1114 nInBlock += nLastInflated;
1115 } while( !aInflater.finished() && nLastInflated );
1117 nRealSize += nInBlock;
1120 *nSize = nRealSize;
1121 *nCRC = aCRC.getValue();
1124 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */