Update ooo320-m1
[ooovba.git] / package / source / zipapi / ZipOutputStream.cxx
blobe5de16f552a48c6151269ad061cea17009379470
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: ZipOutputStream.cxx,v $
10 * $Revision: 1.41 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_package.hxx"
33 #include <ZipOutputStream.hxx>
34 #include <com/sun/star/packages/zip/ZipConstants.hpp>
35 #include <osl/time.h>
36 #include <EncryptionData.hxx>
37 #include <PackageConstants.hxx>
38 #include <ZipEntry.hxx>
39 #include <ZipFile.hxx>
40 #include <vos/ref.hxx>
41 #include <com/sun/star/io/XOutputStream.hpp>
43 #include <comphelper/storagehelper.hxx>
45 using namespace rtl;
46 using namespace com::sun::star::io;
47 using namespace com::sun::star::uno;
48 using namespace com::sun::star::packages;
49 using namespace com::sun::star::packages::zip;
50 using namespace com::sun::star::packages::zip::ZipConstants;
52 /** This class is used to write Zip files
54 ZipOutputStream::ZipOutputStream( Reference < XOutputStream > &xOStream )
55 : xStream(xOStream)
56 , aBuffer(n_ConstBufferSize)
57 , aDeflater(DEFAULT_COMPRESSION, sal_True)
58 , aChucker(xOStream)
59 , pCurrentEntry(NULL)
60 , nMethod(DEFLATED)
61 , bFinished(sal_False)
62 , bEncryptCurrentEntry(sal_False)
68 ZipOutputStream::~ZipOutputStream( void )
70 for (sal_Int32 i = 0, nEnd = aZipList.size(); i < nEnd; i++)
71 delete aZipList[i];
74 void SAL_CALL ZipOutputStream::setMethod( sal_Int32 nNewMethod )
75 throw(RuntimeException)
77 nMethod = static_cast < sal_Int16 > (nNewMethod);
79 void SAL_CALL ZipOutputStream::setLevel( sal_Int32 nNewLevel )
80 throw(RuntimeException)
82 aDeflater.setLevel( nNewLevel);
85 void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry& rEntry,
86 vos::ORef < EncryptionData > &xEncryptData,
87 sal_Bool bEncrypt)
88 throw(IOException, RuntimeException)
90 if (pCurrentEntry != NULL)
91 closeEntry();
92 if (rEntry.nTime == -1)
93 rEntry.nTime = getCurrentDosTime();
94 if (rEntry.nMethod == -1)
95 rEntry.nMethod = nMethod;
96 rEntry.nVersion = 20;
97 rEntry.nFlag = 1 << 11;
98 if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
99 rEntry.nCrc == -1)
100 rEntry.nFlag |= 8;
102 if (bEncrypt)
104 bEncryptCurrentEntry = sal_True;
106 ZipFile::StaticGetCipher( xEncryptData, aCipher, sal_False );
108 aDigest = rtl_digest_createSHA1();
109 mnDigested = 0;
110 rEntry.nFlag |= 1 << 4;
111 pCurrentEncryptData = xEncryptData.getBodyPtr();
113 sal_Int32 nLOCLength = writeLOC(rEntry);
114 rEntry.nOffset = static_cast < sal_Int32 > (aChucker.GetPosition()) - nLOCLength;
115 aZipList.push_back( &rEntry );
116 pCurrentEntry = &rEntry;
119 void SAL_CALL ZipOutputStream::closeEntry( )
120 throw(IOException, RuntimeException)
122 ZipEntry *pEntry = pCurrentEntry;
123 if (pEntry)
125 switch (pEntry->nMethod)
127 case DEFLATED:
128 aDeflater.finish();
129 while (!aDeflater.finished())
130 doDeflate();
131 if ((pEntry->nFlag & 8) == 0)
133 if (pEntry->nSize != aDeflater.getTotalIn())
135 OSL_ENSURE(false,"Invalid entry size");
137 if (pEntry->nCompressedSize != aDeflater.getTotalOut())
139 //VOS_DEBUG_ONLY("Invalid entry compressed size");
140 // Different compression strategies make the merit of this
141 // test somewhat dubious
142 pEntry->nCompressedSize = aDeflater.getTotalOut();
144 if (pEntry->nCrc != aCRC.getValue())
146 OSL_ENSURE(false,"Invalid entry CRC-32");
149 else
151 pEntry->nSize = aDeflater.getTotalIn();
152 pEntry->nCompressedSize = aDeflater.getTotalOut();
153 pEntry->nCrc = aCRC.getValue();
154 if ( bEncryptCurrentEntry )
155 pEntry->nSize = pEntry->nCompressedSize;
156 writeEXT(*pEntry);
158 aDeflater.reset();
159 aCRC.reset();
160 break;
161 case STORED:
162 if (!((pEntry->nFlag & 8) == 0))
163 OSL_ENSURE ( false, "Serious error, one of compressed size, size or CRC was -1 in a STORED stream");
164 break;
165 default:
166 OSL_ENSURE(false,"Invalid compression method");
167 break;
170 if (bEncryptCurrentEntry)
172 rtlDigestError aDigestResult;
173 aEncryptionBuffer.realloc ( 0 );
174 bEncryptCurrentEntry = sal_False;
175 rtl_cipher_destroy ( aCipher );
176 pCurrentEncryptData->aDigest.realloc ( RTL_DIGEST_LENGTH_SHA1 );
177 aDigestResult = rtl_digest_getSHA1 ( aDigest,
178 reinterpret_cast < sal_uInt8 * > ( pCurrentEncryptData->aDigest.getArray() ),
179 RTL_DIGEST_LENGTH_SHA1 );
180 OSL_ASSERT( aDigestResult == rtl_Digest_E_None );
181 rtl_digest_destroySHA1 ( aDigest );
183 pCurrentEntry = NULL;
187 void SAL_CALL ZipOutputStream::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
188 throw(IOException, RuntimeException)
190 switch (pCurrentEntry->nMethod)
192 case DEFLATED:
193 if (!aDeflater.finished())
195 aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
196 while (!aDeflater.needsInput())
197 doDeflate();
198 if (!bEncryptCurrentEntry)
199 aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
201 break;
202 case STORED:
204 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
205 aChucker.WriteBytes( aTmpBuffer );
207 break;
211 void SAL_CALL ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
212 throw(IOException, RuntimeException)
214 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
215 aChucker.WriteBytes( aTmpBuffer );
218 void SAL_CALL ZipOutputStream::rawCloseEntry( )
219 throw(IOException, RuntimeException)
221 if ( pCurrentEntry->nMethod == DEFLATED && ( pCurrentEntry->nFlag & 8 ) )
222 writeEXT(*pCurrentEntry);
223 pCurrentEntry = NULL;
226 void SAL_CALL ZipOutputStream::finish( )
227 throw(IOException, RuntimeException)
229 if (bFinished)
230 return;
232 if (pCurrentEntry != NULL)
233 closeEntry();
235 if (aZipList.size() < 1)
236 OSL_ENSURE(false,"Zip file must have at least one entry!\n");
238 sal_Int32 nOffset= static_cast < sal_Int32 > (aChucker.GetPosition());
239 for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++)
240 writeCEN( *aZipList[i] );
241 writeEND( nOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) - nOffset);
242 bFinished = sal_True;
243 xStream->flush();
246 void ZipOutputStream::doDeflate()
248 sal_Int32 nLength = aDeflater.doDeflateSegment(aBuffer, 0, aBuffer.getLength());
249 sal_Int32 nOldLength = aBuffer.getLength();
251 if ( nLength > 0 )
253 Sequence < sal_Int8 > aTmpBuffer ( aBuffer.getConstArray(), nLength );
254 const void *pTmpBuffer = static_cast < const void * > ( aTmpBuffer.getConstArray() );
255 if (bEncryptCurrentEntry)
257 // Need to update our digest before encryption...
258 rtlDigestError aDigestResult = rtl_Digest_E_None;
259 sal_Int16 nDiff = n_ConstDigestLength - mnDigested;
260 if ( nDiff )
262 sal_Int16 nEat = static_cast < sal_Int16 > ( nDiff > nLength ? nLength : nDiff );
263 aDigestResult = rtl_digest_updateSHA1 ( aDigest, pTmpBuffer, nEat );
264 mnDigested = mnDigested + nEat;
266 OSL_ASSERT( aDigestResult == rtl_Digest_E_None );
268 aEncryptionBuffer.realloc ( nLength );
270 rtlCipherError aCipherResult;
271 aCipherResult = rtl_cipher_encode ( aCipher, pTmpBuffer,
272 nLength, reinterpret_cast < sal_uInt8 * > (aEncryptionBuffer.getArray()), nLength );
273 OSL_ASSERT( aCipherResult == rtl_Cipher_E_None );
275 aChucker.WriteBytes( aEncryptionBuffer );
276 aCRC.update ( aEncryptionBuffer );
277 aEncryptionBuffer.realloc ( nOldLength );
279 else
280 aChucker.WriteBytes ( aTmpBuffer );
283 void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
284 throw(IOException, RuntimeException)
286 aChucker << ENDSIG;
287 aChucker << static_cast < sal_Int16 > ( 0 );
288 aChucker << static_cast < sal_Int16 > ( 0 );
289 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
290 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
291 aChucker << nLength;
292 aChucker << nOffset;
293 aChucker << static_cast < sal_Int16 > ( 0 );
295 void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
296 throw(IOException, RuntimeException)
298 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
299 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), Reference< XInterface >() );
301 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
302 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
304 aChucker << CENSIG;
305 aChucker << rEntry.nVersion;
306 aChucker << rEntry.nVersion;
307 if (rEntry.nFlag & (1 << 4) )
309 // If it's an encrypted entry, we pretend its stored plain text
310 ZipEntry *pEntry = const_cast < ZipEntry * > ( &rEntry );
311 pEntry->nFlag &= ~(1 <<4 );
312 aChucker << rEntry.nFlag;
313 aChucker << static_cast < sal_Int16 > ( STORED );
315 else
317 aChucker << rEntry.nFlag;
318 aChucker << rEntry.nMethod;
320 aChucker << static_cast < sal_uInt32> ( rEntry.nTime );
321 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
322 aChucker << rEntry.nCompressedSize;
323 aChucker << rEntry.nSize;
324 aChucker << nNameLength;
325 aChucker << static_cast < sal_Int16> (0);
326 aChucker << static_cast < sal_Int16> (0);
327 aChucker << static_cast < sal_Int16> (0);
328 aChucker << static_cast < sal_Int16> (0);
329 aChucker << static_cast < sal_Int32> (0);
330 aChucker << rEntry.nOffset;
332 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
333 aChucker.WriteBytes( aSequence );
335 void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
336 throw(IOException, RuntimeException)
338 aChucker << EXTSIG;
339 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
340 aChucker << rEntry.nCompressedSize;
341 aChucker << rEntry.nSize;
344 sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry )
345 throw(IOException, RuntimeException)
347 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
348 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), Reference< XInterface >() );
350 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
351 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
353 aChucker << LOCSIG;
354 aChucker << rEntry.nVersion;
356 if (rEntry.nFlag & (1 << 4) )
358 // If it's an encrypted entry, we pretend its stored plain text
359 sal_Int16 nTmpFlag = rEntry.nFlag;
360 nTmpFlag &= ~(1 <<4 );
361 aChucker << nTmpFlag;
362 aChucker << static_cast < sal_Int16 > ( STORED );
364 else
366 aChucker << rEntry.nFlag;
367 aChucker << rEntry.nMethod;
370 aChucker << static_cast < sal_uInt32 > (rEntry.nTime);
371 if ((rEntry.nFlag & 8) == 8 )
373 aChucker << static_cast < sal_Int32 > (0);
374 aChucker << static_cast < sal_Int32 > (0);
375 aChucker << static_cast < sal_Int32 > (0);
377 else
379 aChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
380 aChucker << rEntry.nCompressedSize;
381 aChucker << rEntry.nSize;
383 aChucker << nNameLength;
384 aChucker << static_cast < sal_Int16 > (0);
386 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
387 aChucker.WriteBytes( aSequence );
389 return LOCHDR + nNameLength;
391 sal_uInt32 ZipOutputStream::getCurrentDosTime( )
393 oslDateTime aDateTime;
394 TimeValue aTimeValue;
395 osl_getSystemTime ( &aTimeValue );
396 osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
398 sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
400 if (nYear>1980)
401 nYear-=1980;
402 else if (nYear>80)
403 nYear-=80;
404 sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
405 ( 32 * (aDateTime.Month)) +
406 ( 512 * nYear ) ) << 16) |
407 ( ( aDateTime.Seconds/2) +
408 ( 32 * aDateTime.Minutes) +
409 ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
410 return nResult;
414 This is actually never used, so I removed it, but thought that the
415 implementation details may be useful in the future...mtg 20010307
417 I stopped using the time library and used the OSL version instead, but
418 it might still be useful to have this code here..
420 void ZipOutputStream::dosDateToTMDate ( tm &rTime, sal_uInt32 nDosDate)
422 sal_uInt32 nDate = static_cast < sal_uInt32 > (nDosDate >> 16);
423 rTime.tm_mday = static_cast < sal_uInt32 > ( nDate & 0x1F);
424 rTime.tm_mon = static_cast < sal_uInt32 > ( ( ( (nDate) & 0x1E0)/0x20)-1);
425 rTime.tm_year = static_cast < sal_uInt32 > ( ( (nDate & 0x0FE00)/0x0200)+1980);
427 rTime.tm_hour = static_cast < sal_uInt32 > ( (nDosDate & 0xF800)/0x800);
428 rTime.tm_min = static_cast < sal_uInt32 > ( (nDosDate & 0x7E0)/0x20);
429 rTime.tm_sec = static_cast < sal_uInt32 > ( 2 * (nDosDate & 0x1F) );