diff options
Diffstat (limited to 'package/source/zipapi/ZipOutputStream.cxx')
-rw-r--r-- | package/source/zipapi/ZipOutputStream.cxx | 156 |
1 files changed, 154 insertions, 2 deletions
diff --git a/package/source/zipapi/ZipOutputStream.cxx b/package/source/zipapi/ZipOutputStream.cxx index 7cd5acdb5c7b..29c19c42667a 100644 --- a/package/source/zipapi/ZipOutputStream.cxx +++ b/package/source/zipapi/ZipOutputStream.cxx @@ -23,8 +23,11 @@ #include <com/sun/star/io/XOutputStream.hpp> #include <comphelper/storagehelper.hxx> +#include <osl/time.h> + #include <PackageConstants.hxx> #include <ZipEntry.hxx> +#include <ZipPackageStream.hxx> using namespace com::sun::star; using namespace com::sun::star::io; @@ -37,6 +40,7 @@ ZipOutputStream::ZipOutputStream( const uno::Reference < io::XOutputStream > &xO : m_xStream(xOStream) , m_aChucker(xOStream) , m_bFinished(false) +, m_pCurrentEntry(NULL) { } @@ -46,9 +50,45 @@ ZipOutputStream::~ZipOutputStream( void ) delete m_aZipList[i]; } -void ZipOutputStream::addEntry( ZipEntry *pZipEntry ) +void ZipOutputStream::putNextEntry( ZipEntry& rEntry, bool bEncrypt ) + throw(IOException, RuntimeException) +{ + assert(!m_pCurrentEntry && "Forgot to close an entry before putNextEntry()?"); + if (rEntry.nTime == -1) + rEntry.nTime = getCurrentDosTime(); + if (rEntry.nMethod == -1) + rEntry.nMethod = DEFLATED; + rEntry.nVersion = 20; + rEntry.nFlag = 1 << 11; + if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 || + rEntry.nCrc == -1) + { + rEntry.nSize = rEntry.nCompressedSize = 0; + rEntry.nFlag |= 8; + } + if (bEncrypt) + { + rEntry.nFlag |= 1 << 4; + } + + sal_Int32 nLOCLength = writeLOC(rEntry); + rEntry.nOffset = m_aChucker.GetPosition() - nLOCLength; + m_aZipList.push_back( &rEntry ); + m_pCurrentEntry = &rEntry; +} + +void ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength ) + throw(IOException, RuntimeException) +{ + m_aChucker.WriteBytes( Sequence< sal_Int8 >(rBuffer.getConstArray(), nNewLength) ); +} + +void ZipOutputStream::rawCloseEntry() + throw(IOException, RuntimeException) { - m_aZipList.push_back( pZipEntry ); + if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) ) + writeEXT(*m_pCurrentEntry); + m_pCurrentEntry = NULL; } void ZipOutputStream::finish( ) @@ -148,4 +188,116 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry ) m_aChucker.WriteBytes( aSequence ); } +void ZipOutputStream::writeEXT( const ZipEntry &rEntry ) + throw(IOException, RuntimeException) +{ + bool bWrite64Header = false; + + m_aChucker << EXTSIG; + m_aChucker << static_cast < sal_uInt32> ( rEntry.nCrc ); + m_aChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header ); + m_aChucker << getTruncated( rEntry.nSize, &bWrite64Header ); + + if( bWrite64Header ) + { + // FIXME64: need to append a ZIP64 header instead of throwing + // We're about to silently lose people's data - which they are + // unlikely to appreciate so fail instead: + throw IOException( "File contains streams that are too large." ); + } +} + +sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry ) + throw(IOException, RuntimeException) +{ + if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) ) + throw IOException("Unexpected character is used in file name." ); + + OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 ); + sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() ); + + m_aChucker << LOCSIG; + m_aChucker << rEntry.nVersion; + + if (rEntry.nFlag & (1 << 4) ) + { + // If it's an encrypted entry, we pretend its stored plain text + sal_Int16 nTmpFlag = rEntry.nFlag; + nTmpFlag &= ~(1 <<4 ); + m_aChucker << nTmpFlag; + m_aChucker << static_cast < sal_Int16 > ( STORED ); + } + else + { + m_aChucker << rEntry.nFlag; + m_aChucker << rEntry.nMethod; + } + + bool bWrite64Header = false; + + m_aChucker << static_cast < sal_uInt32 > (rEntry.nTime); + if ((rEntry.nFlag & 8) == 8 ) + { + m_aChucker << static_cast < sal_Int32 > (0); + m_aChucker << static_cast < sal_Int32 > (0); + m_aChucker << static_cast < sal_Int32 > (0); + } + else + { + m_aChucker << static_cast < sal_uInt32 > (rEntry.nCrc); + m_aChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header ); + m_aChucker << getTruncated( rEntry.nSize, &bWrite64Header ); + } + m_aChucker << nNameLength; + m_aChucker << static_cast < sal_Int16 > (0); + + if( bWrite64Header ) + { + // FIXME64: need to append a ZIP64 header instead of throwing + // We're about to silently lose people's data - which they are + // unlikely to appreciate so fail instead: + throw IOException( "File contains streams that are too large." ); + } + + Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() ); + m_aChucker.WriteBytes( aSequence ); + + return LOCHDR + nNameLength; +} + +sal_uInt32 ZipOutputStream::getCurrentDosTime() +{ + oslDateTime aDateTime; + TimeValue aTimeValue; + osl_getSystemTime ( &aTimeValue ); + osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime); + + // at year 2108, there is an overflow + // -> some decision needs to be made + // how to handle the ZIP file format (just overflow?) + + // if the current system time is before 1980, + // then the time traveller will have to make a decision + // how to handle the ZIP file format before it is invented + // (just underflow?) + + assert(aDateTime.Year > 1980 && aDateTime.Year < 2108); + + sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year); + + if (nYear>=1980) + nYear-=1980; + else if (nYear>=80) + { + nYear-=80; + } + sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) + + ( 32 * (aDateTime.Month)) + + ( 512 * nYear ) ) << 16) | + ( ( aDateTime.Seconds/2) + + ( 32 * aDateTime.Minutes) + + ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) ); + return nResult; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |