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 .
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>
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>
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
65 #define THROW_WHERE ""
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
)
75 , xSeek(xInput
, UNO_QUERY
)
76 , m_xContext ( rxContext
)
77 , bRecoveryMode( false )
81 if ( readCEN() == -1 )
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
)
94 , xSeek(xInput
, UNO_QUERY
)
95 , m_xContext ( rxContext
)
96 , xProgressHandler( xProgress
)
97 , bRecoveryMode( bForceRecovery
)
101 if ( bForceRecovery
)
105 else if ( readCEN() == -1 )
108 throw ZipException("stream data looks to be broken" );
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
);
185 throw ZipIOException("Unknown cipher algorithm is requested!" );
190 OSL_ENSURE( false, "Can not create cipher context!" );
196 void ZipFile::StaticFillHeader( const ::rtl::Reference
< EncryptionData
>& rData
,
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
) );
208 *(pHeader
++) = ( n_ConstHeader
>> 0 ) & 0xFF;
209 *(pHeader
++) = ( n_ConstHeader
>> 8 ) & 0xFF;
210 *(pHeader
++) = ( n_ConstHeader
>> 16 ) & 0xFF;
211 *(pHeader
++) = ( n_ConstHeader
>> 24 ) & 0xFF;
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
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
,
294 sal_Int32
&rChecksumAlg
,
295 sal_Int32
&rDerivedKeySize
,
296 sal_Int32
&rStartKeyGenID
,
298 OUString
& aMediaType
,
299 const uno::Reference
< XInputStream
>& rStream
)
302 const sal_Int32 nHeaderSize
= n_ConstHeaderSize
- 4;
303 Sequence
< sal_Int8
> aBuffer ( nHeaderSize
);
304 if ( nHeaderSize
== rStream
->readBytes ( aBuffer
, nHeaderSize
) )
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
) );
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
)
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
);
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;
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
);
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!" );
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() )
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
488 bool ZipFile::hasValidPassword ( ZipEntry
& rEntry
, const ::rtl::Reference
< EncryptionData
>& rData
)
490 ::osl::MutexGuard
aGuard( m_aMutex
);
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
);
512 uno::Reference
< XInputStream
> ZipFile::createUnbufferedStream(
513 SotMutexHolderRef aMutexHolder
,
515 const ::rtl::Reference
< EncryptionData
> &rData
,
516 sal_Int8 nStreamMode
,
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
,
533 SotMutexHolderRef aMutexHolder
)
534 throw(IOException
, ZipException
, RuntimeException
)
536 ::osl::MutexGuard
aGuard( m_aMutex
);
538 if ( rEntry
.nOffset
<= 0 )
541 // We want to return a rawStream if we either don't have a key or if the
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
,
554 bNeedRawStream
? UNBUFF_STREAM_RAW
: UNBUFF_STREAM_DATA
,
558 uno::Reference
< XInputStream
> SAL_CALL
ZipFile::getDataStream( ZipEntry
& rEntry
,
559 const ::rtl::Reference
< EncryptionData
> &rData
,
561 SotMutexHolderRef aMutexHolder
)
562 throw ( packages::WrongPasswordException
,
567 ::osl::MutexGuard
aGuard( m_aMutex
);
569 if ( rEntry
.nOffset
<= 0 )
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;
577 // in case no digest is provided there is no way
578 // to detect password correctness
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
);
589 bNeedRawStream
= ( rEntry
.nMethod
== STORED
);
591 return createUnbufferedStream ( aMutexHolder
,
594 bNeedRawStream
? UNBUFF_STREAM_RAW
: UNBUFF_STREAM_DATA
,
598 uno::Reference
< XInputStream
> SAL_CALL
ZipFile::getRawData( ZipEntry
& rEntry
,
599 const ::rtl::Reference
< EncryptionData
>& rData
,
601 SotMutexHolderRef aMutexHolder
)
602 throw(IOException
, ZipException
, RuntimeException
)
604 ::osl::MutexGuard
aGuard( m_aMutex
);
606 if ( rEntry
.nOffset
<= 0 )
609 return createUnbufferedStream ( aMutexHolder
, rEntry
, rData
, UNBUFF_STREAM_RAW
, bIsEncrypted
);
612 uno::Reference
< XInputStream
> SAL_CALL
ZipFile::getWrappedRawStream(
614 const ::rtl::Reference
< EncryptionData
>& rData
,
615 const OUString
& aMediaType
,
616 SotMutexHolderRef aMutexHolder
)
617 throw ( packages::NoEncryptionException
,
622 ::osl::MutexGuard
aGuard( m_aMutex
);
625 throw packages::NoEncryptionException(THROW_WHERE
);
627 if ( rEntry
.nOffset
<= 0 )
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
;
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)
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
);
698 if ( bBroken
&& !bRecoveryMode
)
699 throw ZipIOException("The stream seems to be broken!" );
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
)
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
;
726 if (pBuffer
[nPos
] == 'P' && pBuffer
[nPos
+1] == 'K' && pBuffer
[nPos
+2] == 5 && pBuffer
[nPos
+3] == 6 )
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
;
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
);
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" );
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()),
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
864 sal_Int32
ZipFile::recover()
865 throw(IOException
, ZipException
, RuntimeException
)
867 ::osl::MutexGuard
aGuard( m_aMutex
);
870 Sequence
< sal_Int8
> aBuffer
;
874 nLength
= aGrabber
.getLength();
875 if (nLength
== 0 || nLength
< ENDHDR
)
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();
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 )
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" );
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]),
934 RTL_TEXTENCODING_UTF8
);
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
) )
951 aEntry
.nCompressedSize
= 0;
955 if ( aEntries
.find( aEntry
.sPath
) == aEntries
.end() )
956 aEntries
[aEntry
.sPath
] = aEntry
;
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
;
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
);
990 sal_Int64 nRealSize
= 0;
991 sal_Int32 nRealCRC
= 0;
992 getSizeAndCRC( nStreamOffset
, nCompressedSize
, &nRealSize
, &nRealCRC
);
993 bAcceptBlock
= ( nRealSize
== nSize
&& nRealCRC
== nCRC32
);
998 (*aIter
).second
.nCrc
= nCRC32
;
999 (*aIter
).second
.nCompressedSize
= nCompressedSize
;
1000 (*aIter
).second
.nSize
= nSize
;
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
;
1023 aGrabber
.seek( nGenPos
);
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
);
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
;
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
;
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
;
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
;
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
;
1107 *nCRC
= aCRC
.getValue();
1110 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */