LCOV - code coverage report
Current view: top level - package/source/zipapi - ZipFile.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 343 537 63.9 %
Date: 2015-06-13 12:38:46 Functions: 19 24 79.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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 <osl/diagnose.h>
      35             : 
      36             : #include <algorithm>
      37             : #include <vector>
      38             : 
      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>
      48             : 
      49             : #include <CRC32.hxx>
      50             : 
      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;
      59             : 
      60             : using ZipUtils::Inflater;
      61             : 
      62             : #if OSL_DEBUG_LEVEL > 0
      63             : #define THROW_WHERE SAL_WHERE
      64             : #else
      65             : #define THROW_WHERE ""
      66             : #endif
      67             : 
      68             : /** This class is used to read entries from a zip file
      69             :  */
      70        6973 : 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       12369 : , bRecoveryMode( false )
      78             : {
      79        6973 :     if (bInitialise)
      80             :     {
      81        6177 :         if ( readCEN() == -1 )
      82             :         {
      83           1 :             aEntries.clear();
      84           1 :             throw ZipException( "stream data looks to be broken" );
      85             :         }
      86             :     }
      87        1577 : }
      88             : 
      89       11287 : 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       11287 : , bRecoveryMode( bForceRecovery )
      98             : {
      99       11287 :     if (bInitialise)
     100             :     {
     101       11287 :         if ( bForceRecovery )
     102             :         {
     103           0 :             recover();
     104             :         }
     105       11287 :         else if ( readCEN() == -1 )
     106             :         {
     107           0 :             aEntries.clear();
     108           0 :             throw ZipException("stream data looks to be broken" );
     109             :         }
     110             :     }
     111       11287 : }
     112             : 
     113       25688 : ZipFile::~ZipFile()
     114             : {
     115       12844 :     aEntries.clear();
     116       12844 : }
     117             : 
     118          36 : void ZipFile::setInputStream ( uno::Reference < XInputStream > xNewStream )
     119             : {
     120          36 :     ::osl::MutexGuard aGuard( m_aMutex );
     121             : 
     122          36 :     xStream = xNewStream;
     123          36 :     xSeek = uno::Reference < XSeekable > ( xStream, UNO_QUERY );
     124          36 :     aGrabber.setInputStream ( xStream );
     125          36 : }
     126             : 
     127          26 : uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
     128             : {
     129          26 :     uno::Reference< xml::crypto::XDigestContext > xDigestContext;
     130          26 :     if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K )
     131             :     {
     132          22 :         uno::Reference< uno::XComponentContext > xContext = xArgContext;
     133          22 :         if ( !xContext.is() )
     134           0 :             xContext = comphelper::getProcessComponentContext();
     135             : 
     136          44 :         uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
     137             : 
     138          44 :         xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
     139             :     }
     140           4 :     else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K )
     141           4 :         xDigestContext.set( SHA1DigestContext::Create(), uno::UNO_SET_THROW );
     142             : 
     143          26 :     return xDigestContext;
     144             : }
     145             : 
     146          44 : uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
     147             : {
     148          44 :     uno::Reference< xml::crypto::XCipherContext > xResult;
     149             : 
     150             :     try
     151             :     {
     152          44 :         if (xEncryptionData->m_nDerivedKeySize < 0)
     153             :         {
     154           0 :             throw ZipIOException("Invalid derived key length!" );
     155             :         }
     156             : 
     157          44 :         uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
     158          88 :         if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
     159          44 :                             aDerivedKey.getLength(),
     160          44 :                             reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
     161          44 :                             xEncryptionData->m_aKey.getLength(),
     162          44 :                             reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
     163          44 :                             xEncryptionData->m_aSalt.getLength(),
     164         220 :                             xEncryptionData->m_nIterationCount ) )
     165             :         {
     166           0 :             throw ZipIOException("Can not create derived key!" );
     167             :         }
     168             : 
     169          44 :         if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
     170             :         {
     171          36 :             uno::Reference< uno::XComponentContext > xContext = xArgContext;
     172          36 :             if ( !xContext.is() )
     173           0 :                 xContext = comphelper::getProcessComponentContext();
     174             : 
     175          72 :             uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
     176             : 
     177          72 :             xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
     178             :         }
     179           8 :         else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
     180             :         {
     181           8 :             xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
     182             :         }
     183             :         else
     184             :         {
     185           0 :             throw ZipIOException("Unknown cipher algorithm is requested!" );
     186          44 :         }
     187             :     }
     188           0 :     catch( ... )
     189             :     {
     190             :         OSL_ENSURE( false, "Can not create cipher context!" );
     191             :     }
     192             : 
     193          44 :     return xResult;
     194             : }
     195             : 
     196           1 : void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
     197             :                                 sal_Int64 nSize,
     198             :                                 const OUString& aMediaType,
     199             :                                 sal_Int8 * & pHeader )
     200             : {
     201             :     // I think it's safe to restrict vector and salt length to 2 bytes !
     202           1 :     sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
     203           1 :     sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
     204           1 :     sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
     205           1 :     sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
     206             : 
     207             :     // First the header
     208           1 :     *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
     209           1 :     *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
     210           1 :     *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
     211           1 :     *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
     212             : 
     213             :     // Then the version
     214           1 :     *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
     215           1 :     *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
     216             : 
     217             :     // Then the iteration Count
     218           1 :     sal_Int32 nIterationCount = rData->m_nIterationCount;
     219           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
     220           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
     221           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
     222           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
     223             : 
     224             :     // FIXME64: need to handle larger sizes
     225             :     // Then the size:
     226           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
     227           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
     228           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
     229           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
     230             : 
     231             :     // Then the encryption algorithm
     232           1 :     sal_Int32 nEncAlgID = rData->m_nEncAlg;
     233           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
     234           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
     235           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
     236           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
     237             : 
     238             :     // Then the checksum algorithm
     239           1 :     sal_Int32 nChecksumAlgID = rData->m_nCheckAlg;
     240           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
     241           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
     242           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
     243           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
     244             : 
     245             :     // Then the derived key size
     246           1 :     sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
     247           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
     248           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
     249           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
     250           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
     251             : 
     252             :     // Then the start key generation algorithm
     253           1 :     sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
     254           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
     255           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
     256           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
     257           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
     258             : 
     259             :     // Then the salt length
     260           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
     261           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
     262             : 
     263             :     // Then the IV length
     264           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
     265           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
     266             : 
     267             :     // Then the digest length
     268           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
     269           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
     270             : 
     271             :     // Then the mediatype length
     272           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
     273           1 :     *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
     274             : 
     275             :     // Then the salt content
     276           1 :     memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
     277           1 :     pHeader += nSaltLength;
     278             : 
     279             :     // Then the IV content
     280           1 :     memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
     281           1 :     pHeader += nIVLength;
     282             : 
     283             :     // Then the digest content
     284           1 :     memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
     285           1 :     pHeader += nDigestLength;
     286             : 
     287             :     // Then the mediatype itself
     288           1 :     memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
     289           1 :     pHeader += nMediaTypeLength;
     290           1 : }
     291             : 
     292           1 : 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 )
     300             : {
     301           1 :     bool bOk = false;
     302           1 :     const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
     303           1 :     Sequence < sal_Int8 > aBuffer ( nHeaderSize );
     304           1 :     if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
     305             :     {
     306           1 :         sal_Int16 nPos = 0;
     307           1 :         sal_Int8 *pBuffer = aBuffer.getArray();
     308           1 :         sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
     309           1 :         nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
     310           1 :         if ( nVersion == n_ConstCurrentVersion )
     311             :         {
     312           1 :             sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
     313           1 :             nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
     314           1 :             nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
     315           1 :             nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
     316           1 :             rData->m_nIterationCount = nCount;
     317             : 
     318           1 :             rSize  =   pBuffer[nPos++] & 0xFF;
     319           1 :             rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
     320           1 :             rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
     321           1 :             rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
     322             : 
     323           1 :             rEncAlg   =   pBuffer[nPos++] & 0xFF;
     324           1 :             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
     325           1 :             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
     326           1 :             rEncAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
     327             : 
     328           1 :             rChecksumAlg   =   pBuffer[nPos++] & 0xFF;
     329           1 :             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 8;
     330           1 :             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 16;
     331           1 :             rChecksumAlg  |= ( pBuffer[nPos++] & 0xFF ) << 24;
     332             : 
     333           1 :             rDerivedKeySize   =   pBuffer[nPos++] & 0xFF;
     334           1 :             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 8;
     335           1 :             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 16;
     336           1 :             rDerivedKeySize  |= ( pBuffer[nPos++] & 0xFF ) << 24;
     337             : 
     338           1 :             rStartKeyGenID   =   pBuffer[nPos++] & 0xFF;
     339           1 :             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 8;
     340           1 :             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 16;
     341           1 :             rStartKeyGenID  |= ( pBuffer[nPos++] & 0xFF ) << 24;
     342             : 
     343           1 :             sal_Int16 nSaltLength =   pBuffer[nPos++] & 0xFF;
     344           1 :             nSaltLength          |= ( pBuffer[nPos++] & 0xFF ) << 8;
     345           1 :             sal_Int16 nIVLength   = ( pBuffer[nPos++] & 0xFF );
     346           1 :             nIVLength            |= ( pBuffer[nPos++] & 0xFF ) << 8;
     347           1 :             sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
     348           1 :             nDigestLength        |= ( pBuffer[nPos++] & 0xFF ) << 8;
     349             : 
     350           1 :             sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
     351           1 :             nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
     352             : 
     353           1 :             if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
     354             :             {
     355           1 :                 rData->m_aSalt.realloc ( nSaltLength );
     356           1 :                 memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
     357           1 :                 if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
     358             :                 {
     359           1 :                     rData->m_aInitVector.realloc ( nIVLength );
     360           1 :                     memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
     361           1 :                     if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
     362             :                     {
     363           1 :                         rData->m_aDigest.realloc ( nDigestLength );
     364           1 :                         memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
     365             : 
     366           1 :                         if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
     367             :                         {
     368           2 :                             aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
     369           2 :                                                             nMediaTypeLength / sizeof( sal_Unicode ) );
     370           1 :                             bOk = true;
     371             :                         }
     372             :                     }
     373             :                 }
     374             :             }
     375             :         }
     376             :     }
     377           1 :     return bOk;
     378             : }
     379             : 
     380           0 : 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 )
     384             : {
     385           0 :     if ( !rData.is() )
     386           0 :         throw ZipIOException("Encrypted stream without encryption data!" );
     387             : 
     388           0 :     if ( !rData->m_aKey.getLength() )
     389           0 :         throw packages::WrongPasswordException(THROW_WHERE );
     390             : 
     391           0 :     uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY );
     392           0 :     if ( !xSeek.is() )
     393           0 :         throw ZipIOException("The stream must be seekable!" );
     394             : 
     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           0 :     if ( rData->m_aDigest.getLength() )
     399             :     {
     400           0 :         sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() );
     401           0 :         if ( nSize > n_ConstDigestLength + 32 )
     402           0 :             nSize = n_ConstDigestLength + 32;
     403             : 
     404             :         // skip header
     405           0 :         xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() +
     406           0 :                                 rData->m_aSalt.getLength() + rData->m_aDigest.getLength() );
     407             : 
     408             :         // Only want to read enough to verify the digest
     409           0 :         Sequence < sal_Int8 > aReadBuffer ( nSize );
     410             : 
     411           0 :         xStream->readBytes( aReadBuffer, nSize );
     412             : 
     413           0 :         if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) )
     414           0 :             throw packages::WrongPasswordException(THROW_WHERE );
     415             :     }
     416             : 
     417           0 :     return new XUnbufferedStream( rxContext, xStream, rData );
     418             : }
     419             : 
     420             : #if 0
     421             : // for debugging purposes
     422             : void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
     423             : {
     424             :     if ( aSequence.getLength() )
     425             :     {
     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!" );
     431             :     }
     432             : }
     433             : #endif
     434             : 
     435          22 : bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
     436             : {
     437          22 :     if ( !rData.is() || !rData->m_aKey.getLength() )
     438           0 :         return false;
     439             : 
     440          22 :     bool bRet = false;
     441             : 
     442          22 :     uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
     443             : 
     444          44 :     uno::Sequence< sal_Int8 > aDecryptBuffer;
     445          44 :     uno::Sequence< sal_Int8 > aDecryptBuffer2;
     446             :     try
     447             :     {
     448          22 :         aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
     449          22 :         aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
     450             :     }
     451          12 :     catch( uno::Exception& )
     452             :     {
     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 )
     455             :     }
     456             : 
     457          22 :     if ( aDecryptBuffer2.getLength() )
     458             :     {
     459           6 :         sal_Int32 nOldLen = aDecryptBuffer.getLength();
     460           6 :         aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
     461           6 :         memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getArray(), aDecryptBuffer2.getLength() );
     462             :     }
     463             : 
     464          22 :     if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
     465          11 :         aDecryptBuffer.realloc( n_ConstDigestLength );
     466             : 
     467          44 :     uno::Sequence< sal_Int8 > aDigestSeq;
     468          44 :     uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
     469             : 
     470          22 :     xDigestContext->updateDigest( aDecryptBuffer );
     471          22 :     aDigestSeq = xDigestContext->finalizeDigestAndDispose();
     472             : 
     473             :     // If we don't have a digest, then we have to assume that the password is correct
     474          48 :     if (  rData->m_aDigest.getLength() != 0  &&
     475          44 :           ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
     476          22 :             0 != memcmp ( aDigestSeq.getConstArray(),
     477          22 :                                      rData->m_aDigest.getConstArray(),
     478          44 :                                     aDigestSeq.getLength() ) ) )
     479             :     {
     480             :         // We should probably tell the user that the password they entered was wrong
     481             :     }
     482             :     else
     483          18 :         bRet = true;
     484             : 
     485          44 :     return bRet;
     486             : }
     487             : 
     488          22 : bool ZipFile::hasValidPassword ( ZipEntry & rEntry, const ::rtl::Reference< EncryptionData >& rData )
     489             : {
     490          22 :     ::osl::MutexGuard aGuard( m_aMutex );
     491             : 
     492          22 :     bool bRet = false;
     493          22 :     if ( rData.is() && rData->m_aKey.getLength() )
     494             :     {
     495          22 :         xSeek->seek( rEntry.nOffset );
     496          22 :         sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
     497             : 
     498             :         // Only want to read enough to verify the digest
     499          22 :         if ( nSize > n_ConstDigestDecrypt )
     500          11 :             nSize = n_ConstDigestDecrypt;
     501             : 
     502          22 :         Sequence < sal_Int8 > aReadBuffer ( nSize );
     503             : 
     504          22 :         xStream->readBytes( aReadBuffer, nSize );
     505             : 
     506          22 :         bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData );
     507             :     }
     508             : 
     509          22 :     return bRet;
     510             : }
     511             : 
     512       70446 : 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 )
     519             : {
     520       70446 :     ::osl::MutexGuard aGuard( m_aMutex );
     521             : 
     522       70446 :     return new XUnbufferedStream ( m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode );
     523             : }
     524             : 
     525       11287 : ZipEnumeration * SAL_CALL ZipFile::entries(  )
     526             : {
     527       11287 :     return new ZipEnumeration ( aEntries );
     528             : }
     529             : 
     530        6907 : 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)
     535             : {
     536        6907 :     ::osl::MutexGuard aGuard( m_aMutex );
     537             : 
     538        6907 :     if ( rEntry.nOffset <= 0 )
     539        6907 :         readLOC( rEntry );
     540             : 
     541             :     // We want to return a rawStream if we either don't have a key or if the
     542             :     // key is wrong
     543             : 
     544        6907 :     bool bNeedRawStream = rEntry.nMethod == STORED;
     545             : 
     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        6907 :     if ( bIsEncrypted && rData.is() && rData->m_aDigest.getLength() )
     549           0 :         bNeedRawStream = !hasValidPassword ( rEntry, rData );
     550             : 
     551             :     return createUnbufferedStream ( aMutexHolder,
     552             :                                     rEntry,
     553             :                                     rData,
     554             :                                     bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
     555        6907 :                                     bIsEncrypted );
     556             : }
     557             : 
     558       63338 : 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 )
     566             : {
     567       63338 :     ::osl::MutexGuard aGuard( m_aMutex );
     568             : 
     569       63338 :     if ( rEntry.nOffset <= 0 )
     570       47193 :         readLOC( rEntry );
     571             : 
     572             :     // An exception must be thrown in case stream is encrypted and
     573             :     // there is no key or the key is wrong
     574       63338 :     bool bNeedRawStream = false;
     575       63338 :     if ( bIsEncrypted )
     576             :     {
     577             :         // in case no digest is provided there is no way
     578             :         // to detect password correctness
     579          22 :         if ( !rData.is() )
     580           0 :             throw ZipException("Encrypted stream without encryption data!" );
     581             : 
     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          22 :         if ( rData->m_aDigest.getLength() && !hasValidPassword ( rEntry, rData ) )
     586           4 :                 throw packages::WrongPasswordException(THROW_WHERE );
     587             :     }
     588             :     else
     589       63316 :         bNeedRawStream = ( rEntry.nMethod == STORED );
     590             : 
     591             :     return createUnbufferedStream ( aMutexHolder,
     592             :                                     rEntry,
     593             :                                     rData,
     594             :                                     bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
     595       63334 :                                     bIsEncrypted );
     596             : }
     597             : 
     598         204 : 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)
     603             : {
     604         204 :     ::osl::MutexGuard aGuard( m_aMutex );
     605             : 
     606         204 :     if ( rEntry.nOffset <= 0 )
     607         102 :         readLOC( rEntry );
     608             : 
     609         204 :     return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted );
     610             : }
     611             : 
     612           1 : 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 )
     621             : {
     622           1 :     ::osl::MutexGuard aGuard( m_aMutex );
     623             : 
     624           1 :     if ( !rData.is() )
     625           0 :         throw packages::NoEncryptionException(THROW_WHERE );
     626             : 
     627           1 :     if ( rEntry.nOffset <= 0 )
     628           1 :         readLOC( rEntry );
     629             : 
     630           1 :     return createUnbufferedStream ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, aMediaType );
     631             : }
     632             : 
     633       54203 : bool ZipFile::readLOC( ZipEntry &rEntry )
     634             :     throw(IOException, ZipException, RuntimeException)
     635             : {
     636       54203 :     ::osl::MutexGuard aGuard( m_aMutex );
     637             : 
     638       54203 :     sal_Int64 nPos = -rEntry.nOffset;
     639             : 
     640       54203 :     aGrabber.seek(nPos);
     641       54203 :     sal_Int32 nTestSig = aGrabber.ReadInt32();
     642             : 
     643       54203 :     if (nTestSig != LOCSIG)
     644           0 :         throw ZipIOException("Invalid LOC header (bad signature)" );
     645       54203 :     sal_Int16 nVersion = aGrabber.ReadInt16();
     646       54203 :     aGrabber.ReadInt16(); //flag
     647       54203 :     aGrabber.ReadInt16(); //how
     648       54203 :     aGrabber.ReadInt32(); //time
     649       54203 :     aGrabber.ReadInt32(); //crc
     650       54203 :     aGrabber.ReadInt32(); //compressed size
     651       54203 :     aGrabber.ReadInt32(); //size
     652       54203 :     sal_Int16 nPathLen = aGrabber.ReadInt16();
     653       54203 :     sal_Int16 nExtraLen = aGrabber.ReadInt16();
     654       54203 :     rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
     655             : 
     656             :     // FIXME64: need to read 64bit LOC
     657             : 
     658       54203 :     bool bBroken = false;
     659             : 
     660             :     try
     661             :     {
     662       54203 :         sal_Int16 nPathLenToRead = nPathLen;
     663       54203 :         const sal_Int64 nBytesAvailable = aGrabber.getLength() - aGrabber.getPosition();
     664       54203 :         if (nPathLenToRead > nBytesAvailable)
     665           0 :             nPathLenToRead = nBytesAvailable;
     666       54203 :         else if (nPathLenToRead < 0)
     667           0 :             nPathLenToRead = 0;
     668             : 
     669             :         // read always in UTF8, some tools seem not to set UTF8 bit
     670       54203 :         uno::Sequence<sal_Int8> aNameBuffer(nPathLenToRead);
     671       54203 :         sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLenToRead);
     672       54203 :         if (nRead < aNameBuffer.getLength())
     673           0 :             aNameBuffer.realloc(nRead);
     674             : 
     675       54203 :         OUString sLOCPath = OUString::intern( reinterpret_cast<char *>(aNameBuffer.getArray()),
     676             :                                                           aNameBuffer.getLength(),
     677      162609 :                                                           RTL_TEXTENCODING_UTF8 );
     678             : 
     679       54203 :         if ( rEntry.nPathLen == -1 ) // the file was created
     680             :         {
     681           0 :             rEntry.nPathLen = nPathLen;
     682           0 :             rEntry.sPath = sLOCPath;
     683             :         }
     684             : 
     685             :         // check basic local file header / entry consistency, just
     686             :         // plain ignore bits 1 & 2 of the flag field - they are either
     687             :         // purely informative, or even fully undefined (depending on
     688             :         // nMethod)
     689             :         // Do *not* compare nMethod / nHow, older versions with
     690             :         // encrypted streams write mismatching DEFLATE/STORE pairs
     691             :         // there.
     692             :         // Do *not* compare timestamps, since MSO 2010 can produce documents
     693             :         // with timestamp difference in the central directory entry and local
     694             :         // file header.
     695       54203 :         bBroken = rEntry.nVersion != nVersion
     696       54203 :                         || rEntry.nPathLen != nPathLen
     697      162609 :                         || !rEntry.sPath.equals( sLOCPath );
     698             :     }
     699           0 :     catch(...)
     700             :     {
     701           0 :         bBroken = true;
     702             :     }
     703             : 
     704       54203 :     if ( bBroken && !bRecoveryMode )
     705           0 :         throw ZipIOException("The stream seems to be broken!" );
     706             : 
     707       54203 :     return true;
     708             : }
     709             : 
     710       17464 : sal_Int32 ZipFile::findEND( )
     711             :     throw(IOException, ZipException, RuntimeException)
     712             : {
     713             :     // this method is called in constructor only, no need for mutex
     714             :     sal_Int32 nLength, nPos, nEnd;
     715       17464 :     Sequence < sal_Int8 > aBuffer;
     716             :     try
     717             :     {
     718       17464 :         nLength = static_cast <sal_Int32 > (aGrabber.getLength());
     719       17464 :         if (nLength == 0 || nLength < ENDHDR)
     720           1 :             return -1;
     721       17463 :         nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
     722       17463 :         nEnd = nPos >= 0 ? nPos : 0 ;
     723             : 
     724       17463 :         aGrabber.seek( nEnd );
     725       17463 :         aGrabber.readBytes ( aBuffer, nLength - nEnd );
     726             : 
     727       17463 :         const sal_Int8 *pBuffer = aBuffer.getConstArray();
     728             : 
     729       17463 :         nPos = nLength - nEnd - ENDHDR;
     730     2799245 :         while ( nPos >= 0 )
     731             :         {
     732     2776387 :             if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
     733       12068 :                 return nPos + nEnd;
     734     2764319 :             nPos--;
     735             :         }
     736             :     }
     737           0 :     catch ( IllegalArgumentException& )
     738             :     {
     739           0 :         throw ZipException("Zip END signature not found!" );
     740             :     }
     741           0 :     catch ( NotConnectedException& )
     742             :     {
     743           0 :         throw ZipException("Zip END signature not found!" );
     744             :     }
     745           0 :     catch ( BufferSizeExceededException& )
     746             :     {
     747           0 :         throw ZipException("Zip END signature not found!" );
     748             :     }
     749        5395 :     throw ZipException("Zip END signature not found!" );
     750             : }
     751             : 
     752       17464 : sal_Int32 ZipFile::readCEN()
     753             :     throw(IOException, ZipException, RuntimeException)
     754             : {
     755             :     // this method is called in constructor only, no need for mutex
     756       17464 :     sal_Int32 nCenPos = -1, nEndPos, nLocPos;
     757             :     sal_uInt16 nCount;
     758             : 
     759             :     try
     760             :     {
     761       17464 :         nEndPos = findEND();
     762       12069 :         if (nEndPos == -1)
     763           1 :             return -1;
     764       12068 :         aGrabber.seek(nEndPos + ENDTOT);
     765       12068 :         sal_uInt16 nTotal = aGrabber.ReadUInt16();
     766       12068 :         sal_Int32 nCenLen = aGrabber.ReadInt32();
     767       12068 :         sal_Int32 nCenOff = aGrabber.ReadInt32();
     768             : 
     769       12068 :         if ( nTotal * CENHDR > nCenLen )
     770           0 :             throw ZipException("invalid END header (bad entry count)" );
     771             : 
     772       12068 :         if ( nTotal > ZIP_MAXENTRIES )
     773           0 :             throw ZipException("too many entries in ZIP File" );
     774             : 
     775       12068 :         if ( nCenLen < 0 || nCenLen > nEndPos )
     776           0 :             throw ZipException("Invalid END header (bad central directory size)" );
     777             : 
     778       12068 :         nCenPos = nEndPos - nCenLen;
     779             : 
     780       12068 :         if ( nCenOff < 0 || nCenOff > nCenPos )
     781           0 :             throw ZipException("Invalid END header (bad central directory size)" );
     782             : 
     783       12068 :         nLocPos = nCenPos - nCenOff;
     784       12068 :         aGrabber.seek( nCenPos );
     785       12068 :         Sequence < sal_Int8 > aCENBuffer ( nCenLen );
     786       12068 :         sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
     787       12068 :         if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
     788           0 :             throw ZipException ("Error reading CEN into memory buffer!" );
     789             : 
     790       24136 :         MemoryByteGrabber aMemGrabber ( aCENBuffer );
     791             : 
     792       24136 :         ZipEntry aEntry;
     793             :         sal_Int16 nCommentLen;
     794             : 
     795      865971 :         for (nCount = 0 ; nCount < nTotal; nCount++)
     796             :         {
     797      853903 :             sal_Int32 nTestSig = aMemGrabber.ReadInt32();
     798      853903 :             if ( nTestSig != CENSIG )
     799           0 :                 throw ZipException("Invalid CEN header (bad signature)" );
     800             : 
     801      853903 :             aMemGrabber.skipBytes ( 2 );
     802      853903 :             aEntry.nVersion = aMemGrabber.ReadInt16();
     803             : 
     804      853903 :             if ( ( aEntry.nVersion & 1 ) == 1 )
     805           0 :                 throw ZipException("Invalid CEN header (encrypted entry)" );
     806             : 
     807      853903 :             aEntry.nFlag = aMemGrabber.ReadInt16();
     808      853903 :             aEntry.nMethod = aMemGrabber.ReadInt16();
     809             : 
     810      853903 :             if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
     811           0 :                 throw ZipException("Invalid CEN header (bad compression method)" );
     812             : 
     813      853903 :             aEntry.nTime = aMemGrabber.ReadInt32();
     814      853903 :             aEntry.nCrc = aMemGrabber.ReadInt32();
     815             : 
     816      853903 :             sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
     817      853903 :             sal_uInt32 nSize = aMemGrabber.ReadUInt32();
     818      853903 :             aEntry.nPathLen = aMemGrabber.ReadInt16();
     819      853903 :             aEntry.nExtraLen = aMemGrabber.ReadInt16();
     820      853903 :             nCommentLen = aMemGrabber.ReadInt16();
     821      853903 :             aMemGrabber.skipBytes ( 8 );
     822      853903 :             sal_uInt32 nOffset = aMemGrabber.ReadUInt32();
     823             : 
     824             :             // FIXME64: need to read the 64bit header instead
     825      853903 :             if ( nSize == 0xffffffff ||
     826      853903 :                  nOffset == 0xffffffff ||
     827             :                  nCompressedSize == 0xffffffff ) {
     828           0 :                 throw ZipException("PK64 zip file entry" );
     829             :             } else {
     830      853903 :                 aEntry.nCompressedSize = nCompressedSize;
     831      853903 :                 aEntry.nSize = nSize;
     832      853903 :                 aEntry.nOffset = nOffset;
     833             :             }
     834             : 
     835      853903 :             aEntry.nOffset += nLocPos;
     836      853903 :             aEntry.nOffset *= -1;
     837             : 
     838      853903 :             if ( aEntry.nPathLen < 0 )
     839           0 :                 throw ZipException("unexpected name length" );
     840             : 
     841      853903 :             if ( nCommentLen < 0 )
     842           0 :                 throw ZipException("unexpected comment length" );
     843             : 
     844      853903 :             if ( aEntry.nExtraLen < 0 )
     845           0 :                 throw ZipException("unexpected extra header info length" );
     846             : 
     847             :             // read always in UTF8, some tools seem not to set UTF8 bit
     848     1707806 :             aEntry.sPath = OUString::intern ( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
     849             :                                                    aEntry.nPathLen,
     850      853903 :                                                    RTL_TEXTENCODING_UTF8 );
     851             : 
     852      853903 :             if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
     853           0 :                 throw ZipException("Zip entry has an invalid name." );
     854             : 
     855      853903 :             aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
     856      853903 :             aEntries[aEntry.sPath] = aEntry;
     857             :         }
     858             : 
     859       12068 :         if (nCount != nTotal)
     860       12068 :             throw ZipException("Count != Total" );
     861             :     }
     862           0 :     catch ( IllegalArgumentException & )
     863             :     {
     864             :         // seek can throw this...
     865           0 :         nCenPos = -1; // make sure we return -1 to indicate an error
     866             :     }
     867       12068 :     return nCenPos;
     868             : }
     869             : 
     870           0 : sal_Int32 ZipFile::recover()
     871             :     throw(IOException, ZipException, RuntimeException)
     872             : {
     873           0 :     ::osl::MutexGuard aGuard( m_aMutex );
     874             : 
     875             :     sal_Int64 nLength;
     876           0 :     Sequence < sal_Int8 > aBuffer;
     877             : 
     878             :     try
     879             :     {
     880           0 :         nLength = aGrabber.getLength();
     881           0 :         if (nLength == 0 || nLength < ENDHDR)
     882           0 :             return -1;
     883             : 
     884           0 :         aGrabber.seek( 0 );
     885             : 
     886           0 :         const sal_Int64 nToRead = 32000;
     887           0 :         for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
     888             :         {
     889           0 :             const sal_Int8 *pBuffer = aBuffer.getConstArray();
     890           0 :             sal_Int32 nBufSize = aBuffer.getLength();
     891             : 
     892           0 :             sal_Int64 nPos = 0;
     893             :             // the buffer should contain at least one header,
     894             :             // or if it is end of the file, at least the postheader with sizes and hash
     895           0 :             while( nPos < nBufSize - 30
     896           0 :                 || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
     897             : 
     898             :             {
     899           0 :                 if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
     900             :                 {
     901           0 :                     ZipEntry aEntry;
     902           0 :                     MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( &(pBuffer[nPos+4]), 26 ) );
     903             : 
     904           0 :                     aEntry.nVersion = aMemGrabber.ReadInt16();
     905           0 :                     if ( ( aEntry.nVersion & 1 ) != 1 )
     906             :                     {
     907           0 :                         aEntry.nFlag = aMemGrabber.ReadInt16();
     908           0 :                         aEntry.nMethod = aMemGrabber.ReadInt16();
     909             : 
     910           0 :                         if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
     911             :                         {
     912           0 :                             aEntry.nTime = aMemGrabber.ReadInt32();
     913           0 :                             aEntry.nCrc = aMemGrabber.ReadInt32();
     914           0 :                             sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
     915           0 :                             sal_uInt32 nSize = aMemGrabber.ReadUInt32();
     916           0 :                             aEntry.nPathLen = aMemGrabber.ReadInt16();
     917           0 :                             aEntry.nExtraLen = aMemGrabber.ReadInt16();
     918             : 
     919             :                             // FIXME64: need to read the 64bit header instead
     920           0 :                             if ( nSize == 0xffffffff ||
     921             :                                  nCompressedSize == 0xffffffff ) {
     922           0 :                                 throw ZipException("PK64 zip file entry" );
     923             :                             } else {
     924           0 :                                 aEntry.nCompressedSize = nCompressedSize;
     925           0 :                                 aEntry.nSize = nSize;
     926             :                             }
     927             : 
     928             :                             sal_Int32 nDescrLength =
     929           0 :                                 ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
     930             : 
     931           0 :                             sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
     932           0 :                             sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
     933           0 :                             if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
     934           0 :                                 && ( nGenPos + nPos + nBlockLength ) <= nLength )
     935             :                             {
     936             :                                 // read always in UTF8, some tools seem not to set UTF8 bit
     937           0 :                                 if( nPos + 30 + aEntry.nPathLen <= nBufSize )
     938           0 :                                     aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]),
     939             :                                                               aEntry.nPathLen,
     940           0 :                                                               RTL_TEXTENCODING_UTF8 );
     941             :                                 else
     942             :                                 {
     943           0 :                                     Sequence < sal_Int8 > aFileName;
     944           0 :                                     aGrabber.seek( nGenPos + nPos + 30 );
     945           0 :                                     aGrabber.readBytes( aFileName, aEntry.nPathLen );
     946           0 :                                     aEntry.sPath = OUString ( reinterpret_cast<char *>(aFileName.getArray()),
     947             :                                                               aFileName.getLength(),
     948           0 :                                                               RTL_TEXTENCODING_UTF8 );
     949           0 :                                     aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
     950             :                                 }
     951             : 
     952           0 :                                 aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
     953             : 
     954           0 :                                 if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
     955             :                                 {
     956           0 :                                     aEntry.nCrc = 0;
     957           0 :                                     aEntry.nCompressedSize = 0;
     958           0 :                                     aEntry.nSize = 0;
     959             :                                 }
     960             : 
     961           0 :                                 if ( aEntries.find( aEntry.sPath ) == aEntries.end() )
     962           0 :                                     aEntries[aEntry.sPath] = aEntry;
     963             :                             }
     964             :                         }
     965             :                     }
     966             : 
     967           0 :                     nPos += 4;
     968             :                 }
     969           0 :                 else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
     970             :                 {
     971             :                     sal_Int64 nCompressedSize, nSize;
     972           0 :                     MemoryByteGrabber aMemGrabber ( Sequence< sal_Int8 >( &(pBuffer[nPos+4]), 12 ) );
     973           0 :                     sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
     974           0 :                     sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32();
     975           0 :                     sal_uInt32 nSize32 = aMemGrabber.ReadUInt32();
     976             : 
     977             :                     // FIXME64: work to be done here ...
     978           0 :                     nCompressedSize = nCompressedSize32;
     979           0 :                     nSize = nSize32;
     980             : 
     981           0 :                     for( EntryHash::iterator aIter = aEntries.begin(); aIter != aEntries.end(); ++aIter )
     982             :                     {
     983           0 :                         ZipEntry aTmp = (*aIter).second;
     984             : 
     985             :                         // this is a broken package, accept this block not only for DEFLATED streams
     986           0 :                         if( (*aIter).second.nFlag & 8 )
     987             :                         {
     988           0 :                             sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
     989           0 :                             if ( nStreamOffset == (*aIter).second.nOffset && nCompressedSize > (*aIter).second.nCompressedSize )
     990             :                             {
     991             :                                 // only DEFLATED blocks need to be checked
     992           0 :                                 bool bAcceptBlock = ( (*aIter).second.nMethod == STORED && nCompressedSize == nSize );
     993             : 
     994           0 :                                 if ( !bAcceptBlock )
     995             :                                 {
     996           0 :                                     sal_Int64 nRealSize = 0;
     997           0 :                                     sal_Int32 nRealCRC = 0;
     998           0 :                                     getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
     999           0 :                                     bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
    1000             :                                 }
    1001             : 
    1002           0 :                                 if ( bAcceptBlock )
    1003             :                                 {
    1004           0 :                                     (*aIter).second.nCrc = nCRC32;
    1005           0 :                                     (*aIter).second.nCompressedSize = nCompressedSize;
    1006           0 :                                     (*aIter).second.nSize = nSize;
    1007             :                                 }
    1008             :                             }
    1009             : #if 0
    1010             : // for now ignore clearly broken streams
    1011             :                             else if( !(*aIter).second.nCompressedSize )
    1012             :                             {
    1013             :                                 (*aIter).second.nCrc = nCRC32;
    1014             :                                 sal_Int32 nRealStreamSize = nGenPos + nPos - (*aIter).second.nOffset;
    1015             :                                 (*aIter).second.nCompressedSize = nGenPos + nPos - (*aIter).second.nOffset;
    1016             :                                 (*aIter).second.nSize = nSize;
    1017             :                             }
    1018             : #endif
    1019             :                         }
    1020           0 :                     }
    1021             : 
    1022           0 :                     nPos += 4;
    1023             :                 }
    1024             :                 else
    1025           0 :                     nPos++;
    1026             :             }
    1027             : 
    1028           0 :             nGenPos += nPos;
    1029           0 :             aGrabber.seek( nGenPos );
    1030             :         }
    1031             : 
    1032           0 :         return 0;
    1033             :     }
    1034           0 :     catch ( IllegalArgumentException& )
    1035             :     {
    1036           0 :         throw ZipException("Zip END signature not found!" );
    1037             :     }
    1038           0 :     catch ( NotConnectedException& )
    1039             :     {
    1040           0 :         throw ZipException("Zip END signature not found!" );
    1041             :     }
    1042           0 :     catch ( BufferSizeExceededException& )
    1043             :     {
    1044           0 :         throw ZipException("Zip END signature not found!" );
    1045           0 :     }
    1046             : }
    1047             : 
    1048           0 : bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
    1049             : {
    1050           0 :     ::osl::MutexGuard aGuard( m_aMutex );
    1051             : 
    1052           0 :     sal_Int32 nCRC = 0;
    1053           0 :     sal_Int64 nSize = 0;
    1054             : 
    1055           0 :     if( aEntry.nMethod == STORED )
    1056           0 :         return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
    1057             : 
    1058           0 :     getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
    1059           0 :     return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
    1060             : }
    1061             : 
    1062           0 : sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
    1063             : {
    1064           0 :     ::osl::MutexGuard aGuard( m_aMutex );
    1065             : 
    1066           0 :     Sequence < sal_Int8 > aBuffer;
    1067           0 :     CRC32 aCRC;
    1068           0 :     sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
    1069             : 
    1070           0 :     aGrabber.seek( nOffset );
    1071           0 :     for (sal_Int64 ind = 0;
    1072           0 :          aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
    1073             :          ++ind)
    1074             :     {
    1075           0 :         sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize);
    1076           0 :         aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen));
    1077             :     }
    1078             : 
    1079           0 :     return aCRC.getValue();
    1080             : }
    1081             : 
    1082           0 : void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
    1083             : {
    1084           0 :     ::osl::MutexGuard aGuard( m_aMutex );
    1085             : 
    1086           0 :     Sequence < sal_Int8 > aBuffer;
    1087           0 :     CRC32 aCRC;
    1088           0 :     sal_Int64 nRealSize = 0;
    1089           0 :     Inflater aInflaterLocal( true );
    1090           0 :     sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
    1091             : 
    1092           0 :     aGrabber.seek( nOffset );
    1093           0 :     for ( sal_Int64 ind = 0;
    1094           0 :           !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
    1095             :           ind++ )
    1096             :     {
    1097           0 :         Sequence < sal_Int8 > aData( nBlockSize );
    1098           0 :         sal_Int32 nLastInflated = 0;
    1099           0 :         sal_Int64 nInBlock = 0;
    1100             : 
    1101           0 :         aInflaterLocal.setInput( aBuffer );
    1102           0 :         do
    1103             :         {
    1104           0 :             nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
    1105           0 :             aCRC.updateSegment( aData, nLastInflated );
    1106           0 :             nInBlock += nLastInflated;
    1107           0 :         } while( !aInflater.finished() && nLastInflated );
    1108             : 
    1109           0 :         nRealSize += nInBlock;
    1110           0 :     }
    1111             : 
    1112           0 :     *nSize = nRealSize;
    1113           0 :     *nCRC = aCRC.getValue();
    1114           0 : }
    1115             : 
    1116             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11