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