LCOV - code coverage report
Current view: top level - package/source/zipapi - ZipOutputStream.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 143 154 92.9 %
Date: 2015-06-13 12:38:46 Functions: 14 14 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 <ZipOutputStream.hxx>
      21             : 
      22             : #include <com/sun/star/packages/zip/ZipConstants.hpp>
      23             : #include <com/sun/star/io/XInputStream.hpp>
      24             : #include <com/sun/star/io/XOutputStream.hpp>
      25             : #include <comphelper/storagehelper.hxx>
      26             : #include <cppuhelper/exc_hlp.hxx>
      27             : #include <osl/diagnose.h>
      28             : 
      29             : #include <osl/time.h>
      30             : 
      31             : #include <PackageConstants.hxx>
      32             : #include <ZipEntry.hxx>
      33             : #include <ZipOutputEntry.hxx>
      34             : #include <ZipPackageStream.hxx>
      35             : 
      36             : using namespace com::sun::star;
      37             : using namespace com::sun::star::io;
      38             : using namespace com::sun::star::uno;
      39             : using namespace com::sun::star::packages::zip::ZipConstants;
      40             : 
      41             : /** This class is used to write Zip files
      42             :  */
      43         832 : ZipOutputStream::ZipOutputStream( const uno::Reference < io::XOutputStream > &xOStream )
      44             : : m_xStream(xOStream)
      45             : , m_aChucker(xOStream)
      46             : , m_pCurrentEntry(NULL)
      47         832 : , m_rSharedThreadPool(comphelper::ThreadPool::getSharedOptimalPool())
      48             : {
      49         832 : }
      50             : 
      51         832 : ZipOutputStream::~ZipOutputStream()
      52             : {
      53         832 : }
      54             : 
      55       12119 : void ZipOutputStream::setEntry( ZipEntry *pEntry )
      56             : {
      57       12119 :     if (pEntry->nTime == -1)
      58       10916 :         pEntry->nTime = getCurrentDosTime();
      59       12119 :     if (pEntry->nMethod == -1)
      60           0 :         pEntry->nMethod = DEFLATED;
      61       12119 :     pEntry->nVersion = 20;
      62       12119 :     pEntry->nFlag = 1 << 11;
      63       13521 :     if (pEntry->nSize == -1 || pEntry->nCompressedSize == -1 ||
      64        1402 :         pEntry->nCrc == -1)
      65             :     {
      66       10717 :         pEntry->nSize = pEntry->nCompressedSize = 0;
      67       10717 :         pEntry->nFlag |= 8;
      68             :     }
      69       12119 : }
      70             : 
      71          44 : void ZipOutputStream::addDeflatingThread( ZipOutputEntry *pEntry, comphelper::ThreadTask *pThread )
      72             : {
      73          44 :     m_rSharedThreadPool.pushTask(pThread);
      74          44 :     m_aEntries.push_back(pEntry);
      75          44 : }
      76             : 
      77         831 : void ZipOutputStream::rawWrite( const Sequence< sal_Int8 >& rBuffer )
      78             :     throw(IOException, RuntimeException)
      79             : {
      80         831 :     m_aChucker.WriteBytes( rBuffer );
      81         831 : }
      82             : 
      83       12119 : void ZipOutputStream::rawCloseEntry( bool bEncrypt )
      84             :     throw(IOException, RuntimeException)
      85             : {
      86             :     assert(m_pCurrentEntry && "Forgot to call writeLOC()?");
      87       12119 :     if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
      88       10717 :         writeEXT(*m_pCurrentEntry);
      89             : 
      90       12119 :     if (bEncrypt)
      91           4 :         m_pCurrentEntry->nMethod = STORED;
      92             : 
      93       12119 :     m_pCurrentEntry = NULL;
      94       12119 : }
      95             : 
      96         832 : void ZipOutputStream::finish()
      97             :     throw(IOException, RuntimeException)
      98             : {
      99             :     assert(!m_aZipList.empty() && "Zip file must have at least one entry!");
     100             : 
     101             :     // Wait for all threads to finish & write
     102         832 :     m_rSharedThreadPool.waitUntilEmpty();
     103         876 :     for (size_t i = 0; i < m_aEntries.size(); i++)
     104             :     {
     105             :         //Any exceptions thrown in the threads were caught and stored for now
     106          44 :         ::css::uno::Any aCaughtException(m_aEntries[i]->getParallelDeflateException());
     107          44 :         if (aCaughtException.hasValue())
     108           0 :             ::cppu::throwException(aCaughtException);
     109             : 
     110          44 :         writeLOC(m_aEntries[i]->getZipEntry(), m_aEntries[i]->isEncrypt());
     111             : 
     112             :         sal_Int32 nRead;
     113          88 :         uno::Sequence< sal_Int8 > aSequence(n_ConstBufferSize);
     114          88 :         uno::Reference< io::XInputStream > xInput = m_aEntries[i]->getData();
     115          97 :         do
     116             :         {
     117          97 :             nRead = xInput->readBytes(aSequence, n_ConstBufferSize);
     118          97 :             if (nRead < n_ConstBufferSize)
     119          44 :                 aSequence.realloc(nRead);
     120             : 
     121          97 :             rawWrite(aSequence);
     122             :         }
     123             :         while (nRead == n_ConstBufferSize);
     124             : 
     125          44 :         rawCloseEntry(m_aEntries[i]->isEncrypt());
     126             : 
     127          44 :         m_aEntries[i]->getZipPackageStream()->successfullyWritten(m_aEntries[i]->getZipEntry());
     128          44 :         delete m_aEntries[i];
     129          44 :     }
     130             : 
     131         832 :     sal_Int32 nOffset= static_cast < sal_Int32 > (m_aChucker.GetPosition());
     132       12951 :     for (size_t i = 0; i < m_aZipList.size(); i++)
     133             :     {
     134       12119 :         writeCEN( *m_aZipList[i] );
     135       12119 :         delete m_aZipList[i];
     136             :     }
     137         832 :     writeEND( nOffset, static_cast < sal_Int32 > (m_aChucker.GetPosition()) - nOffset);
     138         832 :     m_xStream->flush();
     139         832 :     m_aZipList.clear();
     140         832 : }
     141             : 
     142       10673 : css::uno::Reference< css::io::XOutputStream > ZipOutputStream::getStream()
     143             : {
     144       10673 :     return m_xStream;
     145             : }
     146             : 
     147         832 : void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
     148             :     throw(IOException, RuntimeException)
     149             : {
     150         832 :     m_aChucker.WriteInt32( ENDSIG );
     151         832 :     m_aChucker.WriteInt16( 0 );
     152         832 :     m_aChucker.WriteInt16( 0 );
     153         832 :     m_aChucker.WriteInt16( m_aZipList.size() );
     154         832 :     m_aChucker.WriteInt16( m_aZipList.size() );
     155         832 :     m_aChucker.WriteUInt32( nLength );
     156         832 :     m_aChucker.WriteUInt32( nOffset );
     157         832 :     m_aChucker.WriteInt16( 0 );
     158         832 : }
     159             : 
     160       60595 : static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
     161             : {
     162       60595 :     if( nNum >= 0xffffffff )
     163             :     {
     164           0 :         *pIsTruncated = true;
     165           0 :         return 0xffffffff;
     166             :     }
     167             :     else
     168       60595 :         return static_cast< sal_uInt32 >( nNum );
     169             : }
     170             : 
     171       12119 : void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
     172             :     throw(IOException, RuntimeException)
     173             : {
     174       12119 :     if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
     175           0 :         throw IOException("Unexpected character is used in file name." );
     176             : 
     177       12119 :     OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
     178       12119 :     sal_Int16 nNameLength       = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
     179             : 
     180       12119 :     m_aChucker.WriteInt32( CENSIG );
     181       12119 :     m_aChucker.WriteInt16( rEntry.nVersion );
     182       12119 :     m_aChucker.WriteInt16( rEntry.nVersion );
     183       12119 :     m_aChucker.WriteInt16( rEntry.nFlag );
     184       12119 :     m_aChucker.WriteInt16( rEntry.nMethod );
     185       12119 :     bool bWrite64Header = false;
     186             : 
     187       12119 :     m_aChucker.WriteUInt32( rEntry.nTime );
     188       12119 :     m_aChucker.WriteUInt32( rEntry.nCrc );
     189       12119 :     m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
     190       12119 :     m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
     191       12119 :     m_aChucker.WriteInt16( nNameLength );
     192       12119 :     m_aChucker.WriteInt16( 0 );
     193       12119 :     m_aChucker.WriteInt16( 0 );
     194       12119 :     m_aChucker.WriteInt16( 0 );
     195       12119 :     m_aChucker.WriteInt16( 0 );
     196       12119 :     m_aChucker.WriteInt32( 0 );
     197       12119 :     m_aChucker.WriteUInt32( getTruncated( rEntry.nOffset, &bWrite64Header ) );
     198             : 
     199       12119 :     if( bWrite64Header )
     200             :     {
     201             :         // FIXME64: need to append a ZIP64 header instead of throwing
     202             :         // We're about to silently lose people's data - which they are
     203             :         // unlikely to appreciate so fail instead:
     204           0 :         throw IOException( "File contains streams that are too large." );
     205             :     }
     206             : 
     207       24238 :     Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
     208       24238 :     m_aChucker.WriteBytes( aSequence );
     209       12119 : }
     210             : 
     211       10717 : void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
     212             :     throw(IOException, RuntimeException)
     213             : {
     214       10717 :     bool bWrite64Header = false;
     215             : 
     216       10717 :     m_aChucker.WriteInt32( EXTSIG );
     217       10717 :     m_aChucker.WriteUInt32( rEntry.nCrc );
     218       10717 :     m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
     219       10717 :     m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
     220             : 
     221       10717 :     if( bWrite64Header )
     222             :     {
     223             :         // FIXME64: need to append a ZIP64 header instead of throwing
     224             :         // We're about to silently lose people's data - which they are
     225             :         // unlikely to appreciate so fail instead:
     226           0 :         throw IOException( "File contains streams that are too large." );
     227             :     }
     228       10717 : }
     229             : 
     230       12119 : void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
     231             :     throw(IOException, RuntimeException)
     232             : {
     233             :     assert(!m_pCurrentEntry && "Forgot to close an entry with rawCloseEntry()?");
     234       12119 :     m_pCurrentEntry = pEntry;
     235       12119 :     m_aZipList.push_back( m_pCurrentEntry );
     236       12119 :     const ZipEntry &rEntry = *m_pCurrentEntry;
     237             : 
     238       12119 :     if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
     239           0 :         throw IOException("Unexpected character is used in file name." );
     240             : 
     241       12119 :     OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
     242       12119 :     sal_Int16 nNameLength       = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
     243             : 
     244       12119 :     m_aChucker.WriteInt32( LOCSIG );
     245       12119 :     m_aChucker.WriteInt16( rEntry.nVersion );
     246             : 
     247       12119 :     m_aChucker.WriteInt16( rEntry.nFlag );
     248             :     // If it's an encrypted entry, we pretend its stored plain text
     249       12119 :     if (bEncrypt)
     250           4 :         m_aChucker.WriteInt16( STORED );
     251             :     else
     252       12115 :         m_aChucker.WriteInt16( rEntry.nMethod );
     253             : 
     254       12119 :     bool bWrite64Header = false;
     255             : 
     256       12119 :     m_aChucker.WriteUInt32( rEntry.nTime );
     257       12119 :     if ((rEntry.nFlag & 8) == 8 )
     258             :     {
     259       10717 :         m_aChucker.WriteInt32( 0 );
     260       10717 :         m_aChucker.WriteInt32( 0 );
     261       10717 :         m_aChucker.WriteInt32( 0 );
     262             :     }
     263             :     else
     264             :     {
     265        1402 :         m_aChucker.WriteUInt32( rEntry.nCrc );
     266        1402 :         m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
     267        1402 :         m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
     268             :     }
     269       12119 :     m_aChucker.WriteInt16( nNameLength );
     270       12119 :     m_aChucker.WriteInt16( 0 );
     271             : 
     272       12119 :     if( bWrite64Header )
     273             :     {
     274             :         // FIXME64: need to append a ZIP64 header instead of throwing
     275             :         // We're about to silently lose people's data - which they are
     276             :         // unlikely to appreciate so fail instead:
     277           0 :         throw IOException( "File contains streams that are too large." );
     278             :     }
     279             : 
     280       24238 :     Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
     281       12119 :     m_aChucker.WriteBytes( aSequence );
     282             : 
     283       24238 :     m_pCurrentEntry->nOffset = m_aChucker.GetPosition() - (LOCHDR + nNameLength);
     284       12119 : }
     285             : 
     286       12017 : sal_uInt32 ZipOutputStream::getCurrentDosTime()
     287             : {
     288             :     oslDateTime aDateTime;
     289             :     TimeValue aTimeValue;
     290       12017 :     osl_getSystemTime ( &aTimeValue );
     291       12017 :     osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
     292             : 
     293             :     // at year 2108, there is an overflow
     294             :     // -> some decision needs to be made
     295             :     // how to handle the ZIP file format (just overflow?)
     296             : 
     297             :     // if the current system time is before 1980,
     298             :     // then the time traveller will have to make a decision
     299             :     // how to handle the ZIP file format before it is invented
     300             :     // (just underflow?)
     301             : 
     302             :     assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
     303             : 
     304       12017 :     sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
     305             : 
     306       12017 :     if (nYear>=1980)
     307       12017 :         nYear-=1980;
     308           0 :     else if (nYear>=80)
     309             :     {
     310           0 :         nYear-=80;
     311             :     }
     312             :     sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
     313       24034 :                                           ( 32 * (aDateTime.Month)) +
     314       24034 :                                           ( 512 * nYear ) ) << 16) |
     315             :                                         ( ( aDateTime.Seconds/2) +
     316       24034 :                                             ( 32 * aDateTime.Minutes) +
     317       24034 :                                           ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
     318       12017 :     return nResult;
     319             : }
     320             : 
     321             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11