1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
)
66 , aInflater (sal_True
)
68 , xSeek(xInput
, UNO_QUERY
)
69 , m_xContext ( rxContext
)
70 , bRecoveryMode( sal_False
)
74 if ( readCEN() == -1 )
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
)
87 , aInflater (sal_True
)
89 , xSeek(xInput
, UNO_QUERY
)
90 , m_xContext ( rxContext
)
91 , xProgressHandler( xProgress
)
92 , bRecoveryMode( bForceRecovery
)
100 else if ( readCEN() == -1 )
103 throw ZipException("stream data looks to be broken", uno::Reference
< XInterface
> () );
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
);
182 throw ZipIOException("Unknown cipher algorithm is requested!",
183 uno::Reference
< XInterface
>() );
188 OSL_ENSURE( sal_False
, "Can not create cipher context!" );
194 void ZipFile::StaticFillHeader( const ::rtl::Reference
< EncryptionData
>& rData
,
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
) );
206 *(pHeader
++) = ( n_ConstHeader
>> 0 ) & 0xFF;
207 *(pHeader
++) = ( n_ConstHeader
>> 8 ) & 0xFF;
208 *(pHeader
++) = ( n_ConstHeader
>> 16 ) & 0xFF;
209 *(pHeader
++) = ( n_ConstHeader
>> 24 ) & 0xFF;
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
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
,
292 sal_Int32
&rChecksumAlg
,
293 sal_Int32
&rDerivedKeySize
,
294 sal_Int32
&rStartKeyGenID
,
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
) )
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
) );
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
)
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
);
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;
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
);
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!" );
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() )
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
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
);
513 uno::Reference
< XInputStream
> ZipFile::createUnbufferedStream(
514 SotMutexHolderRef aMutexHolder
,
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 )
543 // We want to return a rawStream if we either don't have a key or if the
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
,
556 bNeedRawStream
? UNBUFF_STREAM_RAW
: UNBUFF_STREAM_DATA
,
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
,
569 ::osl::MutexGuard
aGuard( m_aMutex
);
571 if ( rEntry
.nOffset
<= 0 )
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
;
579 // in case no digest is provided there is no way
580 // to detect password correctness
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
>() );
592 bNeedRawStream
= ( rEntry
.nMethod
== STORED
);
594 return createUnbufferedStream ( aMutexHolder
,
597 bNeedRawStream
? UNBUFF_STREAM_RAW
: UNBUFF_STREAM_DATA
,
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 )
612 return createUnbufferedStream ( aMutexHolder
, rEntry
, rData
, UNBUFF_STREAM_RAW
, bIsEncrypted
);
615 uno::Reference
< XInputStream
> SAL_CALL
ZipFile::getWrappedRawStream(
617 const ::rtl::Reference
< EncryptionData
>& rData
,
618 const OUString
& aMediaType
,
619 SotMutexHolderRef aMutexHolder
)
620 throw ( packages::NoEncryptionException
,
625 ::osl::MutexGuard
aGuard( m_aMutex
);
628 throw packages::NoEncryptionException(OSL_LOG_PREFIX
, uno::Reference
< uno::XInterface
>() );
630 if ( rEntry
.nOffset
<= 0 )
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
;
646 aGrabber
>> nTestSig
;
648 if (nTestSig
!= LOCSIG
)
649 throw ZipIOException("Invalid LOC header (bad signature)", uno::Reference
< XInterface
> () );
650 aGrabber
>> nVersion
;
655 aGrabber
>> nCompressedSize
;
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
687 // Do *not* compare nMethod / nHow, older versions with
688 // encrypted streams write mismatching DEFLATE/STORE pairs
690 // Do *not* compare timestamps, since MSO 2010 can produce documents
691 // with timestamp difference in the central directory entry and local
693 bBroken
= rEntry
.nVersion
!= nVersion
694 || rEntry
.nPathLen
!= nPathLen
695 || !rEntry
.sPath
.equals( sLOCPath
);
702 if ( bBroken
&& !bRecoveryMode
)
703 throw ZipIOException("The stream seems to be broken!",
704 uno::Reference
< XInterface
>() );
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
)
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
;
731 if (pBuffer
[nPos
] == 'P' && pBuffer
[nPos
+1] == 'K' && pBuffer
[nPos
+2] == 5 && pBuffer
[nPos
+3] == 6 )
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
;
763 aGrabber
.seek(nEndPos
+ ENDTOT
);
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
);
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
> () );
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(),
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
872 sal_Int32
ZipFile::recover()
873 throw(IOException
, ZipException
, RuntimeException
)
875 ::osl::MutexGuard
aGuard( m_aMutex
);
878 Sequence
< sal_Int8
> aBuffer
;
879 Sequence
< sal_Int32
> aHeaderOffsets
;
883 nLength
= aGrabber
.getLength();
884 if (nLength
== 0 || nLength
< ENDHDR
)
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();
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 )
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
> () );
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],
945 RTL_TEXTENCODING_UTF8
);
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
) )
962 aEntry
.nCompressedSize
= 0;
966 if ( aEntries
.find( aEntry
.sPath
) == aEntries
.end() )
967 aEntries
[aEntry
.sPath
] = aEntry
;
974 else if (pBuffer
[nPos
] == 'P' && pBuffer
[nPos
+1] == 'K' && pBuffer
[nPos
+2] == 7 && pBuffer
[nPos
+3] == 8 )
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
;
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
);
1011 (*aIter
).second
.nCrc
= nCRC32
;
1012 (*aIter
).second
.nCompressedSize
= nCompressedSize
;
1013 (*aIter
).second
.nSize
= nSize
;
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
;
1036 aGrabber
.seek( nGenPos
);
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
);
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
;
1075 sal_Int32 nBlockSize
= static_cast< sal_Int32
> (::std::min( nSize
, static_cast< sal_Int64
>( 32000 ) ) );
1077 aGrabber
.seek( nOffset
);
1079 aGrabber
.readBytes( aBuffer
, nBlockSize
) && ind
* nBlockSize
< nSize
;
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
;
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
);
1102 !aInflaterLocal
.finished() && aGrabber
.readBytes( aBuffer
, nBlockSize
) && ind
* nBlockSize
< nCompressedSize
;
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
;
1121 *nCRC
= aCRC
.getValue();
1124 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */