1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_package.hxx"
26
27 #include <com/sun/star/packages/zip/ZipConstants.hpp>
28 #include <com/sun/star/io/XOutputStream.hpp>
29 #include <comphelper/storagehelper.hxx>
30
31 #include <osl/time.h>
32
33 #include <EncryptionData.hxx>
34 #include <PackageConstants.hxx>
35 #include <ZipEntry.hxx>
36 #include <ZipFile.hxx>
37 #include <ZipPackageStream.hxx>
38 #include <ZipOutputStream.hxx>
39
40 using namespace rtl;
41 using namespace com::sun::star;
42 using namespace com::sun::star::io;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::packages;
45 using namespace com::sun::star::packages::zip;
46 using namespace com::sun::star::packages::zip::ZipConstants;
47
48 /** This class is used to write Zip files
49 */
ZipOutputStream(const uno::Reference<lang::XMultiServiceFactory> & xFactory,const uno::Reference<XOutputStream> & xOStream)50 ZipOutputStream::ZipOutputStream( const uno::Reference< lang::XMultiServiceFactory >& xFactory,
51 const uno::Reference < XOutputStream > &xOStream )
52 : m_xFactory( xFactory )
53 , xStream(xOStream)
54 , m_aDeflateBuffer(n_ConstBufferSize)
55 , aDeflater(DEFAULT_COMPRESSION, sal_True)
56 , aChucker(xOStream)
57 , pCurrentEntry(NULL)
58 , nMethod(DEFLATED)
59 , bFinished(sal_False)
60 , bEncryptCurrentEntry(sal_False)
61 , m_pCurrentStream(NULL)
62 {
63 }
64
~ZipOutputStream(void)65 ZipOutputStream::~ZipOutputStream( void )
66 {
67 for (sal_Int32 i = 0, nEnd = aZipList.size(); i < nEnd; i++)
68 delete aZipList[i];
69 }
70
setMethod(sal_Int32 nNewMethod)71 void SAL_CALL ZipOutputStream::setMethod( sal_Int32 nNewMethod )
72 {
73 nMethod = static_cast < sal_Int16 > (nNewMethod);
74 }
setLevel(sal_Int32 nNewLevel)75 void SAL_CALL ZipOutputStream::setLevel( sal_Int32 nNewLevel )
76 {
77 aDeflater.setLevel( nNewLevel);
78 }
79
putNextEntry(ZipEntry & rEntry,ZipPackageStream * pStream,sal_Bool bEncrypt)80 void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry& rEntry,
81 ZipPackageStream* pStream,
82 sal_Bool bEncrypt)
83 {
84 if (pCurrentEntry != NULL)
85 closeEntry();
86 if (rEntry.nTime == -1)
87 rEntry.nTime = getCurrentDosTime();
88 if (rEntry.nMethod == -1)
89 rEntry.nMethod = nMethod;
90 rEntry.nVersion = 20;
91 rEntry.nFlag = 1 << 11;
92 if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
93 rEntry.nCrc == -1)
94 {
95 rEntry.nSize = rEntry.nCompressedSize = 0;
96 rEntry.nFlag |= 8;
97 }
98
99 if (bEncrypt)
100 {
101 bEncryptCurrentEntry = sal_True;
102
103 m_xCipherContext = ZipFile::StaticGetCipher( m_xFactory, pStream->GetEncryptionData(), true );
104 m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( m_xFactory, pStream->GetEncryptionData() );
105 mnDigested = 0;
106 rEntry.nFlag |= 1 << 4;
107 m_pCurrentStream = pStream;
108 }
109 sal_Int32 nLOCLength = writeLOC(rEntry);
110 rEntry.nFileHeaderOffset = static_cast < sal_Int32 > (aChucker.GetPosition()) - nLOCLength;
111 aZipList.push_back( &rEntry );
112 pCurrentEntry = &rEntry;
113 }
114
closeEntry()115 void SAL_CALL ZipOutputStream::closeEntry( )
116 {
117 ZipEntry *pEntry = pCurrentEntry;
118 if (pEntry)
119 {
120 switch (pEntry->nMethod)
121 {
122 case DEFLATED:
123 aDeflater.finish();
124 while (!aDeflater.finished())
125 doDeflate();
126 if ((pEntry->nFlag & 8) == 0)
127 {
128 if (pEntry->nSize != aDeflater.getTotalIn())
129 {
130 OSL_ENSURE(false,"Invalid entry size");
131 }
132 if (pEntry->nCompressedSize != aDeflater.getTotalOut())
133 {
134 //VOS_DEBUG_ONLY("Invalid entry compressed size");
135 // Different compression strategies make the merit of this
136 // test somewhat dubious
137 pEntry->nCompressedSize = aDeflater.getTotalOut();
138 }
139 if (pEntry->nCrc != aCRC.getValue())
140 {
141 OSL_ENSURE(false,"Invalid entry CRC-32");
142 }
143 }
144 else
145 {
146 if ( !bEncryptCurrentEntry )
147 {
148 pEntry->nSize = aDeflater.getTotalIn();
149 pEntry->nCompressedSize = aDeflater.getTotalOut();
150 }
151 pEntry->nCrc = aCRC.getValue();
152 writeEXT(*pEntry);
153 }
154 aDeflater.reset();
155 aCRC.reset();
156 break;
157 case STORED:
158 if (!((pEntry->nFlag & 8) == 0))
159 OSL_ENSURE ( false, "Serious error, one of compressed size, size or CRC was -1 in a STORED stream");
160 break;
161 default:
162 OSL_ENSURE(false,"Invalid compression method");
163 break;
164 }
165
166 if (bEncryptCurrentEntry)
167 {
168 bEncryptCurrentEntry = sal_False;
169
170 m_xCipherContext.clear();
171
172 uno::Sequence< sal_Int8 > aDigestSeq;
173 if ( m_xDigestContext.is() )
174 {
175 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
176 m_xDigestContext.clear();
177 }
178
179 if ( m_pCurrentStream )
180 m_pCurrentStream->setDigest( aDigestSeq );
181 }
182 pCurrentEntry = NULL;
183 m_pCurrentStream = NULL;
184 }
185 }
186
write(const Sequence<sal_Int8> & rBuffer,sal_Int32 nNewOffset,sal_Int32 nNewLength)187 void SAL_CALL ZipOutputStream::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
188 {
189 switch (pCurrentEntry->nMethod)
190 {
191 case DEFLATED:
192 if (!aDeflater.finished())
193 {
194 aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
195 while (!aDeflater.needsInput())
196 doDeflate();
197 if (!bEncryptCurrentEntry)
198 aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
199 }
200 break;
201 case STORED:
202 {
203 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
204 aChucker.WriteBytes( aTmpBuffer );
205 }
206 break;
207 }
208 }
209
rawWrite(Sequence<sal_Int8> & rBuffer,sal_Int32,sal_Int32 nNewLength)210 void SAL_CALL ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
211 {
212 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
213 aChucker.WriteBytes( aTmpBuffer );
214 }
215
rawCloseEntry()216 void SAL_CALL ZipOutputStream::rawCloseEntry( )
217 {
218 if ( pCurrentEntry->nMethod == DEFLATED && ( pCurrentEntry->nFlag & 8 ) )
219 writeEXT(*pCurrentEntry);
220 pCurrentEntry = NULL;
221 }
222
finish()223 void SAL_CALL ZipOutputStream::finish( )
224 {
225 if (bFinished)
226 return;
227
228 if (pCurrentEntry != NULL)
229 closeEntry();
230
231 if (aZipList.size() < 1)
232 OSL_ENSURE(false,"Zip file must have at least one entry!\n");
233
234 sal_Int32 nCENOffset= static_cast < sal_Int32 > (aChucker.GetPosition());
235 for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++)
236 writeCEN( *aZipList[i] );
237 writeEND( nCENOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) - nCENOffset);
238 bFinished = sal_True;
239 xStream->flush();
240 }
241
doDeflate()242 void ZipOutputStream::doDeflate()
243 {
244 sal_Int32 nLength = aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength());
245
246 if ( nLength > 0 )
247 {
248 uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength );
249 if ( bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
250 {
251 // Need to update our digest before encryption...
252 sal_Int32 nDiff = n_ConstDigestLength - mnDigested;
253 if ( nDiff )
254 {
255 sal_Int32 nEat = ::std::min( nLength, nDiff );
256 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
257 m_xDigestContext->updateDigest( aTmpSeq );
258 mnDigested = mnDigested + static_cast< sal_Int16 >( nEat );
259 }
260
261 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
262
263 aChucker.WriteBytes( aEncryptionBuffer );
264
265 // the sizes as well as checksum for encrypted streams is calculated here
266 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
267 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
268 aCRC.update( aEncryptionBuffer );
269 }
270 else
271 {
272 aChucker.WriteBytes ( aTmpBuffer );
273 }
274 }
275
276 if ( aDeflater.finished() && bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
277 {
278 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
279 if ( aEncryptionBuffer.getLength() )
280 {
281 aChucker.WriteBytes( aEncryptionBuffer );
282
283 // the sizes as well as checksum for encrypted streams is calculated hier
284 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
285 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
286 aCRC.update( aEncryptionBuffer );
287 }
288 }
289 }
290
writeEND(sal_uInt32 nCENOffset,sal_uInt32 nLength)291 void ZipOutputStream::writeEND(sal_uInt32 nCENOffset, sal_uInt32 nLength)
292 {
293 aChucker << ENDSIG;
294 aChucker << static_cast < sal_Int16 > ( 0 );
295 aChucker << static_cast < sal_Int16 > ( 0 );
296 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
297 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
298 aChucker << nLength;
299 aChucker << nCENOffset;
300 aChucker << static_cast < sal_Int16 > ( 0 );
301 }
writeCEN(const ZipEntry & rEntry)302 void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
303 {
304 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
305 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() );
306
307 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
308 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
309
310 aChucker << CENSIG;
311 aChucker << rEntry.nVersion;
312 aChucker << rEntry.nVersion;
313 if (rEntry.nFlag & (1 << 4) )
314 {
315 // If it's an encrypted entry, we pretend its stored plain text
316 ZipEntry *pEntry = const_cast < ZipEntry * > ( &rEntry );
317 pEntry->nFlag &= ~(1 <<4 );
318 aChucker << rEntry.nFlag;
319 aChucker << static_cast < sal_Int16 > ( STORED );
320 }
321 else
322 {
323 aChucker << rEntry.nFlag;
324 aChucker << rEntry.nMethod;
325 }
326 aChucker << static_cast < sal_uInt32> ( rEntry.nTime );
327 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
328 aChucker << rEntry.nCompressedSize;
329 aChucker << rEntry.nSize;
330 aChucker << nNameLength;
331 aChucker << static_cast < sal_Int16> (0); // extra field length
332 aChucker << static_cast < sal_Int16> (0); // file comment length
333 aChucker << static_cast < sal_Int16> (0); // disk number start
334 aChucker << static_cast < sal_Int16> (0); // internal file attributes
335 aChucker << static_cast < sal_Int32> (0); // external file attributes
336 aChucker << rEntry.nFileHeaderOffset;
337
338 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
339 aChucker.WriteBytes( aSequence );
340 }
writeEXT(const ZipEntry & rEntry)341 void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
342 {
343 aChucker << EXTSIG;
344 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
345 aChucker << rEntry.nCompressedSize;
346 aChucker << rEntry.nSize;
347 }
348
writeLOC(const ZipEntry & rEntry)349 sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry )
350 {
351 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
352 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() );
353
354 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
355 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
356
357 aChucker << LOCSIG;
358 aChucker << rEntry.nVersion; // version needed to extract
359
360 if (rEntry.nFlag & (1 << 4) )
361 {
362 // If it's an encrypted entry, we pretend its stored plain text
363 sal_Int16 nTmpFlag = rEntry.nFlag;
364 nTmpFlag &= ~(1 <<4 );
365 aChucker << nTmpFlag; // general purpose bit flag
366 aChucker << static_cast < sal_Int16 > ( STORED ); // compression method
367 }
368 else
369 {
370 aChucker << rEntry.nFlag; // general purpose bit flag
371 aChucker << rEntry.nMethod; // compression method
372 }
373
374 aChucker << static_cast < sal_uInt32 > (rEntry.nTime); // last mod file time & date
375 if ((rEntry.nFlag & 8) == 8 )
376 {
377 aChucker << static_cast < sal_Int32 > (0); // crc-32
378 aChucker << static_cast < sal_Int32 > (0); // compressed size
379 aChucker << static_cast < sal_Int32 > (0); // uncompressed size
380 }
381 else
382 {
383 aChucker << static_cast < sal_uInt32 > (rEntry.nCrc); // crc-32
384 aChucker << rEntry.nCompressedSize; // compressed size
385 aChucker << rEntry.nSize; // uncompressed size
386 }
387 aChucker << nNameLength; // file name length
388 aChucker << static_cast < sal_Int16 > (0); // extra field length
389
390 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
391 aChucker.WriteBytes( aSequence ); // file name
392
393 return LOCHDR + nNameLength;
394 }
getCurrentDosTime()395 sal_uInt32 ZipOutputStream::getCurrentDosTime( )
396 {
397 oslDateTime aDateTime;
398 TimeValue aTimeValue;
399 osl_getSystemTime ( &aTimeValue );
400 osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
401
402 sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
403
404 if (nYear>1980)
405 nYear-=1980;
406 else if (nYear>80)
407 nYear-=80;
408 sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
409 ( 32 * (aDateTime.Month)) +
410 ( 512 * nYear ) ) << 16) |
411 ( ( aDateTime.Seconds/2) +
412 ( 32 * aDateTime.Minutes) +
413 ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
414 return nResult;
415 }
416 /*
417
418 This is actually never used, so I removed it, but thought that the
419 implementation details may be useful in the future...mtg 20010307
420
421 I stopped using the time library and used the OSL version instead, but
422 it might still be useful to have this code here..
423
424 void ZipOutputStream::dosDateToTMDate ( tm &rTime, sal_uInt32 nDosDate)
425 {
426 sal_uInt32 nDate = static_cast < sal_uInt32 > (nDosDate >> 16);
427 rTime.tm_mday = static_cast < sal_uInt32 > ( nDate & 0x1F);
428 rTime.tm_mon = static_cast < sal_uInt32 > ( ( ( (nDate) & 0x1E0)/0x20)-1);
429 rTime.tm_year = static_cast < sal_uInt32 > ( ( (nDate & 0x0FE00)/0x0200)+1980);
430
431 rTime.tm_hour = static_cast < sal_uInt32 > ( (nDosDate & 0xF800)/0x800);
432 rTime.tm_min = static_cast < sal_uInt32 > ( (nDosDate & 0x7E0)/0x20);
433 rTime.tm_sec = static_cast < sal_uInt32 > ( 2 * (nDosDate & 0x1F) );
434 }
435 */
436