Bump version to 5.0-14
[LibreOffice.git] / package / source / zipapi / ZipFile.cxx
blob84774b2253f88de4b8c5fd88ed8099b7d118134b
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 .
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>
31 #include <comphelper/storagehelper.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <rtl/digest.h>
34 #include <osl/diagnose.h>
36 #include <algorithm>
37 #include <vector>
39 #include "blowfishcontext.hxx"
40 #include "sha1context.hxx"
41 #include <ZipFile.hxx>
42 #include <ZipEnumeration.hxx>
43 #include <XUnbufferedStream.hxx>
44 #include <PackageConstants.hxx>
45 #include <EncryptedDataHeader.hxx>
46 #include <EncryptionData.hxx>
47 #include <MemoryByteGrabber.hxx>
49 #include <CRC32.hxx>
51 using namespace com::sun::star;
52 using namespace com::sun::star::io;
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::ucb;
55 using namespace com::sun::star::lang;
56 using namespace com::sun::star::packages;
57 using namespace com::sun::star::packages::zip;
58 using namespace com::sun::star::packages::zip::ZipConstants;
60 using ZipUtils::Inflater;
62 #if OSL_DEBUG_LEVEL > 0
63 #define THROW_WHERE SAL_WHERE
64 #else
65 #define THROW_WHERE ""
66 #endif
68 /** This class is used to read entries from a zip file
70 ZipFile::ZipFile( uno::Reference < XInputStream > &xInput, const uno::Reference < XComponentContext > & rxContext, bool bInitialise )
71 throw(IOException, ZipException, RuntimeException)
72 : aGrabber(xInput)
73 , aInflater( true )
74 , xStream(xInput)
75 , xSeek(xInput, UNO_QUERY)
76 , m_xContext ( rxContext )
77 , bRecoveryMode( false )
79 if (bInitialise)
81 if ( readCEN() == -1 )
83 aEntries.clear();
84 throw ZipException( "stream data looks to be broken" );
89 ZipFile::ZipFile( uno::Reference < XInputStream > &xInput, const uno::Reference < XComponentContext > & rxContext, bool bInitialise, bool bForceRecovery, uno::Reference < XProgressHandler > xProgress )
90 throw(IOException, ZipException, RuntimeException)
91 : aGrabber(xInput)
92 , aInflater( true )
93 , xStream(xInput)
94 , xSeek(xInput, UNO_QUERY)
95 , m_xContext ( rxContext )
96 , xProgressHandler( xProgress )
97 , bRecoveryMode( bForceRecovery )
99 if (bInitialise)
101 if ( bForceRecovery )
103 recover();
105 else if ( readCEN() == -1 )
107 aEntries.clear();
108 throw ZipException("stream data looks to be broken" );
113 ZipFile::~ZipFile()
115 aEntries.clear();
118 void ZipFile::setInputStream ( uno::Reference < XInputStream > xNewStream )
120 ::osl::MutexGuard aGuard( m_aMutex );
122 xStream = xNewStream;
123 xSeek = uno::Reference < XSeekable > ( xStream, UNO_QUERY );
124 aGrabber.setInputStream ( xStream );
127 uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
129 uno::Reference< xml::crypto::XDigestContext > xDigestContext;
130 if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K )
132 uno::Reference< uno::XComponentContext > xContext = xArgContext;
133 if ( !xContext.is() )
134 xContext = comphelper::getProcessComponentContext();
136 uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
138 xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
140 else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K )
141 xDigestContext.set( SHA1DigestContext::Create(), uno::UNO_SET_THROW );
143 return xDigestContext;
146 uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
148 uno::Reference< xml::crypto::XCipherContext > xResult;
152 if (xEncryptionData->m_nDerivedKeySize < 0)
154 throw ZipIOException("Invalid derived key length!" );
157 uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
158 if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
159 aDerivedKey.getLength(),
160 reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
161 xEncryptionData->m_aKey.getLength(),
162 reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
163 xEncryptionData->m_aSalt.getLength(),
164 xEncryptionData->m_nIterationCount ) )
166 throw ZipIOException("Can not create derived key!" );
169 if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
171 uno::Reference< uno::XComponentContext > xContext = xArgContext;
172 if ( !xContext.is() )
173 xContext = comphelper::getProcessComponentContext();
175 uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
177 xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
179 else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
181 xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
183 else
185 throw ZipIOException("Unknown cipher algorithm is requested!" );
188 catch( ... )
190 OSL_ENSURE( false, "Can not create cipher context!" );
193 return xResult;
196 void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
197 sal_Int64 nSize,
198 const OUString& aMediaType,
199 sal_Int8 * & pHeader )
201 // I think it's safe to restrict vector and salt length to 2 bytes !
202 sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
203 sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
204 sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
205 sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
207 // First the header
208 *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
209 *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
210 *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
211 *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
213 // Then the version
214 *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
215 *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
217 // Then the iteration Count
218 sal_Int32 nIterationCount = rData->m_nIterationCount;
219 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
220 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
221 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
222 *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
224 // FIXME64: need to handle larger sizes
225 // Then the size:
226 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
227 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
228 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
229 *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
231 // Then the encryption algorithm
232 sal_Int32 nEncAlgID = rData->m_nEncAlg;
233 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
234 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
235 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
236 *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
238 // Then the checksum algorithm
239 sal_Int32 nChecksumAlgID = rData->m_nCheckAlg;
240 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
241 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
242 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
243 *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
245 // Then the derived key size
246 sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
247 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
248 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
249 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
250 *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
252 // Then the start key generation algorithm
253 sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
254 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
255 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
256 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
257 *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
259 // Then the salt length
260 *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
261 *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
263 // Then the IV length
264 *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
265 *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
267 // Then the digest length
268 *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
269 *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
271 // Then the mediatype length
272 *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
273 *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
275 // Then the salt content
276 memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
277 pHeader += nSaltLength;
279 // Then the IV content
280 memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
281 pHeader += nIVLength;
283 // Then the digest content
284 memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
285 pHeader += nDigestLength;
287 // Then the mediatype itself
288 memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
289 pHeader += nMediaTypeLength;
292 bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > & rData,
293 sal_Int32 &rEncAlg,
294 sal_Int32 &rChecksumAlg,
295 sal_Int32 &rDerivedKeySize,
296 sal_Int32 &rStartKeyGenID,
297 sal_Int32 &rSize,
298 OUString& aMediaType,
299 const uno::Reference< XInputStream >& rStream )
301 bool bOk = false;
302 const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
303 Sequence < sal_Int8 > aBuffer ( nHeaderSize );
304 if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
306 sal_Int16 nPos = 0;
307 sal_Int8 *pBuffer = aBuffer.getArray();
308 sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
309 nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
310 if ( nVersion == n_ConstCurrentVersion )
312 sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
313 nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
314 nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
315 nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
316 rData->m_nIterationCount = nCount;
318 rSize = pBuffer[nPos++] & 0xFF;
319 rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
320 rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
321 rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
323 rEncAlg = pBuffer[nPos++] & 0xFF;
324 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
325 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
326 rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
328 rChecksumAlg = pBuffer[nPos++] & 0xFF;
329 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
330 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
331 rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
333 rDerivedKeySize = pBuffer[nPos++] & 0xFF;
334 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 8;
335 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 16;
336 rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 24;
338 rStartKeyGenID = pBuffer[nPos++] & 0xFF;
339 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 8;
340 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 16;
341 rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 24;
343 sal_Int16 nSaltLength = pBuffer[nPos++] & 0xFF;
344 nSaltLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
345 sal_Int16 nIVLength = ( pBuffer[nPos++] & 0xFF );
346 nIVLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
347 sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
348 nDigestLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
350 sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
351 nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
353 if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
355 rData->m_aSalt.realloc ( nSaltLength );
356 memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
357 if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
359 rData->m_aInitVector.realloc ( nIVLength );
360 memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
361 if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
363 rData->m_aDigest.realloc ( nDigestLength );
364 memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
366 if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
368 aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
369 nMediaTypeLength / sizeof( sal_Unicode ) );
370 bOk = true;
377 return bOk;
380 uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream( const uno::Reference< uno::XComponentContext >& rxContext,
381 const uno::Reference< XInputStream >& xStream,
382 const ::rtl::Reference< EncryptionData > &rData )
383 throw ( packages::WrongPasswordException, ZipIOException, RuntimeException )
385 if ( !rData.is() )
386 throw ZipIOException("Encrypted stream without encryption data!" );
388 if ( !rData->m_aKey.getLength() )
389 throw packages::WrongPasswordException(THROW_WHERE );
391 uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY );
392 if ( !xSeek.is() )
393 throw ZipIOException("The stream must be seekable!" );
395 // if we have a digest, then this file is an encrypted one and we should
396 // check if we can decrypt it or not
397 OSL_ENSURE( rData->m_aDigest.getLength(), "Can't detect password correctness without digest!" );
398 if ( rData->m_aDigest.getLength() )
400 sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() );
401 if ( nSize > n_ConstDigestLength + 32 )
402 nSize = n_ConstDigestLength + 32;
404 // skip header
405 xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() +
406 rData->m_aSalt.getLength() + rData->m_aDigest.getLength() );
408 // Only want to read enough to verify the digest
409 Sequence < sal_Int8 > aReadBuffer ( nSize );
411 xStream->readBytes( aReadBuffer, nSize );
413 if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) )
414 throw packages::WrongPasswordException(THROW_WHERE );
417 return new XUnbufferedStream( rxContext, xStream, rData );
420 #if 0
421 // for debugging purposes
422 void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
424 if ( aSequence.getLength() )
426 sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
427 sal_Int32 nSize = *( pPointer + 1 );
428 sal_Int32 nMemSize = *( pPointer - 2 );
429 sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
430 OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
433 #endif
435 bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
437 if ( !rData.is() || !rData->m_aKey.getLength() )
438 return false;
440 bool bRet = false;
442 uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
444 uno::Sequence< sal_Int8 > aDecryptBuffer;
445 uno::Sequence< sal_Int8 > aDecryptBuffer2;
448 aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
449 aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
451 catch( uno::Exception& )
453 // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
454 // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
457 if ( aDecryptBuffer2.getLength() )
459 sal_Int32 nOldLen = aDecryptBuffer.getLength();
460 aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
461 memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getArray(), aDecryptBuffer2.getLength() );
464 if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
465 aDecryptBuffer.realloc( n_ConstDigestLength );
467 uno::Sequence< sal_Int8 > aDigestSeq;
468 uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
470 xDigestContext->updateDigest( aDecryptBuffer );
471 aDigestSeq = xDigestContext->finalizeDigestAndDispose();
473 // If we don't have a digest, then we have to assume that the password is correct
474 if ( rData->m_aDigest.getLength() != 0 &&
475 ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
476 0 != memcmp ( aDigestSeq.getConstArray(),
477 rData->m_aDigest.getConstArray(),
478 aDigestSeq.getLength() ) ) )
480 // We should probably tell the user that the password they entered was wrong
482 else
483 bRet = true;
485 return bRet;
488 bool ZipFile::hasValidPassword ( ZipEntry & rEntry, const ::rtl::Reference< EncryptionData >& rData )
490 ::osl::MutexGuard aGuard( m_aMutex );
492 bool bRet = false;
493 if ( rData.is() && rData->m_aKey.getLength() )
495 xSeek->seek( rEntry.nOffset );
496 sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
498 // Only want to read enough to verify the digest
499 if ( nSize > n_ConstDigestDecrypt )
500 nSize = n_ConstDigestDecrypt;
502 Sequence < sal_Int8 > aReadBuffer ( nSize );
504 xStream->readBytes( aReadBuffer, nSize );
506 bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData );
509 return bRet;
512 uno::Reference< XInputStream > ZipFile::createUnbufferedStream(
513 SotMutexHolderRef aMutexHolder,
514 ZipEntry & rEntry,
515 const ::rtl::Reference< EncryptionData > &rData,
516 sal_Int8 nStreamMode,
517 bool bIsEncrypted,
518 const OUString& aMediaType )
520 ::osl::MutexGuard aGuard( m_aMutex );
522 return new XUnbufferedStream ( m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode );
525 ZipEnumeration * SAL_CALL ZipFile::entries( )
527 return new ZipEnumeration ( aEntries );
530 uno::Reference< XInputStream > SAL_CALL ZipFile::getInputStream( ZipEntry& rEntry,
531 const ::rtl::Reference< EncryptionData > &rData,
532 bool bIsEncrypted,
533 SotMutexHolderRef aMutexHolder )
534 throw(IOException, ZipException, RuntimeException)
536 ::osl::MutexGuard aGuard( m_aMutex );
538 if ( rEntry.nOffset <= 0 )
539 readLOC( rEntry );
541 // We want to return a rawStream if we either don't have a key or if the
542 // key is wrong
544 bool bNeedRawStream = rEntry.nMethod == STORED;
546 // if we have a digest, then this file is an encrypted one and we should
547 // check if we can decrypt it or not
548 if ( bIsEncrypted && rData.is() && rData->m_aDigest.getLength() )
549 bNeedRawStream = !hasValidPassword ( rEntry, rData );
551 return createUnbufferedStream ( aMutexHolder,
552 rEntry,
553 rData,
554 bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
555 bIsEncrypted );
558 uno::Reference< XInputStream > SAL_CALL ZipFile::getDataStream( ZipEntry& rEntry,
559 const ::rtl::Reference< EncryptionData > &rData,
560 bool bIsEncrypted,
561 SotMutexHolderRef aMutexHolder )
562 throw ( packages::WrongPasswordException,
563 IOException,
564 ZipException,
565 RuntimeException )
567 ::osl::MutexGuard aGuard( m_aMutex );
569 if ( rEntry.nOffset <= 0 )
570 readLOC( rEntry );
572 // An exception must be thrown in case stream is encrypted and
573 // there is no key or the key is wrong
574 bool bNeedRawStream = false;
575 if ( bIsEncrypted )
577 // in case no digest is provided there is no way
578 // to detect password correctness
579 if ( !rData.is() )
580 throw ZipException("Encrypted stream without encryption data!" );
582 // if we have a digest, then this file is an encrypted one and we should
583 // check if we can decrypt it or not
584 OSL_ENSURE( rData->m_aDigest.getLength(), "Can't detect password correctness without digest!\n" );
585 if ( rData->m_aDigest.getLength() && !hasValidPassword ( rEntry, rData ) )
586 throw packages::WrongPasswordException(THROW_WHERE );
588 else
589 bNeedRawStream = ( rEntry.nMethod == STORED );
591 return createUnbufferedStream ( aMutexHolder,
592 rEntry,
593 rData,
594 bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
595 bIsEncrypted );
598 uno::Reference< XInputStream > SAL_CALL ZipFile::getRawData( ZipEntry& rEntry,
599 const ::rtl::Reference< EncryptionData >& rData,
600 bool bIsEncrypted,
601 SotMutexHolderRef aMutexHolder )
602 throw(IOException, ZipException, RuntimeException)
604 ::osl::MutexGuard aGuard( m_aMutex );
606 if ( rEntry.nOffset <= 0 )
607 readLOC( rEntry );
609 return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted );
612 uno::Reference< XInputStream > SAL_CALL ZipFile::getWrappedRawStream(
613 ZipEntry& rEntry,
614 const ::rtl::Reference< EncryptionData >& rData,
615 const OUString& aMediaType,
616 SotMutexHolderRef aMutexHolder )
617 throw ( packages::NoEncryptionException,
618 IOException,
619 ZipException,
620 RuntimeException )
622 ::osl::MutexGuard aGuard( m_aMutex );
624 if ( !rData.is() )
625 throw packages::NoEncryptionException(THROW_WHERE );
627 if ( rEntry.nOffset <= 0 )
628 readLOC( rEntry );
630 return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, aMediaType );
633 bool ZipFile::readLOC( ZipEntry &rEntry )
634 throw(IOException, ZipException, RuntimeException)
636 ::osl::MutexGuard aGuard( m_aMutex );
638 sal_Int64 nPos = -rEntry.nOffset;
640 aGrabber.seek(nPos);
641 sal_Int32 nTestSig = aGrabber.ReadInt32();
642 if (nTestSig != LOCSIG)
643 throw ZipIOException("Invalid LOC header (bad signature)" );
645 // Ignore all (duplicated) information from the local file header.
646 // various programs produced "broken" zip files; even LO at some point.
647 // Just verify the path and calculate the data offset and otherwise
648 // rely on the central directory info.
650 aGrabber.ReadInt16(); //version
651 aGrabber.ReadInt16(); //flag
652 aGrabber.ReadInt16(); //how
653 aGrabber.ReadInt32(); //time
654 aGrabber.ReadInt32(); //crc
655 aGrabber.ReadInt32(); //compressed size
656 aGrabber.ReadInt32(); //size
657 sal_Int16 nPathLen = aGrabber.ReadInt16();
658 sal_Int16 nExtraLen = aGrabber.ReadInt16();
659 rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
661 // FIXME64: need to read 64bit LOC
663 bool bBroken = false;
667 sal_Int16 nPathLenToRead = nPathLen;
668 const sal_Int64 nBytesAvailable = aGrabber.getLength() - aGrabber.getPosition();
669 if (nPathLenToRead > nBytesAvailable)
670 nPathLenToRead = nBytesAvailable;
671 else if (nPathLenToRead < 0)
672 nPathLenToRead = 0;
674 // read always in UTF8, some tools seem not to set UTF8 bit
675 uno::Sequence<sal_Int8> aNameBuffer(nPathLenToRead);
676 sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLenToRead);
677 if (nRead < aNameBuffer.getLength())
678 aNameBuffer.realloc(nRead);
680 OUString sLOCPath = OUString::intern( reinterpret_cast<char *>(aNameBuffer.getArray()),
681 aNameBuffer.getLength(),
682 RTL_TEXTENCODING_UTF8 );
684 if ( rEntry.nPathLen == -1 ) // the file was created
686 rEntry.nPathLen = nPathLen;
687 rEntry.sPath = sLOCPath;
690 bBroken = rEntry.nPathLen != nPathLen
691 || !rEntry.sPath.equals( sLOCPath );
693 catch(...)
695 bBroken = true;
698 if ( bBroken && !bRecoveryMode )
699 throw ZipIOException("The stream seems to be broken!" );
701 return true;
704 sal_Int32 ZipFile::findEND( )
705 throw(IOException, ZipException, RuntimeException)
707 // this method is called in constructor only, no need for mutex
708 sal_Int32 nLength, nPos, nEnd;
709 Sequence < sal_Int8 > aBuffer;
712 nLength = static_cast <sal_Int32 > (aGrabber.getLength());
713 if (nLength == 0 || nLength < ENDHDR)
714 return -1;
715 nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
716 nEnd = nPos >= 0 ? nPos : 0 ;
718 aGrabber.seek( nEnd );
719 aGrabber.readBytes ( aBuffer, nLength - nEnd );
721 const sal_Int8 *pBuffer = aBuffer.getConstArray();
723 nPos = nLength - nEnd - ENDHDR;
724 while ( nPos >= 0 )
726 if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
727 return nPos + nEnd;
728 nPos--;
731 catch ( IllegalArgumentException& )
733 throw ZipException("Zip END signature not found!" );
735 catch ( NotConnectedException& )
737 throw ZipException("Zip END signature not found!" );
739 catch ( BufferSizeExceededException& )
741 throw ZipException("Zip END signature not found!" );
743 throw ZipException("Zip END signature not found!" );
746 sal_Int32 ZipFile::readCEN()
747 throw(IOException, ZipException, RuntimeException)
749 // this method is called in constructor only, no need for mutex
750 sal_Int32 nCenPos = -1, nEndPos, nLocPos;
751 sal_uInt16 nCount;
755 nEndPos = findEND();
756 if (nEndPos == -1)
757 return -1;
758 aGrabber.seek(nEndPos + ENDTOT);
759 sal_uInt16 nTotal = aGrabber.ReadUInt16();
760 sal_Int32 nCenLen = aGrabber.ReadInt32();
761 sal_Int32 nCenOff = aGrabber.ReadInt32();
763 if ( nTotal * CENHDR > nCenLen )
764 throw ZipException("invalid END header (bad entry count)" );
766 if ( nTotal > ZIP_MAXENTRIES )
767 throw ZipException("too many entries in ZIP File" );
769 if ( nCenLen < 0 || nCenLen > nEndPos )
770 throw ZipException("Invalid END header (bad central directory size)" );
772 nCenPos = nEndPos - nCenLen;
774 if ( nCenOff < 0 || nCenOff > nCenPos )
775 throw ZipException("Invalid END header (bad central directory size)" );
777 nLocPos = nCenPos - nCenOff;
778 aGrabber.seek( nCenPos );
779 Sequence < sal_Int8 > aCENBuffer ( nCenLen );
780 sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
781 if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
782 throw ZipException ("Error reading CEN into memory buffer!" );
784 MemoryByteGrabber aMemGrabber ( aCENBuffer );
786 ZipEntry aEntry;
787 sal_Int16 nCommentLen;
789 for (nCount = 0 ; nCount < nTotal; nCount++)
791 sal_Int32 nTestSig = aMemGrabber.ReadInt32();
792 if ( nTestSig != CENSIG )
793 throw ZipException("Invalid CEN header (bad signature)" );
795 aMemGrabber.skipBytes ( 2 );
796 aEntry.nVersion = aMemGrabber.ReadInt16();
798 if ( ( aEntry.nVersion & 1 ) == 1 )
799 throw ZipException("Invalid CEN header (encrypted entry)" );
801 aEntry.nFlag = aMemGrabber.ReadInt16();
802 aEntry.nMethod = aMemGrabber.ReadInt16();
804 if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
805 throw ZipException("Invalid CEN header (bad compression method)" );
807 aEntry.nTime = aMemGrabber.ReadInt32();
808 aEntry.nCrc = aMemGrabber.ReadInt32();
810 sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
811 sal_uInt32 nSize = aMemGrabber.ReadUInt32();
812 aEntry.nPathLen = aMemGrabber.ReadInt16();
813 aEntry.nExtraLen = aMemGrabber.ReadInt16();
814 nCommentLen = aMemGrabber.ReadInt16();
815 aMemGrabber.skipBytes ( 8 );
816 sal_uInt32 nOffset = aMemGrabber.ReadUInt32();
818 // FIXME64: need to read the 64bit header instead
819 if ( nSize == 0xffffffff ||
820 nOffset == 0xffffffff ||
821 nCompressedSize == 0xffffffff ) {
822 throw ZipException("PK64 zip file entry" );
823 } else {
824 aEntry.nCompressedSize = nCompressedSize;
825 aEntry.nSize = nSize;
826 aEntry.nOffset = nOffset;
829 aEntry.nOffset += nLocPos;
830 aEntry.nOffset *= -1;
832 if ( aEntry.nPathLen < 0 )
833 throw ZipException("unexpected name length" );
835 if ( nCommentLen < 0 )
836 throw ZipException("unexpected comment length" );
838 if ( aEntry.nExtraLen < 0 )
839 throw ZipException("unexpected extra header info length" );
841 // read always in UTF8, some tools seem not to set UTF8 bit
842 aEntry.sPath = OUString::intern ( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
843 aEntry.nPathLen,
844 RTL_TEXTENCODING_UTF8 );
846 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
847 throw ZipException("Zip entry has an invalid name." );
849 aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
850 aEntries[aEntry.sPath] = aEntry;
853 if (nCount != nTotal)
854 throw ZipException("Count != Total" );
856 catch ( IllegalArgumentException & )
858 // seek can throw this...
859 nCenPos = -1; // make sure we return -1 to indicate an error
861 return nCenPos;
864 sal_Int32 ZipFile::recover()
865 throw(IOException, ZipException, RuntimeException)
867 ::osl::MutexGuard aGuard( m_aMutex );
869 sal_Int64 nLength;
870 Sequence < sal_Int8 > aBuffer;
874 nLength = aGrabber.getLength();
875 if (nLength == 0 || nLength < ENDHDR)
876 return -1;
878 aGrabber.seek( 0 );
880 const sal_Int64 nToRead = 32000;
881 for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
883 const sal_Int8 *pBuffer = aBuffer.getConstArray();
884 sal_Int32 nBufSize = aBuffer.getLength();
886 sal_Int64 nPos = 0;
887 // the buffer should contain at least one header,
888 // or if it is end of the file, at least the postheader with sizes and hash
889 while( nPos < nBufSize - 30
890 || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
893 if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
895 ZipEntry aEntry;
896 MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( &(pBuffer[nPos+4]), 26 ) );
898 aEntry.nVersion = aMemGrabber.ReadInt16();
899 if ( ( aEntry.nVersion & 1 ) != 1 )
901 aEntry.nFlag = aMemGrabber.ReadInt16();
902 aEntry.nMethod = aMemGrabber.ReadInt16();
904 if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
906 aEntry.nTime = aMemGrabber.ReadInt32();
907 aEntry.nCrc = aMemGrabber.ReadInt32();
908 sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
909 sal_uInt32 nSize = aMemGrabber.ReadUInt32();
910 aEntry.nPathLen = aMemGrabber.ReadInt16();
911 aEntry.nExtraLen = aMemGrabber.ReadInt16();
913 // FIXME64: need to read the 64bit header instead
914 if ( nSize == 0xffffffff ||
915 nCompressedSize == 0xffffffff ) {
916 throw ZipException("PK64 zip file entry" );
917 } else {
918 aEntry.nCompressedSize = nCompressedSize;
919 aEntry.nSize = nSize;
922 sal_Int32 nDescrLength =
923 ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
925 sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
926 sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
927 if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
928 && ( nGenPos + nPos + nBlockLength ) <= nLength )
930 // read always in UTF8, some tools seem not to set UTF8 bit
931 if( nPos + 30 + aEntry.nPathLen <= nBufSize )
932 aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]),
933 aEntry.nPathLen,
934 RTL_TEXTENCODING_UTF8 );
935 else
937 Sequence < sal_Int8 > aFileName;
938 aGrabber.seek( nGenPos + nPos + 30 );
939 aGrabber.readBytes( aFileName, aEntry.nPathLen );
940 aEntry.sPath = OUString ( reinterpret_cast<char *>(aFileName.getArray()),
941 aFileName.getLength(),
942 RTL_TEXTENCODING_UTF8 );
943 aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
946 aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
948 if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
950 aEntry.nCrc = 0;
951 aEntry.nCompressedSize = 0;
952 aEntry.nSize = 0;
955 if ( aEntries.find( aEntry.sPath ) == aEntries.end() )
956 aEntries[aEntry.sPath] = aEntry;
961 nPos += 4;
963 else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
965 sal_Int64 nCompressedSize, nSize;
966 MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( &(pBuffer[nPos+4]), 12 ) );
967 sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
968 sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32();
969 sal_uInt32 nSize32 = aMemGrabber.ReadUInt32();
971 // FIXME64: work to be done here ...
972 nCompressedSize = nCompressedSize32;
973 nSize = nSize32;
975 for( EntryHash::iterator aIter = aEntries.begin(); aIter != aEntries.end(); ++aIter )
977 ZipEntry aTmp = (*aIter).second;
979 // this is a broken package, accept this block not only for DEFLATED streams
980 if( (*aIter).second.nFlag & 8 )
982 sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
983 if ( nStreamOffset == (*aIter).second.nOffset && nCompressedSize > (*aIter).second.nCompressedSize )
985 // only DEFLATED blocks need to be checked
986 bool bAcceptBlock = ( (*aIter).second.nMethod == STORED && nCompressedSize == nSize );
988 if ( !bAcceptBlock )
990 sal_Int64 nRealSize = 0;
991 sal_Int32 nRealCRC = 0;
992 getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
993 bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
996 if ( bAcceptBlock )
998 (*aIter).second.nCrc = nCRC32;
999 (*aIter).second.nCompressedSize = nCompressedSize;
1000 (*aIter).second.nSize = nSize;
1003 #if 0
1004 // for now ignore clearly broken streams
1005 else if( !(*aIter).second.nCompressedSize )
1007 (*aIter).second.nCrc = nCRC32;
1008 sal_Int32 nRealStreamSize = nGenPos + nPos - (*aIter).second.nOffset;
1009 (*aIter).second.nCompressedSize = nGenPos + nPos - (*aIter).second.nOffset;
1010 (*aIter).second.nSize = nSize;
1012 #endif
1016 nPos += 4;
1018 else
1019 nPos++;
1022 nGenPos += nPos;
1023 aGrabber.seek( nGenPos );
1026 return 0;
1028 catch ( IllegalArgumentException& )
1030 throw ZipException("Zip END signature not found!" );
1032 catch ( NotConnectedException& )
1034 throw ZipException("Zip END signature not found!" );
1036 catch ( BufferSizeExceededException& )
1038 throw ZipException("Zip END signature not found!" );
1042 bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
1044 ::osl::MutexGuard aGuard( m_aMutex );
1046 sal_Int32 nCRC = 0;
1047 sal_Int64 nSize = 0;
1049 if( aEntry.nMethod == STORED )
1050 return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
1052 getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
1053 return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
1056 sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
1058 ::osl::MutexGuard aGuard( m_aMutex );
1060 Sequence < sal_Int8 > aBuffer;
1061 CRC32 aCRC;
1062 sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
1064 aGrabber.seek( nOffset );
1065 for (sal_Int64 ind = 0;
1066 aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
1067 ++ind)
1069 sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize);
1070 aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen));
1073 return aCRC.getValue();
1076 void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
1078 ::osl::MutexGuard aGuard( m_aMutex );
1080 Sequence < sal_Int8 > aBuffer;
1081 CRC32 aCRC;
1082 sal_Int64 nRealSize = 0;
1083 Inflater aInflaterLocal( true );
1084 sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
1086 aGrabber.seek( nOffset );
1087 for ( sal_Int64 ind = 0;
1088 !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
1089 ind++ )
1091 Sequence < sal_Int8 > aData( nBlockSize );
1092 sal_Int32 nLastInflated = 0;
1093 sal_Int64 nInBlock = 0;
1095 aInflaterLocal.setInput( aBuffer );
1098 nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
1099 aCRC.updateSegment( aData, nLastInflated );
1100 nInBlock += nLastInflated;
1101 } while( !aInflater.finished() && nLastInflated );
1103 nRealSize += nInBlock;
1106 *nSize = nRealSize;
1107 *nCRC = aCRC.getValue();
1110 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */