LCOV - code coverage report
Current view: top level - package/source/zipapi - ZipOutputEntry.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 151 165 91.5 %
Date: 2014-11-03 Functions: 11 11 100.0 %
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 <ZipOutputEntry.hxx>
      21             : 
      22             : #include <com/sun/star/packages/zip/ZipConstants.hpp>
      23             : #include <comphelper/storagehelper.hxx>
      24             : 
      25             : #include <osl/time.h>
      26             : 
      27             : #include <PackageConstants.hxx>
      28             : #include <ZipEntry.hxx>
      29             : #include <ZipFile.hxx>
      30             : #include <ZipPackageStream.hxx>
      31             : 
      32             : using namespace com::sun::star;
      33             : using namespace com::sun::star::io;
      34             : using namespace com::sun::star::uno;
      35             : using namespace com::sun::star::packages::zip::ZipConstants;
      36             : 
      37             : /** This class is used to deflate Zip entries
      38             :  */
      39       19164 : ZipOutputEntry::ZipOutputEntry( const uno::Reference< uno::XComponentContext >& rxContext,
      40             :                         ByteChucker& rChucker,
      41             :                         ZipEntry& rEntry,
      42             :                         ZipPackageStream* pStream,
      43             :                         bool bEncrypt)
      44             : : m_aDeflateBuffer(n_ConstBufferSize)
      45             : , m_aDeflater(DEFAULT_COMPRESSION, true)
      46             : , m_rChucker(rChucker)
      47             : , m_pCurrentEntry(&rEntry)
      48             : , m_nDigested(0)
      49             : , m_bEncryptCurrentEntry(false)
      50       19164 : , m_pCurrentStream(NULL)
      51             : {
      52       19164 :     if (rEntry.nTime == -1)
      53       17236 :         rEntry.nTime = getCurrentDosTime();
      54       19164 :     if (rEntry.nMethod == -1)
      55           0 :         rEntry.nMethod = DEFLATED;
      56       19164 :     rEntry.nVersion = 20;
      57       19164 :     rEntry.nFlag = 1 << 11;
      58       20784 :     if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
      59        1620 :         rEntry.nCrc == -1)
      60             :     {
      61       17544 :         rEntry.nSize = rEntry.nCompressedSize = 0;
      62       17544 :         rEntry.nFlag |= 8;
      63             :     }
      64             : 
      65       19164 :     if (bEncrypt)
      66             :     {
      67          10 :         m_bEncryptCurrentEntry = true;
      68             : 
      69          10 :         m_xCipherContext = ZipFile::StaticGetCipher( rxContext, pStream->GetEncryptionData(), true );
      70          10 :         m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( rxContext, pStream->GetEncryptionData() );
      71          10 :         m_nDigested = 0;
      72          10 :         rEntry.nFlag |= 1 << 4;
      73          10 :         m_pCurrentStream = pStream;
      74             :     }
      75       19164 :     sal_Int32 nLOCLength = writeLOC(rEntry);
      76       19164 :     rEntry.nOffset = m_rChucker.GetPosition() - nLOCLength;
      77       19164 : }
      78             : 
      79       19164 : ZipOutputEntry::~ZipOutputEntry( void )
      80             : {
      81       19164 : }
      82             : 
      83       18064 : void SAL_CALL ZipOutputEntry::closeEntry(  )
      84             :     throw(IOException, RuntimeException)
      85             : {
      86       18064 :     ZipEntry *pEntry = m_pCurrentEntry;
      87       18064 :     if (pEntry)
      88             :     {
      89       18064 :         switch (pEntry->nMethod)
      90             :         {
      91             :             case DEFLATED:
      92       17544 :                 m_aDeflater.finish();
      93       52632 :                 while (!m_aDeflater.finished())
      94       17544 :                     doDeflate();
      95       17544 :                 if ((pEntry->nFlag & 8) == 0)
      96             :                 {
      97           0 :                     if (pEntry->nSize != m_aDeflater.getTotalIn())
      98             :                     {
      99             :                         OSL_FAIL("Invalid entry size");
     100             :                     }
     101           0 :                     if (pEntry->nCompressedSize != m_aDeflater.getTotalOut())
     102             :                     {
     103             :                         // Different compression strategies make the merit of this
     104             :                         // test somewhat dubious
     105           0 :                         pEntry->nCompressedSize = m_aDeflater.getTotalOut();
     106             :                     }
     107           0 :                     if (pEntry->nCrc != m_aCRC.getValue())
     108             :                     {
     109             :                         OSL_FAIL("Invalid entry CRC-32");
     110             :                     }
     111             :                 }
     112             :                 else
     113             :                 {
     114       17544 :                     if ( !m_bEncryptCurrentEntry )
     115             :                     {
     116       17534 :                         pEntry->nSize = m_aDeflater.getTotalIn();
     117       17534 :                         pEntry->nCompressedSize = m_aDeflater.getTotalOut();
     118             :                     }
     119       17544 :                     pEntry->nCrc = m_aCRC.getValue();
     120       17544 :                     writeEXT(*pEntry);
     121             :                 }
     122       17544 :                 m_aDeflater.reset();
     123       17544 :                 m_aCRC.reset();
     124       17544 :                 break;
     125             :             case STORED:
     126         520 :                 if (!((pEntry->nFlag & 8) == 0))
     127             :                     OSL_FAIL( "Serious error, one of compressed size, size or CRC was -1 in a STORED stream");
     128         520 :                 break;
     129             :             default:
     130             :                 OSL_FAIL("Invalid compression method");
     131           0 :                 break;
     132             :         }
     133             : 
     134       18064 :         if (m_bEncryptCurrentEntry)
     135             :         {
     136          10 :             m_bEncryptCurrentEntry = false;
     137             : 
     138          10 :             m_xCipherContext.clear();
     139             : 
     140          10 :             uno::Sequence< sal_Int8 > aDigestSeq;
     141          10 :             if ( m_xDigestContext.is() )
     142             :             {
     143          10 :                 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
     144          10 :                 m_xDigestContext.clear();
     145             :             }
     146             : 
     147          10 :             if ( m_pCurrentStream )
     148          10 :                 m_pCurrentStream->setDigest( aDigestSeq );
     149             :         }
     150       18064 :         m_pCurrentEntry = NULL;
     151       18064 :         m_pCurrentStream = NULL;
     152             :     }
     153       18064 : }
     154             : 
     155       18868 : void SAL_CALL ZipOutputEntry::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
     156             :     throw(IOException, RuntimeException)
     157             : {
     158       18868 :     switch (m_pCurrentEntry->nMethod)
     159             :     {
     160             :         case DEFLATED:
     161       18334 :             if (!m_aDeflater.finished())
     162             :             {
     163       18334 :                 m_aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
     164       54902 :                  while (!m_aDeflater.needsInput())
     165       18234 :                     doDeflate();
     166       18334 :                 if (!m_bEncryptCurrentEntry)
     167       18324 :                     m_aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
     168             :             }
     169       18334 :             break;
     170             :         case STORED:
     171             :             {
     172         534 :                 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
     173         534 :                 m_rChucker.WriteBytes( aTmpBuffer );
     174             :             }
     175         534 :             break;
     176             :     }
     177       18868 : }
     178             : 
     179         188 : void SAL_CALL ZipOutputEntry::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
     180             :     throw(IOException, RuntimeException)
     181             : {
     182         188 :     Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
     183         188 :     m_rChucker.WriteBytes( aTmpBuffer );
     184         188 : }
     185             : 
     186        1100 : void SAL_CALL ZipOutputEntry::rawCloseEntry(  )
     187             :     throw(IOException, RuntimeException)
     188             : {
     189        1100 :     if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
     190           0 :         writeEXT(*m_pCurrentEntry);
     191        1100 :     m_pCurrentEntry = NULL;
     192        1100 : }
     193             : 
     194       35778 : void ZipOutputEntry::doDeflate()
     195             : {
     196       35778 :     sal_Int32 nLength = m_aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength());
     197             : 
     198       35778 :     if ( nLength > 0 )
     199             :     {
     200       18010 :         uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength );
     201       18010 :         if ( m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
     202             :         {
     203             :             // Need to update our digest before encryption...
     204          10 :             sal_Int32 nDiff = n_ConstDigestLength - m_nDigested;
     205          10 :             if ( nDiff )
     206             :             {
     207          10 :                 sal_Int32 nEat = ::std::min( nLength, nDiff );
     208          10 :                 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
     209          10 :                 m_xDigestContext->updateDigest( aTmpSeq );
     210          10 :                 m_nDigested = m_nDigested + static_cast< sal_Int16 >( nEat );
     211             :             }
     212             : 
     213             :             // FIXME64: uno::Sequence not 64bit safe.
     214          10 :             uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
     215             : 
     216          10 :             m_rChucker.WriteBytes( aEncryptionBuffer );
     217             : 
     218             :             // the sizes as well as checksum for encrypted streams is calculated here
     219          10 :             m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
     220          10 :             m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
     221          10 :             m_aCRC.update( aEncryptionBuffer );
     222             :         }
     223             :         else
     224             :         {
     225       18000 :             m_rChucker.WriteBytes ( aTmpBuffer );
     226       18010 :         }
     227             :     }
     228             : 
     229       35778 :     if ( m_aDeflater.finished() && m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
     230             :     {
     231             :         // FIXME64: sequence not 64bit safe.
     232          10 :         uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
     233          10 :         if ( aEncryptionBuffer.getLength() )
     234             :         {
     235          10 :             m_rChucker.WriteBytes( aEncryptionBuffer );
     236             : 
     237             :             // the sizes as well as checksum for encrypted streams is calculated hier
     238          10 :             m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
     239          10 :             m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
     240          10 :             m_aCRC.update( aEncryptionBuffer );
     241          10 :         }
     242             :     }
     243       35778 : }
     244             : 
     245       38328 : static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
     246             : {
     247       38328 :     if( nNum >= 0xffffffff )
     248             :     {
     249           0 :         *pIsTruncated = true;
     250           0 :         return 0xffffffff;
     251             :     }
     252             :     else
     253       38328 :         return static_cast< sal_uInt32 >( nNum );
     254             : }
     255             : 
     256       17544 : void ZipOutputEntry::writeEXT( const ZipEntry &rEntry )
     257             :     throw(IOException, RuntimeException)
     258             : {
     259       17544 :     bool bWrite64Header = false;
     260             : 
     261       17544 :     m_rChucker << EXTSIG;
     262       17544 :     m_rChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
     263       17544 :     m_rChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
     264       17544 :     m_rChucker << getTruncated( rEntry.nSize, &bWrite64Header );
     265             : 
     266       17544 :     if( bWrite64Header )
     267             :     {
     268             :         // FIXME64: need to append a ZIP64 header instead of throwing
     269             :         // We're about to silently lose people's data - which they are
     270             :         // unlikely to appreciate so fail instead:
     271           0 :         throw IOException( "File contains streams that are too large." );
     272             :     }
     273       17544 : }
     274             : 
     275       19164 : sal_Int32 ZipOutputEntry::writeLOC( const ZipEntry &rEntry )
     276             :     throw(IOException, RuntimeException)
     277             : {
     278       19164 :     if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
     279           0 :         throw IOException("Unexpected character is used in file name." );
     280             : 
     281       19164 :     OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
     282       19164 :     sal_Int16 nNameLength       = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
     283             : 
     284       19164 :     m_rChucker << LOCSIG;
     285       19164 :     m_rChucker << rEntry.nVersion;
     286             : 
     287       19164 :     if (rEntry.nFlag & (1 << 4) )
     288             :     {
     289             :         // If it's an encrypted entry, we pretend its stored plain text
     290          10 :         sal_Int16 nTmpFlag = rEntry.nFlag;
     291          10 :         nTmpFlag &= ~(1 <<4 );
     292          10 :         m_rChucker << nTmpFlag;
     293          10 :         m_rChucker << static_cast < sal_Int16 > ( STORED );
     294             :     }
     295             :     else
     296             :     {
     297       19154 :         m_rChucker << rEntry.nFlag;
     298       19154 :         m_rChucker << rEntry.nMethod;
     299             :     }
     300             : 
     301       19164 :     bool bWrite64Header = false;
     302             : 
     303       19164 :     m_rChucker << static_cast < sal_uInt32 > (rEntry.nTime);
     304       19164 :     if ((rEntry.nFlag & 8) == 8 )
     305             :     {
     306       17544 :         m_rChucker << static_cast < sal_Int32 > (0);
     307       17544 :         m_rChucker << static_cast < sal_Int32 > (0);
     308       17544 :         m_rChucker << static_cast < sal_Int32 > (0);
     309             :     }
     310             :     else
     311             :     {
     312        1620 :         m_rChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
     313        1620 :         m_rChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
     314        1620 :         m_rChucker << getTruncated( rEntry.nSize, &bWrite64Header );
     315             :     }
     316       19164 :     m_rChucker << nNameLength;
     317       19164 :     m_rChucker << static_cast < sal_Int16 > (0);
     318             : 
     319       19164 :     if( bWrite64Header )
     320             :     {
     321             :         // FIXME64: need to append a ZIP64 header instead of throwing
     322             :         // We're about to silently lose people's data - which they are
     323             :         // unlikely to appreciate so fail instead:
     324           0 :         throw IOException( "File contains streams that are too large." );
     325             :     }
     326             : 
     327       38328 :     Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
     328       19164 :     m_rChucker.WriteBytes( aSequence );
     329             : 
     330       38328 :     return LOCHDR + nNameLength;
     331             : }
     332       18986 : sal_uInt32 ZipOutputEntry::getCurrentDosTime( )
     333             : {
     334             :     oslDateTime aDateTime;
     335             :     TimeValue aTimeValue;
     336       18986 :     osl_getSystemTime ( &aTimeValue );
     337       18986 :     osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
     338             : 
     339             :     // at year 2108, there is an overflow
     340             :     // -> some decision needs to be made
     341             :     // how to handle the ZIP file format (just overflow?)
     342             : 
     343             :     // if the current system time is before 1980,
     344             :     // then the time traveller will have to make a decision
     345             :     // how to handle the ZIP file format before it is invented
     346             :     // (just underflow?)
     347             : 
     348             :     assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
     349             : 
     350       18986 :     sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
     351             : 
     352       18986 :     if (nYear>=1980)
     353       18986 :         nYear-=1980;
     354           0 :     else if (nYear>=80)
     355             :     {
     356           0 :         nYear-=80;
     357             :     }
     358             :     sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
     359       37972 :                                           ( 32 * (aDateTime.Month)) +
     360       37972 :                                           ( 512 * nYear ) ) << 16) |
     361             :                                         ( ( aDateTime.Seconds/2) +
     362       37972 :                                             ( 32 * aDateTime.Minutes) +
     363       37972 :                                           ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
     364       18986 :     return nResult;
     365             : }
     366             : 
     367             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10