diff options
Diffstat (limited to '')
-rw-r--r-- | package/source/zipapi/ZipFile.cxx | 1263 |
1 files changed, 1263 insertions, 0 deletions
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx new file mode 100644 index 000000000..79dc5bb68 --- /dev/null +++ b/package/source/zipapi/ZipFile.cxx @@ -0,0 +1,1263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/packages/NoEncryptionException.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/packages/zip/ZipConstants.hpp> +#include <com/sun/star/packages/zip/ZipException.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> +#include <com/sun/star/xml/crypto/XCipherContext.hpp> +#include <com/sun/star/xml/crypto/XDigestContext.hpp> +#include <com/sun/star/xml/crypto/CipherID.hpp> +#include <com/sun/star/xml/crypto/DigestID.hpp> +#include <com/sun/star/xml/crypto/NSSInitializer.hpp> + +#include <comphelper/bytereader.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <rtl/digest.h> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <algorithm> +#include <iterator> +#include <vector> + +#include "blowfishcontext.hxx" +#include "sha1context.hxx" +#include <ZipFile.hxx> +#include <ZipEnumeration.hxx> +#include "XUnbufferedStream.hxx" +#include "XBufferedThreadedStream.hxx" +#include <PackageConstants.hxx> +#include <EncryptedDataHeader.hxx> +#include <EncryptionData.hxx> +#include "MemoryByteGrabber.hxx" + +#include <CRC32.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::io; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::packages; +using namespace com::sun::star::packages::zip; +using namespace com::sun::star::packages::zip::ZipConstants; + +using ZipUtils::Inflater; + +#if OSL_DEBUG_LEVEL > 0 +#define THROW_WHERE SAL_WHERE +#else +#define THROW_WHERE "" +#endif + +/** This class is used to read entries from a zip file + */ +ZipFile::ZipFile( const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder, + uno::Reference < XInputStream > const &xInput, + const uno::Reference < XComponentContext > & rxContext, + bool bInitialise ) +: m_aMutexHolder( aMutexHolder ) +, aGrabber( xInput ) +, aInflater( true ) +, xStream(xInput) +, m_xContext ( rxContext ) +, bRecoveryMode( false ) +{ + if (bInitialise && readCEN() == -1 ) + { + aEntries.clear(); + throw ZipException( "stream data looks to be broken" ); + } +} + +ZipFile::ZipFile( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder, + uno::Reference < XInputStream > const &xInput, + const uno::Reference < XComponentContext > & rxContext, + bool bInitialise, bool bForceRecovery) +: m_aMutexHolder( aMutexHolder ) +, aGrabber( xInput ) +, aInflater( true ) +, xStream(xInput) +, m_xContext ( rxContext ) +, bRecoveryMode( bForceRecovery ) +{ + if (bInitialise) + { + if ( bForceRecovery ) + { + recover(); + } + else if ( readCEN() == -1 ) + { + aEntries.clear(); + throw ZipException("stream data looks to be broken" ); + } + } +} + +ZipFile::~ZipFile() +{ + aEntries.clear(); +} + +void ZipFile::setInputStream ( const uno::Reference < XInputStream >& xNewStream ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + xStream = xNewStream; + aGrabber.setInputStream ( xStream ); +} + +uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData ) +{ + uno::Reference< xml::crypto::XDigestContext > xDigestContext; + if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K ) + { + uno::Reference< uno::XComponentContext > xContext = xArgContext; + if ( !xContext.is() ) + xContext = comphelper::getProcessComponentContext(); + + uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext ); + + xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW ); + } + else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K ) + { + if (xEncryptionData->m_bTryWrongSHA1) + { + xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW); + } + else + { + xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW); + } + } + + return xDigestContext; +} + +uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt ) +{ + uno::Reference< xml::crypto::XCipherContext > xResult; + + if (xEncryptionData->m_nDerivedKeySize < 0) + { + throw ZipIOException("Invalid derived key length!" ); + } + + uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize ); + if ( !xEncryptionData->m_nIterationCount && + xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength() ) + { + // gpg4libre: no need to derive key, m_aKey is already + // usable as symmetric session key + aDerivedKey = xEncryptionData->m_aKey; + } + else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ), + aDerivedKey.getLength(), + reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ), + xEncryptionData->m_aKey.getLength(), + reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ), + xEncryptionData->m_aSalt.getLength(), + xEncryptionData->m_nIterationCount ) ) + { + throw ZipIOException("Can not create derived key!" ); + } + + if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING ) + { + uno::Reference< uno::XComponentContext > xContext = xArgContext; + if ( !xContext.is() ) + xContext = comphelper::getProcessComponentContext(); + + uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext ); + + xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() ); + } + else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 ) + { + xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt ); + } + else + { + throw ZipIOException("Unknown cipher algorithm is requested!" ); + } + + return xResult; +} + +void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData, + sal_Int64 nSize, + const OUString& aMediaType, + sal_Int8 * & pHeader ) +{ + // I think it's safe to restrict vector and salt length to 2 bytes ! + sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() ); + sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() ); + sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() ); + sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) ); + + // First the header + *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF; + *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF; + *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF; + *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF; + + // Then the version + *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF; + *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF; + + // Then the iteration Count + sal_Int32 nIterationCount = rData->m_nIterationCount; + *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF); + + // FIXME64: need to handle larger sizes + // Then the size: + *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF); + + // Then the encryption algorithm + sal_Int32 nEncAlgID = rData->m_nEncAlg; + *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF); + + // Then the checksum algorithm + sal_Int32 nChecksumAlgID = rData->m_nCheckAlg; + *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF); + + // Then the derived key size + sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize; + *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF); + + // Then the start key generation algorithm + sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID; + *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF); + + // Then the salt length + *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF); + + // Then the IV length + *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF); + + // Then the digest length + *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF); + + // Then the mediatype length + *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF); + *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF); + + // Then the salt content + memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength ); + pHeader += nSaltLength; + + // Then the IV content + memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength ); + pHeader += nIVLength; + + // Then the digest content + memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength ); + pHeader += nDigestLength; + + // Then the mediatype itself + memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength ); + pHeader += nMediaTypeLength; +} + +bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > const & rData, + sal_Int32 &rEncAlg, + sal_Int32 &rChecksumAlg, + sal_Int32 &rDerivedKeySize, + sal_Int32 &rStartKeyGenID, + sal_Int32 &rSize, + OUString& aMediaType, + const uno::Reference< XInputStream >& rStream ) +{ + bool bOk = false; + const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4; + Sequence < sal_Int8 > aBuffer ( nHeaderSize ); + if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) ) + { + sal_Int16 nPos = 0; + sal_Int8 *pBuffer = aBuffer.getArray(); + sal_Int16 nVersion = pBuffer[nPos++] & 0xFF; + nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8; + if ( nVersion == n_ConstCurrentVersion ) + { + sal_Int32 nCount = pBuffer[nPos++] & 0xFF; + nCount |= ( pBuffer[nPos++] & 0xFF ) << 8; + nCount |= ( pBuffer[nPos++] & 0xFF ) << 16; + nCount |= ( pBuffer[nPos++] & 0xFF ) << 24; + rData->m_nIterationCount = nCount; + + rSize = pBuffer[nPos++] & 0xFF; + rSize |= ( pBuffer[nPos++] & 0xFF ) << 8; + rSize |= ( pBuffer[nPos++] & 0xFF ) << 16; + rSize |= ( pBuffer[nPos++] & 0xFF ) << 24; + + rEncAlg = pBuffer[nPos++] & 0xFF; + rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 8; + rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 16; + rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 24; + + rChecksumAlg = pBuffer[nPos++] & 0xFF; + rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 8; + rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 16; + rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 24; + + rDerivedKeySize = pBuffer[nPos++] & 0xFF; + rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 8; + rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 16; + rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 24; + + rStartKeyGenID = pBuffer[nPos++] & 0xFF; + rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 8; + rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 16; + rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 24; + + sal_Int16 nSaltLength = pBuffer[nPos++] & 0xFF; + nSaltLength |= ( pBuffer[nPos++] & 0xFF ) << 8; + sal_Int16 nIVLength = ( pBuffer[nPos++] & 0xFF ); + nIVLength |= ( pBuffer[nPos++] & 0xFF ) << 8; + sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF; + nDigestLength |= ( pBuffer[nPos++] & 0xFF ) << 8; + + sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF; + nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8; + + if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) ) + { + rData->m_aSalt.realloc ( nSaltLength ); + memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength ); + if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) ) + { + rData->m_aInitVector.realloc ( nIVLength ); + memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength ); + if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) ) + { + rData->m_aDigest.realloc ( nDigestLength ); + memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength ); + + if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) ) + { + aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()), + nMediaTypeLength / sizeof( sal_Unicode ) ); + bOk = true; + } + } + } + } + } + } + return bOk; +} + +uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< XInputStream >& xStream, + const ::rtl::Reference< EncryptionData > &rData ) +{ + if ( !rData.is() ) + throw ZipIOException("Encrypted stream without encryption data!" ); + + if ( !rData->m_aKey.hasElements() ) + throw packages::WrongPasswordException(THROW_WHERE ); + + uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY ); + if ( !xSeek.is() ) + throw ZipIOException("The stream must be seekable!" ); + + // if we have a digest, then this file is an encrypted one and we should + // check if we can decrypt it or not + OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" ); + if ( rData->m_aDigest.hasElements() ) + { + sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() ); + if ( nSize > n_ConstDigestLength + 32 ) + nSize = n_ConstDigestLength + 32; + + // skip header + xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() + + rData->m_aSalt.getLength() + rData->m_aDigest.getLength() ); + + // Only want to read enough to verify the digest + Sequence < sal_Int8 > aReadBuffer ( nSize ); + + xStream->readBytes( aReadBuffer, nSize ); + + if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) ) + throw packages::WrongPasswordException(THROW_WHERE ); + } + + return new XUnbufferedStream( aMutexHolder, xStream, rData ); +} + +#if 0 +// for debugging purposes +void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence ) +{ + if ( aSequence.getLength() ) + { + sal_Int32* pPointer = *( (sal_Int32**)&aSequence ); + sal_Int32 nSize = *( pPointer + 1 ); + sal_Int32 nMemSize = *( pPointer - 2 ); + sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) ); + OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" ); + } +} +#endif + +bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData ) +{ + if ( !rData.is() || !rData->m_aKey.hasElements() ) + return false; + + bool bRet = false; + + uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW ); + + uno::Sequence< sal_Int8 > aDecryptBuffer; + uno::Sequence< sal_Int8 > aDecryptBuffer2; + try + { + aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer ); + aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose(); + } + catch( uno::Exception& ) + { + // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream + // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks ) + } + + if ( aDecryptBuffer2.hasElements() ) + { + sal_Int32 nOldLen = aDecryptBuffer.getLength(); + aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() ); + memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getConstArray(), aDecryptBuffer2.getLength() ); + } + + if ( aDecryptBuffer.getLength() > n_ConstDigestLength ) + aDecryptBuffer.realloc( n_ConstDigestLength ); + + uno::Sequence< sal_Int8 > aDigestSeq; + uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW ); + + xDigestContext->updateDigest( aDecryptBuffer ); + aDigestSeq = xDigestContext->finalizeDigestAndDispose(); + + // If we don't have a digest, then we have to assume that the password is correct + if ( rData->m_aDigest.hasElements() && + ( aDigestSeq.getLength() != rData->m_aDigest.getLength() || + 0 != memcmp ( aDigestSeq.getConstArray(), + rData->m_aDigest.getConstArray(), + aDigestSeq.getLength() ) ) ) + { + // We should probably tell the user that the password they entered was wrong + } + else + bRet = true; + + return bRet; +} + +bool ZipFile::hasValidPassword ( ZipEntry const & rEntry, const ::rtl::Reference< EncryptionData >& rData ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + bool bRet = false; + if ( rData.is() && rData->m_aKey.hasElements() ) + { + css::uno::Reference < css::io::XSeekable > xSeek(xStream, UNO_QUERY_THROW); + xSeek->seek( rEntry.nOffset ); + sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize; + + // Only want to read enough to verify the digest + if ( nSize > n_ConstDigestDecrypt ) + nSize = n_ConstDigestDecrypt; + + Sequence < sal_Int8 > aReadBuffer ( nSize ); + + xStream->readBytes( aReadBuffer, nSize ); + + bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData ); + } + + return bRet; +} + +namespace { + +class XBufferedStream : public cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable> +{ + std::vector<sal_Int8> maBytes; + size_t mnPos; + + size_t remainingSize() const + { + return maBytes.size() - mnPos; + } + + bool hasBytes() const + { + return mnPos < maBytes.size(); + } + +public: + XBufferedStream( const uno::Reference<XInputStream>& xSrcStream ) : mnPos(0) + { + sal_Int32 nRemaining = xSrcStream->available(); + uno::Reference< css::lang::XUnoTunnel > xInputTunnel( xSrcStream, uno::UNO_QUERY ); + comphelper::ByteReader* pByteReader = nullptr; + if (xInputTunnel) + pByteReader = reinterpret_cast< comphelper::ByteReader* >( xInputTunnel->getSomething( comphelper::ByteReader::getUnoTunnelId() ) ); + + if (pByteReader) + { + maBytes.resize(nRemaining); + + sal_Int8* pData = maBytes.data(); + while (nRemaining > 0) + { + sal_Int32 nRead = pByteReader->readSomeBytes(pData, nRemaining); + nRemaining -= nRead; + pData += nRead; + } + } + else + { + const sal_Int32 nBufSize = 8192; + + sal_Int32 nRead = 0; + maBytes.reserve(nRemaining); + uno::Sequence<sal_Int8> aBuf(nBufSize); + + auto readAndCopy = [&]( sal_Int32 nReadSize ) -> sal_Int32 + { + sal_Int32 nBytes = xSrcStream->readBytes(aBuf, nReadSize); + const sal_Int8* p = aBuf.getConstArray(); + const sal_Int8* pEnd = p + nBytes; + maBytes.insert( maBytes.end(), p, pEnd ); + return nBytes; + }; + + while (nRemaining > nBufSize) + { + const auto nBytes = readAndCopy(nBufSize); + if (!nBytes) + break; + nRead += nBytes; + nRemaining -= nBytes; + } + + if (nRemaining) + nRead += readAndCopy(nRemaining); + maBytes.resize(nRead); + } + } + + virtual sal_Int32 SAL_CALL readBytes( uno::Sequence<sal_Int8>& rData, sal_Int32 nBytesToRead ) override + { + if (!hasBytes()) + return 0; + + sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize()); + rData.realloc(nReadSize); + auto pData = rData.getArray(); + std::vector<sal_Int8>::const_iterator it = maBytes.cbegin(); + std::advance(it, mnPos); + for (sal_Int32 i = 0; i < nReadSize; ++i, ++it) + pData[i] = *it; + + mnPos += nReadSize; + + return nReadSize; + } + + virtual sal_Int32 SAL_CALL readSomeBytes( ::css::uno::Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead ) override + { + return readBytes(rData, nMaxBytesToRead); + } + + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override + { + if (!hasBytes()) + return; + + mnPos += nBytesToSkip; + } + + virtual sal_Int32 SAL_CALL available() override + { + if (!hasBytes()) + return 0; + + return remainingSize(); + } + + virtual void SAL_CALL closeInput() override + { + } + // XSeekable + virtual void SAL_CALL seek( sal_Int64 location ) override + { + if ( location < 0 || o3tl::make_unsigned(location) > maBytes.size() ) + throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + mnPos = location; + } + virtual sal_Int64 SAL_CALL getPosition() override + { + return mnPos; + } + virtual sal_Int64 SAL_CALL getLength() override + { + return maBytes.size(); + } +}; + +} + +uno::Reference< XInputStream > ZipFile::createStreamForZipEntry( + const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder, + ZipEntry const & rEntry, + const ::rtl::Reference< EncryptionData > &rData, + sal_Int8 nStreamMode, + bool bIsEncrypted, + const bool bUseBufferedStream, + const OUString& aMediaType ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + rtl::Reference< XUnbufferedStream > xSrcStream = new XUnbufferedStream( + m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode); + + if (!bUseBufferedStream) + return xSrcStream; + + uno::Reference<io::XInputStream> xBufStream; +#ifndef EMSCRIPTEN + static const sal_Int32 nThreadingThreshold = 10000; + + if( xSrcStream->available() > nThreadingThreshold ) + xBufStream = new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize()); + else +#endif + xBufStream = new XBufferedStream(xSrcStream); + + return xBufStream; +} + +ZipEnumeration ZipFile::entries() +{ + return aEntries; +} + +uno::Reference< XInputStream > ZipFile::getInputStream( ZipEntry& rEntry, + const ::rtl::Reference< EncryptionData > &rData, + bool bIsEncrypted, + const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + if ( rEntry.nOffset <= 0 ) + readLOC( rEntry ); + + // We want to return a rawStream if we either don't have a key or if the + // key is wrong + + bool bNeedRawStream = rEntry.nMethod == STORED; + + // if we have a digest, then this file is an encrypted one and we should + // check if we can decrypt it or not + if ( bIsEncrypted && rData.is() && rData->m_aDigest.hasElements() ) + bNeedRawStream = !hasValidPassword ( rEntry, rData ); + + return createStreamForZipEntry ( aMutexHolder, + rEntry, + rData, + bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA, + bIsEncrypted ); +} + +uno::Reference< XInputStream > ZipFile::getDataStream( ZipEntry& rEntry, + const ::rtl::Reference< EncryptionData > &rData, + bool bIsEncrypted, + const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + if ( rEntry.nOffset <= 0 ) + readLOC( rEntry ); + + // An exception must be thrown in case stream is encrypted and + // there is no key or the key is wrong + bool bNeedRawStream = false; + if ( bIsEncrypted ) + { + // in case no digest is provided there is no way + // to detect password correctness + if ( !rData.is() ) + throw ZipException("Encrypted stream without encryption data!" ); + + // if we have a digest, then this file is an encrypted one and we should + // check if we can decrypt it or not + OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" ); + if ( rData->m_aDigest.hasElements() && !hasValidPassword ( rEntry, rData ) ) + throw packages::WrongPasswordException(THROW_WHERE ); + } + else + bNeedRawStream = ( rEntry.nMethod == STORED ); + + return createStreamForZipEntry ( aMutexHolder, + rEntry, + rData, + bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA, + bIsEncrypted ); +} + +uno::Reference< XInputStream > ZipFile::getRawData( ZipEntry& rEntry, + const ::rtl::Reference< EncryptionData >& rData, + bool bIsEncrypted, + const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder, + const bool bUseBufferedStream ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + if ( rEntry.nOffset <= 0 ) + readLOC( rEntry ); + + return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted, bUseBufferedStream ); +} + +uno::Reference< XInputStream > ZipFile::getWrappedRawStream( + ZipEntry& rEntry, + const ::rtl::Reference< EncryptionData >& rData, + const OUString& aMediaType, + const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + if ( !rData.is() ) + throw packages::NoEncryptionException(THROW_WHERE ); + + if ( rEntry.nOffset <= 0 ) + readLOC( rEntry ); + + return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, true, aMediaType ); +} + +void ZipFile::readLOC( ZipEntry &rEntry ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + sal_Int64 nPos = -rEntry.nOffset; + + aGrabber.seek(nPos); + sal_Int32 nTestSig = aGrabber.ReadInt32(); + if (nTestSig != LOCSIG) + throw ZipIOException("Invalid LOC header (bad signature)" ); + + // Ignore all (duplicated) information from the local file header. + // various programs produced "broken" zip files; even LO at some point. + // Just verify the path and calculate the data offset and otherwise + // rely on the central directory info. + + aGrabber.ReadInt16(); //version + aGrabber.ReadInt16(); //flag + aGrabber.ReadInt16(); //how + aGrabber.ReadInt32(); //time + aGrabber.ReadInt32(); //crc + aGrabber.ReadInt32(); //compressed size + aGrabber.ReadInt32(); //size + sal_Int16 nPathLen = aGrabber.ReadInt16(); + sal_Int16 nExtraLen = aGrabber.ReadInt16(); + rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen; + + // FIXME64: need to read 64bit LOC + + bool bBroken = false; + + try + { + sal_Int16 nPathLenToRead = nPathLen; + const sal_Int64 nBytesAvailable = aGrabber.getLength() - aGrabber.getPosition(); + if (nPathLenToRead > nBytesAvailable) + nPathLenToRead = nBytesAvailable; + else if (nPathLenToRead < 0) + nPathLenToRead = 0; + + // read always in UTF8, some tools seem not to set UTF8 bit + uno::Sequence<sal_Int8> aNameBuffer(nPathLenToRead); + sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLenToRead); + if (nRead < aNameBuffer.getLength()) + aNameBuffer.realloc(nRead); + + OUString sLOCPath( reinterpret_cast<const char *>(aNameBuffer.getConstArray()), + aNameBuffer.getLength(), + RTL_TEXTENCODING_UTF8 ); + + if ( rEntry.nPathLen == -1 ) // the file was created + { + rEntry.nPathLen = nPathLen; + rEntry.sPath = sLOCPath; + } + + bBroken = rEntry.nPathLen != nPathLen + || rEntry.sPath != sLOCPath; + } + catch(...) + { + bBroken = true; + } + + if ( bBroken && !bRecoveryMode ) + throw ZipIOException("The stream seems to be broken!" ); +} + +sal_Int32 ZipFile::findEND() +{ + // this method is called in constructor only, no need for mutex + sal_Int32 nPos, nEnd; + Sequence < sal_Int8 > aBuffer; + try + { + sal_Int32 nLength = static_cast <sal_Int32 > (aGrabber.getLength()); + if (nLength < ENDHDR) + return -1; + nPos = nLength - ENDHDR - ZIP_MAXNAMELEN; + nEnd = nPos >= 0 ? nPos : 0 ; + + aGrabber.seek( nEnd ); + + auto nSize = nLength - nEnd; + if (nSize != aGrabber.readBytes(aBuffer, nSize)) + throw ZipException("Zip END signature not found!" ); + + const sal_Int8 *pBuffer = aBuffer.getConstArray(); + + nPos = nSize - ENDHDR; + while ( nPos >= 0 ) + { + if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 ) + return nPos + nEnd; + nPos--; + } + } + catch ( IllegalArgumentException& ) + { + throw ZipException("Zip END signature not found!" ); + } + catch ( NotConnectedException& ) + { + throw ZipException("Zip END signature not found!" ); + } + catch ( BufferSizeExceededException& ) + { + throw ZipException("Zip END signature not found!" ); + } + throw ZipException("Zip END signature not found!" ); +} + +sal_Int32 ZipFile::readCEN() +{ + // this method is called in constructor only, no need for mutex + sal_Int32 nCenPos = -1, nLocPos; + sal_uInt16 nCount; + + try + { + sal_Int32 nEndPos = findEND(); + if (nEndPos == -1) + return -1; + aGrabber.seek(nEndPos + ENDTOT); + sal_uInt16 nTotal = aGrabber.ReadUInt16(); + sal_Int32 nCenLen = aGrabber.ReadInt32(); + sal_Int32 nCenOff = aGrabber.ReadInt32(); + + if ( nTotal * CENHDR > nCenLen ) + throw ZipException("invalid END header (bad entry count)" ); + + if ( nTotal > ZIP_MAXENTRIES ) + throw ZipException("too many entries in ZIP File" ); + + if ( nCenLen < 0 || nCenLen > nEndPos ) + throw ZipException("Invalid END header (bad central directory size)" ); + + nCenPos = nEndPos - nCenLen; + + if ( nCenOff < 0 || nCenOff > nCenPos ) + throw ZipException("Invalid END header (bad central directory size)" ); + + nLocPos = nCenPos - nCenOff; + aGrabber.seek( nCenPos ); + Sequence < sal_Int8 > aCENBuffer ( nCenLen ); + sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen ); + if ( static_cast < sal_Int64 > ( nCenLen ) != nRead ) + throw ZipException ("Error reading CEN into memory buffer!" ); + + MemoryByteGrabber aMemGrabber(aCENBuffer); + + ZipEntry aEntry; + sal_Int16 nCommentLen; + + aEntries.reserve(nTotal); + for (nCount = 0 ; nCount < nTotal; nCount++) + { + sal_Int32 nTestSig = aMemGrabber.ReadInt32(); + if ( nTestSig != CENSIG ) + throw ZipException("Invalid CEN header (bad signature)" ); + + aMemGrabber.skipBytes ( 2 ); + aEntry.nVersion = aMemGrabber.ReadInt16(); + aEntry.nFlag = aMemGrabber.ReadInt16(); + + if ( ( aEntry.nFlag & 1 ) == 1 ) + throw ZipException("Invalid CEN header (encrypted entry)" ); + + aEntry.nMethod = aMemGrabber.ReadInt16(); + + if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED) + throw ZipException("Invalid CEN header (bad compression method)" ); + + aEntry.nTime = aMemGrabber.ReadInt32(); + aEntry.nCrc = aMemGrabber.ReadInt32(); + + sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32(); + sal_uInt32 nSize = aMemGrabber.ReadUInt32(); + aEntry.nPathLen = aMemGrabber.ReadInt16(); + aEntry.nExtraLen = aMemGrabber.ReadInt16(); + nCommentLen = aMemGrabber.ReadInt16(); + aMemGrabber.skipBytes ( 8 ); + sal_uInt32 nOffset = aMemGrabber.ReadUInt32(); + + // FIXME64: need to read the 64bit header instead + if ( nSize == 0xffffffff || + nOffset == 0xffffffff || + nCompressedSize == 0xffffffff ) { + throw ZipException("PK64 zip file entry" ); + } + aEntry.nCompressedSize = nCompressedSize; + aEntry.nSize = nSize; + aEntry.nOffset = nOffset; + + aEntry.nOffset += nLocPos; + aEntry.nOffset *= -1; + + if ( aEntry.nPathLen < 0 ) + throw ZipException("unexpected name length" ); + + if ( nCommentLen < 0 ) + throw ZipException("unexpected comment length" ); + + if ( aEntry.nExtraLen < 0 ) + throw ZipException("unexpected extra header info length" ); + + if (aEntry.nPathLen > aMemGrabber.remainingSize()) + throw ZipException("name too long"); + + // read always in UTF8, some tools seem not to set UTF8 bit + aEntry.sPath = OUString( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()), + aEntry.nPathLen, + RTL_TEXTENCODING_UTF8 ); + + if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) ) + throw ZipException("Zip entry has an invalid name." ); + + aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen ); + aEntries[aEntry.sPath] = aEntry; + } + + if (nCount != nTotal) + throw ZipException("Count != Total" ); + } + catch ( IllegalArgumentException & ) + { + // seek can throw this... + nCenPos = -1; // make sure we return -1 to indicate an error + } + return nCenPos; +} + +void ZipFile::recover() +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + sal_Int64 nLength; + Sequence < sal_Int8 > aBuffer; + + try + { + nLength = aGrabber.getLength(); + if (nLength < ENDHDR) + return; + + aGrabber.seek( 0 ); + + const sal_Int64 nToRead = 32000; + for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; ) + { + const sal_Int8 *pBuffer = aBuffer.getConstArray(); + sal_Int32 nBufSize = aBuffer.getLength(); + + sal_Int64 nPos = 0; + // the buffer should contain at least one header, + // or if it is end of the file, at least the postheader with sizes and hash + while( nPos < nBufSize - 30 + || ( nBufSize < nToRead && nPos < nBufSize - 16 ) ) + + { + if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 ) + { + ZipEntry aEntry; + Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 26); + MemoryByteGrabber aMemGrabber(aTmpBuffer); + + aEntry.nVersion = aMemGrabber.ReadInt16(); + aEntry.nFlag = aMemGrabber.ReadInt16(); + + if ( ( aEntry.nFlag & 1 ) != 1 ) + { + aEntry.nMethod = aMemGrabber.ReadInt16(); + + if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED ) + { + aEntry.nTime = aMemGrabber.ReadInt32(); + aEntry.nCrc = aMemGrabber.ReadInt32(); + sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32(); + sal_uInt32 nSize = aMemGrabber.ReadUInt32(); + aEntry.nPathLen = aMemGrabber.ReadInt16(); + aEntry.nExtraLen = aMemGrabber.ReadInt16(); + + // FIXME64: need to read the 64bit header instead + if ( nSize == 0xffffffff || + nCompressedSize == 0xffffffff ) { + throw ZipException("PK64 zip file entry" ); + } + aEntry.nCompressedSize = nCompressedSize; + aEntry.nSize = nSize; + + sal_Int32 nDescrLength = + ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0; + + sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize; + sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength; + if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0 + && ( nGenPos + nPos + nBlockLength ) <= nLength ) + { + // read always in UTF8, some tools seem not to set UTF8 bit + if( nPos + 30 + aEntry.nPathLen <= nBufSize ) + aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]), + aEntry.nPathLen, + RTL_TEXTENCODING_UTF8 ); + else + { + Sequence < sal_Int8 > aFileName; + aGrabber.seek( nGenPos + nPos + 30 ); + aGrabber.readBytes( aFileName, aEntry.nPathLen ); + aEntry.sPath = OUString ( reinterpret_cast<const char *>(aFileName.getConstArray()), + aFileName.getLength(), + RTL_TEXTENCODING_UTF8 ); + aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength()); + } + + aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen; + + if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) ) + { + aEntry.nCrc = 0; + aEntry.nCompressedSize = 0; + aEntry.nSize = 0; + } + + aEntries.emplace( aEntry.sPath, aEntry ); + } + } + } + + nPos += 4; + } + else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 ) + { + sal_Int64 nCompressedSize, nSize; + Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 12); + MemoryByteGrabber aMemGrabber(aTmpBuffer); + sal_Int32 nCRC32 = aMemGrabber.ReadInt32(); + sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32(); + sal_uInt32 nSize32 = aMemGrabber.ReadUInt32(); + + // FIXME64: work to be done here ... + nCompressedSize = nCompressedSize32; + nSize = nSize32; + + for( auto& rEntry : aEntries ) + { + // this is a broken package, accept this block not only for DEFLATED streams + if( rEntry.second.nFlag & 8 ) + { + sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize; + if ( nStreamOffset == rEntry.second.nOffset && nCompressedSize > rEntry.second.nCompressedSize ) + { + // only DEFLATED blocks need to be checked + bool bAcceptBlock = ( rEntry.second.nMethod == STORED && nCompressedSize == nSize ); + + if ( !bAcceptBlock ) + { + sal_Int64 nRealSize = 0; + sal_Int32 nRealCRC = 0; + getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC ); + bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 ); + } + + if ( bAcceptBlock ) + { + rEntry.second.nCrc = nCRC32; + rEntry.second.nCompressedSize = nCompressedSize; + rEntry.second.nSize = nSize; + } + } +#if 0 +// for now ignore clearly broken streams + else if( !rEntry.second.nCompressedSize ) + { + rEntry.second.nCrc = nCRC32; + sal_Int32 nRealStreamSize = nGenPos + nPos - rEntry.second.nOffset; + rEntry.second.nCompressedSize = nRealStreamSize; + rEntry.second.nSize = nSize; + } +#endif + } + } + + nPos += 4; + } + else + nPos++; + } + + nGenPos += nPos; + aGrabber.seek( nGenPos ); + } + } + catch ( IllegalArgumentException& ) + { + throw ZipException("Zip END signature not found!" ); + } + catch ( NotConnectedException& ) + { + throw ZipException("Zip END signature not found!" ); + } + catch ( BufferSizeExceededException& ) + { + throw ZipException("Zip END signature not found!" ); + } +} + +bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + sal_Int32 nCRC = 0; + sal_Int64 nSize = 0; + + if( aEntry.nMethod == STORED ) + return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc ); + + getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC ); + return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC ); +} + +sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + Sequence < sal_Int8 > aBuffer; + CRC32 aCRC; + sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000)); + + aGrabber.seek( nOffset ); + for (sal_Int64 ind = 0; + aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize; + ++ind) + { + sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize); + aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen)); + } + + return aCRC.getValue(); +} + +void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC ) +{ + ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() ); + + Sequence < sal_Int8 > aBuffer; + CRC32 aCRC; + sal_Int64 nRealSize = 0; + Inflater aInflaterLocal( true ); + sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) ); + + aGrabber.seek( nOffset ); + for ( sal_Int64 ind = 0; + !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize; + ind++ ) + { + Sequence < sal_Int8 > aData( nBlockSize ); + sal_Int32 nLastInflated = 0; + sal_Int64 nInBlock = 0; + + aInflaterLocal.setInput( aBuffer ); + do + { + nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize ); + aCRC.updateSegment( aData, nLastInflated ); + nInBlock += nLastInflated; + } while( !aInflater.finished() && nLastInflated ); + + nRealSize += nInBlock; + } + + *nSize = nRealSize; + *nCRC = aCRC.getValue(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |