xref: /core/package/source/zipapi/ZipFile.cxx (revision d0a8d4a9)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
21 #include <com/sun/star/ucb/XProgressHandler.hpp>
22 #include <com/sun/star/packages/zip/ZipConstants.hpp>
23 #include <com/sun/star/xml/crypto/XCipherContext.hpp>
24 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
25 #include <com/sun/star/xml/crypto/XCipherContextSupplier.hpp>
26 #include <com/sun/star/xml/crypto/XDigestContextSupplier.hpp>
27 #include <com/sun/star/xml/crypto/CipherID.hpp>
28 #include <com/sun/star/xml/crypto/DigestID.hpp>
29 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
30 
31 #include <comphelper/storagehelper.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <rtl/digest.h>
34 #include <sal/log.hxx>
35 #include <osl/diagnose.h>
36 
37 #include <algorithm>
38 #include <iterator>
39 #include <memory>
40 #include <vector>
41 
42 #include "blowfishcontext.hxx"
43 #include "sha1context.hxx"
44 #include <ZipFile.hxx>
45 #include <ZipEnumeration.hxx>
46 #include "XUnbufferedStream.hxx"
47 #include "XBufferedThreadedStream.hxx"
48 #include <PackageConstants.hxx>
49 #include <EncryptedDataHeader.hxx>
50 #include <EncryptionData.hxx>
51 #include "MemoryByteGrabber.hxx"
52 
53 #include <CRC32.hxx>
54 
55 using namespace com::sun::star;
56 using namespace com::sun::star::io;
57 using namespace com::sun::star::uno;
58 using namespace com::sun::star::ucb;
59 using namespace com::sun::star::lang;
60 using namespace com::sun::star::packages;
61 using namespace com::sun::star::packages::zip;
62 using namespace com::sun::star::packages::zip::ZipConstants;
63 
64 using ZipUtils::Inflater;
65 
66 #if OSL_DEBUG_LEVEL > 0
67 #define THROW_WHERE SAL_WHERE
68 #else
69 #define THROW_WHERE ""
70 #endif
71 
72 /** This class is used to read entries from a zip file
73  */
74 ZipFile::ZipFile( const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
75                   uno::Reference < XInputStream > const &xInput,
76                   const uno::Reference < XComponentContext > & rxContext,
77                   bool bInitialise )
78 : m_aMutexHolder( aMutexHolder )
79 , aGrabber( xInput )
80 , aInflater( true )
81 , xStream(xInput)
82 , m_xContext ( rxContext )
83 , bRecoveryMode( false )
84 {
85     if (bInitialise && readCEN() == -1 )
86     {
87         aEntries.clear();
88         throw ZipException( "stream data looks to be broken" );
89     }
90 }
91 
92 ZipFile::ZipFile( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
93                   uno::Reference < XInputStream > const &xInput,
94                   const uno::Reference < XComponentContext > & rxContext,
95                   bool bInitialise, bool bForceRecovery)
96 : m_aMutexHolder( aMutexHolder )
97 , aGrabber( xInput )
98 , aInflater( true )
99 , xStream(xInput)
100 , m_xContext ( rxContext )
101 , bRecoveryMode( bForceRecovery )
102 {
103     if (bInitialise)
104     {
105         if ( bForceRecovery )
106         {
107             recover();
108         }
109         else if ( readCEN() == -1 )
110         {
111             aEntries.clear();
112             throw ZipException("stream data looks to be broken" );
113         }
114     }
115 }
116 
117 ZipFile::~ZipFile()
118 {
119     aEntries.clear();
120 }
121 
122 void ZipFile::setInputStream ( const uno::Reference < XInputStream >& xNewStream )
123 {
124     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
125 
126     xStream = xNewStream;
127     aGrabber.setInputStream ( xStream );
128 }
129 
130 uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
131 {
132     uno::Reference< xml::crypto::XDigestContext > xDigestContext;
133     if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K )
134     {
135         uno::Reference< uno::XComponentContext > xContext = xArgContext;
136         if ( !xContext.is() )
137             xContext = comphelper::getProcessComponentContext();
138 
139         uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
140 
141         xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
142     }
143     else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K )
144     {
145         if (xEncryptionData->m_bTryWrongSHA1)
146         {
147             xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW);
148         }
149         else
150         {
151             xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW);
152         }
153     }
154 
155     return xDigestContext;
156 }
157 
158 uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
159 {
160     uno::Reference< xml::crypto::XCipherContext > xResult;
161 
162     if (xEncryptionData->m_nDerivedKeySize < 0)
163     {
164         throw ZipIOException("Invalid derived key length!" );
165     }
166 
167     uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
168     if ( !xEncryptionData->m_nIterationCount &&
169          xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength() )
170     {
171         // gpg4libre: no need to derive key, m_aKey is already
172         // usable as symmetric session key
173         aDerivedKey = xEncryptionData->m_aKey;
174     }
175     else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
176                         aDerivedKey.getLength(),
177                         reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
178                         xEncryptionData->m_aKey.getLength(),
179                         reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
180                         xEncryptionData->m_aSalt.getLength(),
181                         xEncryptionData->m_nIterationCount ) )
182     {
183         throw ZipIOException("Can not create derived key!" );
184     }
185 
186     if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
187     {
188         uno::Reference< uno::XComponentContext > xContext = xArgContext;
189         if ( !xContext.is() )
190             xContext = comphelper::getProcessComponentContext();
191 
192         uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
193 
194         xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
195     }
196     else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
197     {
198         xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
199     }
200     else
201     {
202         throw ZipIOException("Unknown cipher algorithm is requested!" );
203     }
204 
205     return xResult;
206 }
207 
208 void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
209                                 sal_Int64 nSize,
210                                 const OUString& aMediaType,
211                                 sal_Int8 * & pHeader )
212 {
213     // I think it's safe to restrict vector and salt length to 2 bytes !
214     sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
215     sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
216     sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
217     sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
218 
219     // First the header
220     *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
221     *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
222     *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
223     *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
224 
225     // Then the version
226     *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
227     *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
228 
229     // Then the iteration Count
230     sal_Int32 nIterationCount = rData->m_nIterationCount;
231     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
232     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
233     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
234     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
235 
236     // FIXME64: need to handle larger sizes
237     // Then the size:
238     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
239     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
240     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
241     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
242 
243     // Then the encryption algorithm
244     sal_Int32 nEncAlgID = rData->m_nEncAlg;
245     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
246     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
247     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
248     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
249 
250     // Then the checksum algorithm
251     sal_Int32 nChecksumAlgID = rData->m_nCheckAlg;
252     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
253     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
254     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
255     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
256 
257     // Then the derived key size
258     sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
259     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
260     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
261     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
262     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
263 
264     // Then the start key generation algorithm
265     sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
266     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
267     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
268     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
269     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
270 
271     // Then the salt length
272     *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
273     *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
274 
275     // Then the IV length
276     *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
277     *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
278 
279     // Then the digest length
280     *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
281     *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
282 
283     // Then the mediatype length
284     *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
285     *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
286 
287     // Then the salt content
288     memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
289     pHeader += nSaltLength;
290 
291     // Then the IV content
292     memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
293     pHeader += nIVLength;
294 
295     // Then the digest content
296     memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
297     pHeader += nDigestLength;
298 
299     // Then the mediatype itself
300     memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
301     pHeader += nMediaTypeLength;
302 }
303 
304 bool ZipFile::StaticFillData (  ::rtl::Reference< BaseEncryptionData > const & rData,
305                                     sal_Int32 &rEncAlg,
306                                     sal_Int32 &rChecksumAlg,
307                                     sal_Int32 &rDerivedKeySize,
308                                     sal_Int32 &rStartKeyGenID,
309                                     sal_Int32 &rSize,
310                                     OUString& aMediaType,
311                                     const uno::Reference< XInputStream >& rStream )
312 {
313     bool bOk = false;
314     const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
315     Sequence < sal_Int8 > aBuffer ( nHeaderSize );
316     if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
317     {
318         sal_Int16 nPos = 0;
319         sal_Int8 *pBuffer = aBuffer.getArray();
320         sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
321         nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
322         if ( nVersion == n_ConstCurrentVersion )
323         {
324             sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
325             nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
326             nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
327             nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
328             rData->m_nIterationCount = nCount;
329 
330             rSize  =   pBuffer[nPos++] & 0xFF;
331             rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
332             rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
333             rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
334 
335             rEncAlg   =   pBuffer[nPos++] & 0xFF;
336             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
337             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
338             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
339 
340             rChecksumAlg   =   pBuffer[nPos++] & 0xFF;
341             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
342             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
343             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
344 
345             rDerivedKeySize   =   pBuffer[nPos++] & 0xFF;
346             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 8;
347             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 16;
348             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 24;
349 
350             rStartKeyGenID   =   pBuffer[nPos++] & 0xFF;
351             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 8;
352             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 16;
353             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 24;
354 
355             sal_Int16 nSaltLength =   pBuffer[nPos++] & 0xFF;
356             nSaltLength          |= ( pBuffer[nPos++] & 0xFF ) << 8;
357             sal_Int16 nIVLength   = ( pBuffer[nPos++] & 0xFF );
358             nIVLength            |= ( pBuffer[nPos++] & 0xFF ) << 8;
359             sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
360             nDigestLength        |= ( pBuffer[nPos++] & 0xFF ) << 8;
361 
362             sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
363             nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
364 
365             if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
366             {
367                 rData->m_aSalt.realloc ( nSaltLength );
368                 memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
369                 if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
370                 {
371                     rData->m_aInitVector.realloc ( nIVLength );
372                     memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
373                     if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
374                     {
375                         rData->m_aDigest.realloc ( nDigestLength );
376                         memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
377 
378                         if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
379                         {
380                             aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
381                                                             nMediaTypeLength / sizeof( sal_Unicode ) );
382                             bOk = true;
383                         }
384                     }
385                 }
386             }
387         }
388     }
389     return bOk;
390 }
391 
392 uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
393                                                                 const uno::Reference< uno::XComponentContext >& rxContext,
394                                                                 const uno::Reference< XInputStream >& xStream,
395                                                                 const ::rtl::Reference< EncryptionData > &rData )
396 {
397     if ( !rData.is() )
398         throw ZipIOException("Encrypted stream without encryption data!" );
399 
400     if ( !rData->m_aKey.hasElements() )
401         throw packages::WrongPasswordException(THROW_WHERE );
402 
403     uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY );
404     if ( !xSeek.is() )
405         throw ZipIOException("The stream must be seekable!" );
406 
407     // if we have a digest, then this file is an encrypted one and we should
408     // check if we can decrypt it or not
409     OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" );
410     if ( rData->m_aDigest.hasElements() )
411     {
412         sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() );
413         if ( nSize > n_ConstDigestLength + 32 )
414             nSize = n_ConstDigestLength + 32;
415 
416         // skip header
417         xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() +
418                                 rData->m_aSalt.getLength() + rData->m_aDigest.getLength() );
419 
420         // Only want to read enough to verify the digest
421         Sequence < sal_Int8 > aReadBuffer ( nSize );
422 
423         xStream->readBytes( aReadBuffer, nSize );
424 
425         if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) )
426             throw packages::WrongPasswordException(THROW_WHERE );
427     }
428 
429     return new XUnbufferedStream( aMutexHolder, xStream, rData );
430 }
431 
432 #if 0
433 // for debugging purposes
434 void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
435 {
436     if ( aSequence.getLength() )
437     {
438         sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
439         sal_Int32 nSize = *( pPointer + 1 );
440         sal_Int32 nMemSize = *( pPointer - 2 );
441         sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
442         OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
443     }
444 }
445 #endif
446 
447 bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
448 {
449     if ( !rData.is() || !rData->m_aKey.hasElements() )
450         return false;
451 
452     bool bRet = false;
453 
454     uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
455 
456     uno::Sequence< sal_Int8 > aDecryptBuffer;
457     uno::Sequence< sal_Int8 > aDecryptBuffer2;
458     try
459     {
460         aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
461         aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
462     }
463     catch( uno::Exception& )
464     {
465         // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
466         // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
467     }
468 
469     if ( aDecryptBuffer2.hasElements() )
470     {
471         sal_Int32 nOldLen = aDecryptBuffer.getLength();
472         aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
473         memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getConstArray(), aDecryptBuffer2.getLength() );
474     }
475 
476     if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
477         aDecryptBuffer.realloc( n_ConstDigestLength );
478 
479     uno::Sequence< sal_Int8 > aDigestSeq;
480     uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
481 
482     xDigestContext->updateDigest( aDecryptBuffer );
483     aDigestSeq = xDigestContext->finalizeDigestAndDispose();
484 
485     // If we don't have a digest, then we have to assume that the password is correct
486     if (  rData->m_aDigest.hasElements() &&
487           ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
488             0 != memcmp ( aDigestSeq.getConstArray(),
489                                      rData->m_aDigest.getConstArray(),
490                                     aDigestSeq.getLength() ) ) )
491     {
492         // We should probably tell the user that the password they entered was wrong
493     }
494     else
495         bRet = true;
496 
497     return bRet;
498 }
499 
500 bool ZipFile::hasValidPassword ( ZipEntry const & rEntry, const ::rtl::Reference< EncryptionData >& rData )
501 {
502     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
503 
504     bool bRet = false;
505     if ( rData.is() && rData->m_aKey.hasElements() )
506     {
507         css::uno::Reference < css::io::XSeekable > xSeek(xStream, UNO_QUERY_THROW);
508         xSeek->seek( rEntry.nOffset );
509         sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
510 
511         // Only want to read enough to verify the digest
512         if ( nSize > n_ConstDigestDecrypt )
513             nSize = n_ConstDigestDecrypt;
514 
515         Sequence < sal_Int8 > aReadBuffer ( nSize );
516 
517         xStream->readBytes( aReadBuffer, nSize );
518 
519         bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData );
520     }
521 
522     return bRet;
523 }
524 
525 namespace {
526 
527 class XBufferedStream : public cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable>
528 {
529     std::vector<sal_Int8> maBytes;
530     size_t mnPos;
531 
532     size_t remainingSize() const
533     {
534         return maBytes.size() - mnPos;
535     }
536 
537     bool hasBytes() const
538     {
539         return mnPos < maBytes.size();
540     }
541 
542 public:
543     XBufferedStream( const uno::Reference<XInputStream>& xSrcStream ) : mnPos(0)
544     {
545         const sal_Int32 nBufSize = 8192;
546 
547         sal_Int32 nRemaining = xSrcStream->available();
548         sal_Int32 nRead = 0;
549         maBytes.reserve(nRemaining);
550         uno::Sequence<sal_Int8> aBuf(nBufSize);
551 
552         auto readAndCopy = [&]( sal_Int32 nReadSize ) -> sal_Int32
553         {
554             sal_Int32 nBytes = xSrcStream->readBytes(aBuf, nReadSize);
555             const sal_Int8* p = aBuf.getConstArray();
556             const sal_Int8* pEnd = p + nBytes;
557             maBytes.insert( maBytes.end(), p, pEnd );
558             return nBytes;
559         };
560 
561         while (nRemaining > nBufSize)
562         {
563             const auto nBytes = readAndCopy(nBufSize);
564             if (!nBytes)
565                 break;
566             nRead += nBytes;
567             nRemaining -= nBytes;
568         }
569 
570         if (nRemaining)
571             nRead += readAndCopy(nRemaining);
572         maBytes.resize(nRead);
573     }
574 
575     virtual sal_Int32 SAL_CALL readBytes( uno::Sequence<sal_Int8>& rData, sal_Int32 nBytesToRead ) override
576     {
577         if (!hasBytes())
578             return 0;
579 
580         sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize());
581         rData.realloc(nReadSize);
582         auto pData = rData.getArray();
583         std::vector<sal_Int8>::const_iterator it = maBytes.cbegin();
584         std::advance(it, mnPos);
585         for (sal_Int32 i = 0; i < nReadSize; ++i, ++it)
586             pData[i] = *it;
587 
588         mnPos += nReadSize;
589 
590         return nReadSize;
591     }
592 
593     virtual sal_Int32 SAL_CALL readSomeBytes( ::css::uno::Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead ) override
594     {
595         return readBytes(rData, nMaxBytesToRead);
596     }
597 
598     virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override
599     {
600         if (!hasBytes())
601             return;
602 
603         mnPos += nBytesToSkip;
604     }
605 
606     virtual sal_Int32 SAL_CALL available() override
607     {
608         if (!hasBytes())
609             return 0;
610 
611         return remainingSize();
612     }
613 
614     virtual void SAL_CALL closeInput() override
615     {
616     }
617     // XSeekable
618     virtual void SAL_CALL seek( sal_Int64 location ) override
619     {
620         if ( location > sal_Int64(maBytes.size()) || location < 0 )
621             throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
622         mnPos = location;
623     }
624     virtual sal_Int64 SAL_CALL getPosition() override
625     {
626         return mnPos;
627     }
628     virtual sal_Int64 SAL_CALL getLength() override
629     {
630         return maBytes.size();
631     }
632 };
633 
634 }
635 
636 uno::Reference< XInputStream > ZipFile::createStreamForZipEntry(
637             const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
638             ZipEntry const & rEntry,
639             const ::rtl::Reference< EncryptionData > &rData,
640             sal_Int8 nStreamMode,
641             bool bIsEncrypted,
642             const bool bUseBufferedStream,
643             const OUString& aMediaType )
644 {
645     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
646 
647     rtl::Reference< XUnbufferedStream > xSrcStream = new XUnbufferedStream(
648         m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode);
649 
650     if (!bUseBufferedStream)
651         return xSrcStream;
652 
653     uno::Reference<io::XInputStream> xBufStream;
654     static const sal_Int32 nThreadingThreshold = 10000;
655 
656     if( xSrcStream->available() > nThreadingThreshold )
657         xBufStream = new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize());
658     else
659         xBufStream = new XBufferedStream(xSrcStream);
660 
661     return xBufStream;
662 }
663 
664 ZipEnumeration ZipFile::entries()
665 {
666     return aEntries;
667 }
668 
669 uno::Reference< XInputStream > ZipFile::getInputStream( ZipEntry& rEntry,
670         const ::rtl::Reference< EncryptionData > &rData,
671         bool bIsEncrypted,
672         const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
673 {
674     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
675 
676     if ( rEntry.nOffset <= 0 )
677         readLOC( rEntry );
678 
679     // We want to return a rawStream if we either don't have a key or if the
680     // key is wrong
681 
682     bool bNeedRawStream = rEntry.nMethod == STORED;
683 
684     // if we have a digest, then this file is an encrypted one and we should
685     // check if we can decrypt it or not
686     if ( bIsEncrypted && rData.is() && rData->m_aDigest.hasElements() )
687         bNeedRawStream = !hasValidPassword ( rEntry, rData );
688 
689     return createStreamForZipEntry ( aMutexHolder,
690                                     rEntry,
691                                     rData,
692                                     bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
693                                     bIsEncrypted );
694 }
695 
696 uno::Reference< XInputStream > ZipFile::getDataStream( ZipEntry& rEntry,
697         const ::rtl::Reference< EncryptionData > &rData,
698         bool bIsEncrypted,
699         const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
700 {
701     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
702 
703     if ( rEntry.nOffset <= 0 )
704         readLOC( rEntry );
705 
706     // An exception must be thrown in case stream is encrypted and
707     // there is no key or the key is wrong
708     bool bNeedRawStream = false;
709     if ( bIsEncrypted )
710     {
711         // in case no digest is provided there is no way
712         // to detect password correctness
713         if ( !rData.is() )
714             throw ZipException("Encrypted stream without encryption data!" );
715 
716         // if we have a digest, then this file is an encrypted one and we should
717         // check if we can decrypt it or not
718         OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" );
719         if ( rData->m_aDigest.hasElements() && !hasValidPassword ( rEntry, rData ) )
720                 throw packages::WrongPasswordException(THROW_WHERE );
721     }
722     else
723         bNeedRawStream = ( rEntry.nMethod == STORED );
724 
725     return createStreamForZipEntry ( aMutexHolder,
726                                     rEntry,
727                                     rData,
728                                     bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
729                                     bIsEncrypted );
730 }
731 
732 uno::Reference< XInputStream > ZipFile::getRawData( ZipEntry& rEntry,
733         const ::rtl::Reference< EncryptionData >& rData,
734         bool bIsEncrypted,
735         const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
736         const bool bUseBufferedStream )
737 {
738     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
739 
740     if ( rEntry.nOffset <= 0 )
741         readLOC( rEntry );
742 
743     return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted, bUseBufferedStream );
744 }
745 
746 uno::Reference< XInputStream > ZipFile::getWrappedRawStream(
747         ZipEntry& rEntry,
748         const ::rtl::Reference< EncryptionData >& rData,
749         const OUString& aMediaType,
750         const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
751 {
752     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
753 
754     if ( !rData.is() )
755         throw packages::NoEncryptionException(THROW_WHERE );
756 
757     if ( rEntry.nOffset <= 0 )
758         readLOC( rEntry );
759 
760     return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, true, aMediaType );
761 }
762 
763 void ZipFile::readLOC( ZipEntry &rEntry )
764 {
765     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
766 
767     sal_Int64 nPos = -rEntry.nOffset;
768 
769     aGrabber.seek(nPos);
770     sal_Int32 nTestSig = aGrabber.ReadInt32();
771     if (nTestSig != LOCSIG)
772         throw ZipIOException("Invalid LOC header (bad signature)" );
773 
774     // Ignore all (duplicated) information from the local file header.
775     // various programs produced "broken" zip files; even LO at some point.
776     // Just verify the path and calculate the data offset and otherwise
777     // rely on the central directory info.
778 
779     aGrabber.ReadInt16(); //version
780     aGrabber.ReadInt16(); //flag
781     aGrabber.ReadInt16(); //how
782     aGrabber.ReadInt32(); //time
783     aGrabber.ReadInt32(); //crc
784     aGrabber.ReadInt32(); //compressed size
785     aGrabber.ReadInt32(); //size
786     sal_Int16 nPathLen = aGrabber.ReadInt16();
787     sal_Int16 nExtraLen = aGrabber.ReadInt16();
788     rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
789 
790     // FIXME64: need to read 64bit LOC
791 
792     bool bBroken = false;
793 
794     try
795     {
796         sal_Int16 nPathLenToRead = nPathLen;
797         const sal_Int64 nBytesAvailable = aGrabber.getLength() - aGrabber.getPosition();
798         if (nPathLenToRead > nBytesAvailable)
799             nPathLenToRead = nBytesAvailable;
800         else if (nPathLenToRead < 0)
801             nPathLenToRead = 0;
802 
803         // read always in UTF8, some tools seem not to set UTF8 bit
804         uno::Sequence<sal_Int8> aNameBuffer(nPathLenToRead);
805         sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLenToRead);
806         if (nRead < aNameBuffer.getLength())
807             aNameBuffer.realloc(nRead);
808 
809         OUString sLOCPath( reinterpret_cast<const char *>(aNameBuffer.getConstArray()),
810                            aNameBuffer.getLength(),
811                            RTL_TEXTENCODING_UTF8 );
812 
813         if ( rEntry.nPathLen == -1 ) // the file was created
814         {
815             rEntry.nPathLen = nPathLen;
816             rEntry.sPath = sLOCPath;
817         }
818 
819         bBroken = rEntry.nPathLen != nPathLen
820                         || rEntry.sPath != sLOCPath;
821     }
822     catch(...)
823     {
824         bBroken = true;
825     }
826 
827     if ( bBroken && !bRecoveryMode )
828         throw ZipIOException("The stream seems to be broken!" );
829 }
830 
831 sal_Int32 ZipFile::findEND()
832 {
833     // this method is called in constructor only, no need for mutex
834     sal_Int32 nPos, nEnd;
835     Sequence < sal_Int8 > aBuffer;
836     try
837     {
838         sal_Int32 nLength = static_cast <sal_Int32 > (aGrabber.getLength());
839         if (nLength < ENDHDR)
840             return -1;
841         nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
842         nEnd = nPos >= 0 ? nPos : 0 ;
843 
844         aGrabber.seek( nEnd );
845 
846         auto nSize = nLength - nEnd;
847         if (nSize != aGrabber.readBytes(aBuffer, nSize))
848             throw ZipException("Zip END signature not found!" );
849 
850         const sal_Int8 *pBuffer = aBuffer.getConstArray();
851 
852         nPos = nSize - ENDHDR;
853         while ( nPos >= 0 )
854         {
855             if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
856                 return nPos + nEnd;
857             nPos--;
858         }
859     }
860     catch ( IllegalArgumentException& )
861     {
862         throw ZipException("Zip END signature not found!" );
863     }
864     catch ( NotConnectedException& )
865     {
866         throw ZipException("Zip END signature not found!" );
867     }
868     catch ( BufferSizeExceededException& )
869     {
870         throw ZipException("Zip END signature not found!" );
871     }
872     throw ZipException("Zip END signature not found!" );
873 }
874 
875 sal_Int32 ZipFile::readCEN()
876 {
877     // this method is called in constructor only, no need for mutex
878     sal_Int32 nCenPos = -1, nLocPos;
879     sal_uInt16 nCount;
880 
881     try
882     {
883         sal_Int32 nEndPos = findEND();
884         if (nEndPos == -1)
885             return -1;
886         aGrabber.seek(nEndPos + ENDTOT);
887         sal_uInt16 nTotal = aGrabber.ReadUInt16();
888         sal_Int32 nCenLen = aGrabber.ReadInt32();
889         sal_Int32 nCenOff = aGrabber.ReadInt32();
890 
891         if ( nTotal * CENHDR > nCenLen )
892             throw ZipException("invalid END header (bad entry count)" );
893 
894         if ( nTotal > ZIP_MAXENTRIES )
895             throw ZipException("too many entries in ZIP File" );
896 
897         if ( nCenLen < 0 || nCenLen > nEndPos )
898             throw ZipException("Invalid END header (bad central directory size)" );
899 
900         nCenPos = nEndPos - nCenLen;
901 
902         if ( nCenOff < 0 || nCenOff > nCenPos )
903             throw ZipException("Invalid END header (bad central directory size)" );
904 
905         nLocPos = nCenPos - nCenOff;
906         aGrabber.seek( nCenPos );
907         Sequence < sal_Int8 > aCENBuffer ( nCenLen );
908         sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
909         if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
910             throw ZipException ("Error reading CEN into memory buffer!" );
911 
912         MemoryByteGrabber aMemGrabber(aCENBuffer);
913 
914         ZipEntry aEntry;
915         sal_Int16 nCommentLen;
916 
917         for (nCount = 0 ; nCount < nTotal; nCount++)
918         {
919             sal_Int32 nTestSig = aMemGrabber.ReadInt32();
920             if ( nTestSig != CENSIG )
921                 throw ZipException("Invalid CEN header (bad signature)" );
922 
923             aMemGrabber.skipBytes ( 2 );
924             aEntry.nVersion = aMemGrabber.ReadInt16();
925             aEntry.nFlag = aMemGrabber.ReadInt16();
926 
927             if ( ( aEntry.nFlag & 1 ) == 1 )
928                 throw ZipException("Invalid CEN header (encrypted entry)" );
929 
930             aEntry.nMethod = aMemGrabber.ReadInt16();
931 
932             if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
933                 throw ZipException("Invalid CEN header (bad compression method)" );
934 
935             aEntry.nTime = aMemGrabber.ReadInt32();
936             aEntry.nCrc = aMemGrabber.ReadInt32();
937 
938             sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
939             sal_uInt32 nSize = aMemGrabber.ReadUInt32();
940             aEntry.nPathLen = aMemGrabber.ReadInt16();
941             aEntry.nExtraLen = aMemGrabber.ReadInt16();
942             nCommentLen = aMemGrabber.ReadInt16();
943             aMemGrabber.skipBytes ( 8 );
944             sal_uInt32 nOffset = aMemGrabber.ReadUInt32();
945 
946             // FIXME64: need to read the 64bit header instead
947             if ( nSize == 0xffffffff ||
948                  nOffset == 0xffffffff ||
949                  nCompressedSize == 0xffffffff ) {
950                 throw ZipException("PK64 zip file entry" );
951             }
952             aEntry.nCompressedSize = nCompressedSize;
953             aEntry.nSize = nSize;
954             aEntry.nOffset = nOffset;
955 
956             aEntry.nOffset += nLocPos;
957             aEntry.nOffset *= -1;
958 
959             if ( aEntry.nPathLen < 0 )
960                 throw ZipException("unexpected name length" );
961 
962             if ( nCommentLen < 0 )
963                 throw ZipException("unexpected comment length" );
964 
965             if ( aEntry.nExtraLen < 0 )
966                 throw ZipException("unexpected extra header info length" );
967 
968             if (aEntry.nPathLen > aMemGrabber.remainingSize())
969                 throw ZipException("name too long");
970 
971             // read always in UTF8, some tools seem not to set UTF8 bit
972             aEntry.sPath = OUString( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
973                                      aEntry.nPathLen,
974                                      RTL_TEXTENCODING_UTF8 );
975 
976             if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
977                 throw ZipException("Zip entry has an invalid name." );
978 
979             aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
980             aEntries[aEntry.sPath] = aEntry;
981         }
982 
983         if (nCount != nTotal)
984             throw ZipException("Count != Total" );
985     }
986     catch ( IllegalArgumentException & )
987     {
988         // seek can throw this...
989         nCenPos = -1; // make sure we return -1 to indicate an error
990     }
991     return nCenPos;
992 }
993 
994 void ZipFile::recover()
995 {
996     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
997 
998     sal_Int64 nLength;
999     Sequence < sal_Int8 > aBuffer;
1000 
1001     try
1002     {
1003         nLength = aGrabber.getLength();
1004         if (nLength < ENDHDR)
1005             return;
1006 
1007         aGrabber.seek( 0 );
1008 
1009         const sal_Int64 nToRead = 32000;
1010         for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
1011         {
1012             const sal_Int8 *pBuffer = aBuffer.getConstArray();
1013             sal_Int32 nBufSize = aBuffer.getLength();
1014 
1015             sal_Int64 nPos = 0;
1016             // the buffer should contain at least one header,
1017             // or if it is end of the file, at least the postheader with sizes and hash
1018             while( nPos < nBufSize - 30
1019                 || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
1020 
1021             {
1022                 if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
1023                 {
1024                     ZipEntry aEntry;
1025                     Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 26);
1026                     MemoryByteGrabber aMemGrabber(aTmpBuffer);
1027 
1028                     aEntry.nVersion = aMemGrabber.ReadInt16();
1029                     aEntry.nFlag = aMemGrabber.ReadInt16();
1030 
1031                     if ( ( aEntry.nFlag & 1 ) != 1 )
1032                     {
1033                         aEntry.nMethod = aMemGrabber.ReadInt16();
1034 
1035                         if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
1036                         {
1037                             aEntry.nTime = aMemGrabber.ReadInt32();
1038                             aEntry.nCrc = aMemGrabber.ReadInt32();
1039                             sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
1040                             sal_uInt32 nSize = aMemGrabber.ReadUInt32();
1041                             aEntry.nPathLen = aMemGrabber.ReadInt16();
1042                             aEntry.nExtraLen = aMemGrabber.ReadInt16();
1043 
1044                             // FIXME64: need to read the 64bit header instead
1045                             if ( nSize == 0xffffffff ||
1046                                  nCompressedSize == 0xffffffff ) {
1047                                 throw ZipException("PK64 zip file entry" );
1048                             }
1049                             aEntry.nCompressedSize = nCompressedSize;
1050                             aEntry.nSize = nSize;
1051 
1052                             sal_Int32 nDescrLength =
1053                                 ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
1054 
1055                             sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
1056                             sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
1057                             if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
1058                                 && ( nGenPos + nPos + nBlockLength ) <= nLength )
1059                             {
1060                                 // read always in UTF8, some tools seem not to set UTF8 bit
1061                                 if( nPos + 30 + aEntry.nPathLen <= nBufSize )
1062                                     aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]),
1063                                                               aEntry.nPathLen,
1064                                                               RTL_TEXTENCODING_UTF8 );
1065                                 else
1066                                 {
1067                                     Sequence < sal_Int8 > aFileName;
1068                                     aGrabber.seek( nGenPos + nPos + 30 );
1069                                     aGrabber.readBytes( aFileName, aEntry.nPathLen );
1070                                     aEntry.sPath = OUString ( reinterpret_cast<const char *>(aFileName.getConstArray()),
1071                                                               aFileName.getLength(),
1072                                                               RTL_TEXTENCODING_UTF8 );
1073                                     aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
1074                                 }
1075 
1076                                 aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
1077 
1078                                 if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
1079                                 {
1080                                     aEntry.nCrc = 0;
1081                                     aEntry.nCompressedSize = 0;
1082                                     aEntry.nSize = 0;
1083                                 }
1084 
1085                                 aEntries.emplace( aEntry.sPath, aEntry );
1086                             }
1087                         }
1088                     }
1089 
1090                     nPos += 4;
1091                 }
1092                 else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
1093                 {
1094                     sal_Int64 nCompressedSize, nSize;
1095                     Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 12);
1096                     MemoryByteGrabber aMemGrabber(aTmpBuffer);
1097                     sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
1098                     sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32();
1099                     sal_uInt32 nSize32 = aMemGrabber.ReadUInt32();
1100 
1101                     // FIXME64: work to be done here ...
1102                     nCompressedSize = nCompressedSize32;
1103                     nSize = nSize32;
1104 
1105                     for( auto& rEntry : aEntries )
1106                     {
1107                         // this is a broken package, accept this block not only for DEFLATED streams
1108                         if( rEntry.second.nFlag & 8 )
1109                         {
1110                             sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
1111                             if ( nStreamOffset == rEntry.second.nOffset && nCompressedSize > rEntry.second.nCompressedSize )
1112                             {
1113                                 // only DEFLATED blocks need to be checked
1114                                 bool bAcceptBlock = ( rEntry.second.nMethod == STORED && nCompressedSize == nSize );
1115 
1116                                 if ( !bAcceptBlock )
1117                                 {
1118                                     sal_Int64 nRealSize = 0;
1119                                     sal_Int32 nRealCRC = 0;
1120                                     getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
1121                                     bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
1122                                 }
1123 
1124                                 if ( bAcceptBlock )
1125                                 {
1126                                     rEntry.second.nCrc = nCRC32;
1127                                     rEntry.second.nCompressedSize = nCompressedSize;
1128                                     rEntry.second.nSize = nSize;
1129                                 }
1130                             }
1131 #if 0
1132 // for now ignore clearly broken streams
1133                             else if( !rEntry.second.nCompressedSize )
1134                             {
1135                                 rEntry.second.nCrc = nCRC32;
1136                                 sal_Int32 nRealStreamSize = nGenPos + nPos - rEntry.second.nOffset;
1137                                 rEntry.second.nCompressedSize = nRealStreamSize;
1138                                 rEntry.second.nSize = nSize;
1139                             }
1140 #endif
1141                         }
1142                     }
1143 
1144                     nPos += 4;
1145                 }
1146                 else
1147                     nPos++;
1148             }
1149 
1150             nGenPos += nPos;
1151             aGrabber.seek( nGenPos );
1152         }
1153     }
1154     catch ( IllegalArgumentException& )
1155     {
1156         throw ZipException("Zip END signature not found!" );
1157     }
1158     catch ( NotConnectedException& )
1159     {
1160         throw ZipException("Zip END signature not found!" );
1161     }
1162     catch ( BufferSizeExceededException& )
1163     {
1164         throw ZipException("Zip END signature not found!" );
1165     }
1166 }
1167 
1168 bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
1169 {
1170     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1171 
1172     sal_Int32 nCRC = 0;
1173     sal_Int64 nSize = 0;
1174 
1175     if( aEntry.nMethod == STORED )
1176         return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
1177 
1178     getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
1179     return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
1180 }
1181 
1182 sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
1183 {
1184     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1185 
1186     Sequence < sal_Int8 > aBuffer;
1187     CRC32 aCRC;
1188     sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
1189 
1190     aGrabber.seek( nOffset );
1191     for (sal_Int64 ind = 0;
1192          aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
1193          ++ind)
1194     {
1195         sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize);
1196         aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen));
1197     }
1198 
1199     return aCRC.getValue();
1200 }
1201 
1202 void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
1203 {
1204     ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1205 
1206     Sequence < sal_Int8 > aBuffer;
1207     CRC32 aCRC;
1208     sal_Int64 nRealSize = 0;
1209     Inflater aInflaterLocal( true );
1210     sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
1211 
1212     aGrabber.seek( nOffset );
1213     for ( sal_Int64 ind = 0;
1214           !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
1215           ind++ )
1216     {
1217         Sequence < sal_Int8 > aData( nBlockSize );
1218         sal_Int32 nLastInflated = 0;
1219         sal_Int64 nInBlock = 0;
1220 
1221         aInflaterLocal.setInput( aBuffer );
1222         do
1223         {
1224             nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
1225             aCRC.updateSegment( aData, nLastInflated );
1226             nInBlock += nLastInflated;
1227         } while( !aInflater.finished() && nLastInflated );
1228 
1229         nRealSize += nInBlock;
1230     }
1231 
1232     *nSize = nRealSize;
1233     *nCRC = aCRC.getValue();
1234 }
1235 
1236 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1237