1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: ZipOutputStream.cxx,v $
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>
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>
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
)
56 , aBuffer(n_ConstBufferSize
)
57 , aDeflater(DEFAULT_COMPRESSION
, sal_True
)
61 , bFinished(sal_False
)
62 , bEncryptCurrentEntry(sal_False
)
68 ZipOutputStream::~ZipOutputStream( void )
70 for (sal_Int32 i
= 0, nEnd
= aZipList
.size(); i
< nEnd
; 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
,
88 throw(IOException
, RuntimeException
)
90 if (pCurrentEntry
!= NULL
)
92 if (rEntry
.nTime
== -1)
93 rEntry
.nTime
= getCurrentDosTime();
94 if (rEntry
.nMethod
== -1)
95 rEntry
.nMethod
= nMethod
;
97 rEntry
.nFlag
= 1 << 11;
98 if (rEntry
.nSize
== -1 || rEntry
.nCompressedSize
== -1 ||
104 bEncryptCurrentEntry
= sal_True
;
106 ZipFile::StaticGetCipher( xEncryptData
, aCipher
, sal_False
);
108 aDigest
= rtl_digest_createSHA1();
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
;
125 switch (pEntry
->nMethod
)
129 while (!aDeflater
.finished())
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");
151 pEntry
->nSize
= aDeflater
.getTotalIn();
152 pEntry
->nCompressedSize
= aDeflater
.getTotalOut();
153 pEntry
->nCrc
= aCRC
.getValue();
154 if ( bEncryptCurrentEntry
)
155 pEntry
->nSize
= pEntry
->nCompressedSize
;
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");
166 OSL_ENSURE(false,"Invalid compression method");
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
)
193 if (!aDeflater
.finished())
195 aDeflater
.setInputSegment(rBuffer
, nNewOffset
, nNewLength
);
196 while (!aDeflater
.needsInput())
198 if (!bEncryptCurrentEntry
)
199 aCRC
.updateSegment(rBuffer
, nNewOffset
, nNewLength
);
204 Sequence
< sal_Int8
> aTmpBuffer ( rBuffer
.getConstArray(), nNewLength
);
205 aChucker
.WriteBytes( aTmpBuffer
);
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
)
232 if (pCurrentEntry
!= NULL
)
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
;
246 void ZipOutputStream::doDeflate()
248 sal_Int32 nLength
= aDeflater
.doDeflateSegment(aBuffer
, 0, aBuffer
.getLength());
249 sal_Int32 nOldLength
= aBuffer
.getLength();
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
;
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
);
280 aChucker
.WriteBytes ( aTmpBuffer
);
283 void ZipOutputStream::writeEND(sal_uInt32 nOffset
, sal_uInt32 nLength
)
284 throw(IOException
, RuntimeException
)
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() );
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() );
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
);
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
)
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() );
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
);
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);
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
);
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
) ) ) );
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) );