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 throw(RuntimeException)
73 {
74 nMethod = static_cast < sal_Int16 > (nNewMethod);
75 }
setLevel(sal_Int32 nNewLevel)76 void SAL_CALL ZipOutputStream::setLevel( sal_Int32 nNewLevel )
77 throw(RuntimeException)
78 {
79 aDeflater.setLevel( nNewLevel);
80 }
81
putNextEntry(ZipEntry & rEntry,ZipPackageStream * pStream,sal_Bool bEncrypt)82 void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry& rEntry,
83 ZipPackageStream* pStream,
84 sal_Bool bEncrypt)
85 throw(IOException, RuntimeException)
86 {
87 if (pCurrentEntry != NULL)
88 closeEntry();
89 if (rEntry.nTime == -1)
90 rEntry.nTime = getCurrentDosTime();
91 if (rEntry.nMethod == -1)
92 rEntry.nMethod = nMethod;
93 rEntry.nVersion = 20;
94 rEntry.nFlag = 1 << 11;
95 if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
96 rEntry.nCrc == -1)
97 {
98 rEntry.nSize = rEntry.nCompressedSize = 0;
99 rEntry.nFlag |= 8;
100 }
101
102 if (bEncrypt)
103 {
104 bEncryptCurrentEntry = sal_True;
105
106 m_xCipherContext = ZipFile::StaticGetCipher( m_xFactory, pStream->GetEncryptionData(), true );
107 m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( m_xFactory, pStream->GetEncryptionData() );
108 mnDigested = 0;
109 rEntry.nFlag |= 1 << 4;
110 m_pCurrentStream = pStream;
111 }
112 sal_Int32 nLOCLength = writeLOC(rEntry);
113 rEntry.nOffset = static_cast < sal_Int32 > (aChucker.GetPosition()) - nLOCLength;
114 aZipList.push_back( &rEntry );
115 pCurrentEntry = &rEntry;
116 }
117
closeEntry()118 void SAL_CALL ZipOutputStream::closeEntry( )
119 throw(IOException, RuntimeException)
120 {
121 ZipEntry *pEntry = pCurrentEntry;
122 if (pEntry)
123 {
124 switch (pEntry->nMethod)
125 {
126 case DEFLATED:
127 aDeflater.finish();
128 while (!aDeflater.finished())
129 doDeflate();
130 if ((pEntry->nFlag & 8) == 0)
131 {
132 if (pEntry->nSize != aDeflater.getTotalIn())
133 {
134 OSL_ENSURE(false,"Invalid entry size");
135 }
136 if (pEntry->nCompressedSize != aDeflater.getTotalOut())
137 {
138 //VOS_DEBUG_ONLY("Invalid entry compressed size");
139 // Different compression strategies make the merit of this
140 // test somewhat dubious
141 pEntry->nCompressedSize = aDeflater.getTotalOut();
142 }
143 if (pEntry->nCrc != aCRC.getValue())
144 {
145 OSL_ENSURE(false,"Invalid entry CRC-32");
146 }
147 }
148 else
149 {
150 if ( !bEncryptCurrentEntry )
151 {
152 pEntry->nSize = aDeflater.getTotalIn();
153 pEntry->nCompressedSize = aDeflater.getTotalOut();
154 }
155 pEntry->nCrc = aCRC.getValue();
156 writeEXT(*pEntry);
157 }
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;
168 }
169
170 if (bEncryptCurrentEntry)
171 {
172 bEncryptCurrentEntry = sal_False;
173
174 m_xCipherContext.clear();
175
176 uno::Sequence< sal_Int8 > aDigestSeq;
177 if ( m_xDigestContext.is() )
178 {
179 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
180 m_xDigestContext.clear();
181 }
182
183 if ( m_pCurrentStream )
184 m_pCurrentStream->setDigest( aDigestSeq );
185 }
186 pCurrentEntry = NULL;
187 m_pCurrentStream = NULL;
188 }
189 }
190
write(const Sequence<sal_Int8> & rBuffer,sal_Int32 nNewOffset,sal_Int32 nNewLength)191 void SAL_CALL ZipOutputStream::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
192 throw(IOException, RuntimeException)
193 {
194 switch (pCurrentEntry->nMethod)
195 {
196 case DEFLATED:
197 if (!aDeflater.finished())
198 {
199 aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
200 while (!aDeflater.needsInput())
201 doDeflate();
202 if (!bEncryptCurrentEntry)
203 aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
204 }
205 break;
206 case STORED:
207 {
208 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
209 aChucker.WriteBytes( aTmpBuffer );
210 }
211 break;
212 }
213 }
214
rawWrite(Sequence<sal_Int8> & rBuffer,sal_Int32,sal_Int32 nNewLength)215 void SAL_CALL ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
216 throw(IOException, RuntimeException)
217 {
218 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
219 aChucker.WriteBytes( aTmpBuffer );
220 }
221
rawCloseEntry()222 void SAL_CALL ZipOutputStream::rawCloseEntry( )
223 throw(IOException, RuntimeException)
224 {
225 if ( pCurrentEntry->nMethod == DEFLATED && ( pCurrentEntry->nFlag & 8 ) )
226 writeEXT(*pCurrentEntry);
227 pCurrentEntry = NULL;
228 }
229
finish()230 void SAL_CALL ZipOutputStream::finish( )
231 throw(IOException, RuntimeException)
232 {
233 if (bFinished)
234 return;
235
236 if (pCurrentEntry != NULL)
237 closeEntry();
238
239 if (aZipList.size() < 1)
240 OSL_ENSURE(false,"Zip file must have at least one entry!\n");
241
242 sal_Int32 nOffset= static_cast < sal_Int32 > (aChucker.GetPosition());
243 for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++)
244 writeCEN( *aZipList[i] );
245 writeEND( nOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) - nOffset);
246 bFinished = sal_True;
247 xStream->flush();
248 }
249
doDeflate()250 void ZipOutputStream::doDeflate()
251 {
252 sal_Int32 nLength = aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength());
253
254 if ( nLength > 0 )
255 {
256 uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength );
257 if ( bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
258 {
259 // Need to update our digest before encryption...
260 sal_Int32 nDiff = n_ConstDigestLength - mnDigested;
261 if ( nDiff )
262 {
263 sal_Int32 nEat = ::std::min( nLength, nDiff );
264 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
265 m_xDigestContext->updateDigest( aTmpSeq );
266 mnDigested = mnDigested + static_cast< sal_Int16 >( nEat );
267 }
268
269 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
270
271 aChucker.WriteBytes( aEncryptionBuffer );
272
273 // the sizes as well as checksum for encrypted streams is calculated here
274 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
275 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
276 aCRC.update( aEncryptionBuffer );
277 }
278 else
279 {
280 aChucker.WriteBytes ( aTmpBuffer );
281 }
282 }
283
284 if ( aDeflater.finished() && bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
285 {
286 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
287 if ( aEncryptionBuffer.getLength() )
288 {
289 aChucker.WriteBytes( aEncryptionBuffer );
290
291 // the sizes as well as checksum for encrypted streams is calculated hier
292 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
293 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize;
294 aCRC.update( aEncryptionBuffer );
295 }
296 }
297 }
298
writeEND(sal_uInt32 nOffset,sal_uInt32 nLength)299 void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
300 throw(IOException, RuntimeException)
301 {
302 aChucker << ENDSIG;
303 aChucker << static_cast < sal_Int16 > ( 0 );
304 aChucker << static_cast < sal_Int16 > ( 0 );
305 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
306 aChucker << static_cast < sal_Int16 > ( aZipList.size() );
307 aChucker << nLength;
308 aChucker << nOffset;
309 aChucker << static_cast < sal_Int16 > ( 0 );
310 }
writeCEN(const ZipEntry & rEntry)311 void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
312 throw(IOException, RuntimeException)
313 {
314 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
315 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() );
316
317 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
318 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
319
320 aChucker << CENSIG;
321 aChucker << rEntry.nVersion;
322 aChucker << rEntry.nVersion;
323 if (rEntry.nFlag & (1 << 4) )
324 {
325 // If it's an encrypted entry, we pretend its stored plain text
326 ZipEntry *pEntry = const_cast < ZipEntry * > ( &rEntry );
327 pEntry->nFlag &= ~(1 <<4 );
328 aChucker << rEntry.nFlag;
329 aChucker << static_cast < sal_Int16 > ( STORED );
330 }
331 else
332 {
333 aChucker << rEntry.nFlag;
334 aChucker << rEntry.nMethod;
335 }
336 aChucker << static_cast < sal_uInt32> ( rEntry.nTime );
337 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
338 aChucker << rEntry.nCompressedSize;
339 aChucker << rEntry.nSize;
340 aChucker << nNameLength;
341 aChucker << static_cast < sal_Int16> (0);
342 aChucker << static_cast < sal_Int16> (0);
343 aChucker << static_cast < sal_Int16> (0);
344 aChucker << static_cast < sal_Int16> (0);
345 aChucker << static_cast < sal_Int32> (0);
346 aChucker << rEntry.nOffset;
347
348 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
349 aChucker.WriteBytes( aSequence );
350 }
writeEXT(const ZipEntry & rEntry)351 void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
352 throw(IOException, RuntimeException)
353 {
354 aChucker << EXTSIG;
355 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
356 aChucker << rEntry.nCompressedSize;
357 aChucker << rEntry.nSize;
358 }
359
writeLOC(const ZipEntry & rEntry)360 sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry )
361 throw(IOException, RuntimeException)
362 {
363 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) )
364 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() );
365
366 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
367 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
368
369 aChucker << LOCSIG;
370 aChucker << rEntry.nVersion;
371
372 if (rEntry.nFlag & (1 << 4) )
373 {
374 // If it's an encrypted entry, we pretend its stored plain text
375 sal_Int16 nTmpFlag = rEntry.nFlag;
376 nTmpFlag &= ~(1 <<4 );
377 aChucker << nTmpFlag;
378 aChucker << static_cast < sal_Int16 > ( STORED );
379 }
380 else
381 {
382 aChucker << rEntry.nFlag;
383 aChucker << rEntry.nMethod;
384 }
385
386 aChucker << static_cast < sal_uInt32 > (rEntry.nTime);
387 if ((rEntry.nFlag & 8) == 8 )
388 {
389 aChucker << static_cast < sal_Int32 > (0);
390 aChucker << static_cast < sal_Int32 > (0);
391 aChucker << static_cast < sal_Int32 > (0);
392 }
393 else
394 {
395 aChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
396 aChucker << rEntry.nCompressedSize;
397 aChucker << rEntry.nSize;
398 }
399 aChucker << nNameLength;
400 aChucker << static_cast < sal_Int16 > (0);
401
402 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
403 aChucker.WriteBytes( aSequence );
404
405 return LOCHDR + nNameLength;
406 }
getCurrentDosTime()407 sal_uInt32 ZipOutputStream::getCurrentDosTime( )
408 {
409 oslDateTime aDateTime;
410 TimeValue aTimeValue;
411 osl_getSystemTime ( &aTimeValue );
412 osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
413
414 sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
415
416 if (nYear>1980)
417 nYear-=1980;
418 else if (nYear>80)
419 nYear-=80;
420 sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
421 ( 32 * (aDateTime.Month)) +
422 ( 512 * nYear ) ) << 16) |
423 ( ( aDateTime.Seconds/2) +
424 ( 32 * aDateTime.Minutes) +
425 ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
426 return nResult;
427 }
428 /*
429
430 This is actually never used, so I removed it, but thought that the
431 implementation details may be useful in the future...mtg 20010307
432
433 I stopped using the time library and used the OSL version instead, but
434 it might still be useful to have this code here..
435
436 void ZipOutputStream::dosDateToTMDate ( tm &rTime, sal_uInt32 nDosDate)
437 {
438 sal_uInt32 nDate = static_cast < sal_uInt32 > (nDosDate >> 16);
439 rTime.tm_mday = static_cast < sal_uInt32 > ( nDate & 0x1F);
440 rTime.tm_mon = static_cast < sal_uInt32 > ( ( ( (nDate) & 0x1E0)/0x20)-1);
441 rTime.tm_year = static_cast < sal_uInt32 > ( ( (nDate & 0x0FE00)/0x0200)+1980);
442
443 rTime.tm_hour = static_cast < sal_uInt32 > ( (nDosDate & 0xF800)/0x800);
444 rTime.tm_min = static_cast < sal_uInt32 > ( (nDosDate & 0x7E0)/0x20);
445 rTime.tm_sec = static_cast < sal_uInt32 > ( 2 * (nDosDate & 0x1F) );
446 }
447 */
448
449