Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <ZipOutputEntry.hxx>
21 :
22 : #include <com/sun/star/packages/zip/ZipConstants.hpp>
23 : #include <comphelper/storagehelper.hxx>
24 :
25 : #include <osl/time.h>
26 :
27 : #include <PackageConstants.hxx>
28 : #include <ZipEntry.hxx>
29 : #include <ZipFile.hxx>
30 : #include <ZipPackageStream.hxx>
31 :
32 : using namespace com::sun::star;
33 : using namespace com::sun::star::io;
34 : using namespace com::sun::star::uno;
35 : using namespace com::sun::star::packages::zip::ZipConstants;
36 :
37 : /** This class is used to deflate Zip entries
38 : */
39 19164 : ZipOutputEntry::ZipOutputEntry( const uno::Reference< uno::XComponentContext >& rxContext,
40 : ByteChucker& rChucker,
41 : ZipEntry& rEntry,
42 : ZipPackageStream* pStream,
43 : bool bEncrypt)
44 : : m_aDeflateBuffer(n_ConstBufferSize)
45 : , m_aDeflater(DEFAULT_COMPRESSION, true)
46 : , m_rChucker(rChucker)
47 : , m_pCurrentEntry(&rEntry)
48 : , m_nDigested(0)
49 : , m_bEncryptCurrentEntry(false)
50 19164 : , m_pCurrentStream(NULL)
51 : {
52 19164 : if (rEntry.nTime == -1)
53 17236 : rEntry.nTime = getCurrentDosTime();
54 19164 : if (rEntry.nMethod == -1)
55 0 : rEntry.nMethod = DEFLATED;
56 19164 : rEntry.nVersion = 20;
57 19164 : rEntry.nFlag = 1 << 11;
58 20784 : if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
59 1620 : rEntry.nCrc == -1)
60 : {
61 17544 : rEntry.nSize = rEntry.nCompressedSize = 0;
62 17544 : rEntry.nFlag |= 8;
63 : }
64 :
65 19164 : if (bEncrypt)
66 : {
67 10 : m_bEncryptCurrentEntry = true;
68 :
69 10 : m_xCipherContext = ZipFile::StaticGetCipher( rxContext, pStream->GetEncryptionData(), true );
70 10 : m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( rxContext, pStream->GetEncryptionData() );
71 10 : m_nDigested = 0;
72 10 : rEntry.nFlag |= 1 << 4;
73 10 : m_pCurrentStream = pStream;
74 : }
75 19164 : sal_Int32 nLOCLength = writeLOC(rEntry);
76 19164 : rEntry.nOffset = m_rChucker.GetPosition() - nLOCLength;
77 19164 : }
78 :
79 19164 : ZipOutputEntry::~ZipOutputEntry( void )
80 : {
81 19164 : }
82 :
83 18064 : void SAL_CALL ZipOutputEntry::closeEntry( )
84 : throw(IOException, RuntimeException)
85 : {
86 18064 : ZipEntry *pEntry = m_pCurrentEntry;
87 18064 : if (pEntry)
88 : {
89 18064 : switch (pEntry->nMethod)
90 : {
91 : case DEFLATED:
92 17544 : m_aDeflater.finish();
93 52632 : while (!m_aDeflater.finished())
94 17544 : doDeflate();
95 17544 : if ((pEntry->nFlag & 8) == 0)
96 : {
97 0 : if (pEntry->nSize != m_aDeflater.getTotalIn())
98 : {
99 : OSL_FAIL("Invalid entry size");
100 : }
101 0 : if (pEntry->nCompressedSize != m_aDeflater.getTotalOut())
102 : {
103 : // Different compression strategies make the merit of this
104 : // test somewhat dubious
105 0 : pEntry->nCompressedSize = m_aDeflater.getTotalOut();
106 : }
107 0 : if (pEntry->nCrc != m_aCRC.getValue())
108 : {
109 : OSL_FAIL("Invalid entry CRC-32");
110 : }
111 : }
112 : else
113 : {
114 17544 : if ( !m_bEncryptCurrentEntry )
115 : {
116 17534 : pEntry->nSize = m_aDeflater.getTotalIn();
117 17534 : pEntry->nCompressedSize = m_aDeflater.getTotalOut();
118 : }
119 17544 : pEntry->nCrc = m_aCRC.getValue();
120 17544 : writeEXT(*pEntry);
121 : }
122 17544 : m_aDeflater.reset();
123 17544 : m_aCRC.reset();
124 17544 : break;
125 : case STORED:
126 520 : if (!((pEntry->nFlag & 8) == 0))
127 : OSL_FAIL( "Serious error, one of compressed size, size or CRC was -1 in a STORED stream");
128 520 : break;
129 : default:
130 : OSL_FAIL("Invalid compression method");
131 0 : break;
132 : }
133 :
134 18064 : if (m_bEncryptCurrentEntry)
135 : {
136 10 : m_bEncryptCurrentEntry = false;
137 :
138 10 : m_xCipherContext.clear();
139 :
140 10 : uno::Sequence< sal_Int8 > aDigestSeq;
141 10 : if ( m_xDigestContext.is() )
142 : {
143 10 : aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
144 10 : m_xDigestContext.clear();
145 : }
146 :
147 10 : if ( m_pCurrentStream )
148 10 : m_pCurrentStream->setDigest( aDigestSeq );
149 : }
150 18064 : m_pCurrentEntry = NULL;
151 18064 : m_pCurrentStream = NULL;
152 : }
153 18064 : }
154 :
155 18868 : void SAL_CALL ZipOutputEntry::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
156 : throw(IOException, RuntimeException)
157 : {
158 18868 : switch (m_pCurrentEntry->nMethod)
159 : {
160 : case DEFLATED:
161 18334 : if (!m_aDeflater.finished())
162 : {
163 18334 : m_aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength);
164 54902 : while (!m_aDeflater.needsInput())
165 18234 : doDeflate();
166 18334 : if (!m_bEncryptCurrentEntry)
167 18324 : m_aCRC.updateSegment(rBuffer, nNewOffset, nNewLength);
168 : }
169 18334 : break;
170 : case STORED:
171 : {
172 534 : Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
173 534 : m_rChucker.WriteBytes( aTmpBuffer );
174 : }
175 534 : break;
176 : }
177 18868 : }
178 :
179 188 : void SAL_CALL ZipOutputEntry::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength )
180 : throw(IOException, RuntimeException)
181 : {
182 188 : Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength );
183 188 : m_rChucker.WriteBytes( aTmpBuffer );
184 188 : }
185 :
186 1100 : void SAL_CALL ZipOutputEntry::rawCloseEntry( )
187 : throw(IOException, RuntimeException)
188 : {
189 1100 : if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
190 0 : writeEXT(*m_pCurrentEntry);
191 1100 : m_pCurrentEntry = NULL;
192 1100 : }
193 :
194 35778 : void ZipOutputEntry::doDeflate()
195 : {
196 35778 : sal_Int32 nLength = m_aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength());
197 :
198 35778 : if ( nLength > 0 )
199 : {
200 18010 : uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength );
201 18010 : if ( m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
202 : {
203 : // Need to update our digest before encryption...
204 10 : sal_Int32 nDiff = n_ConstDigestLength - m_nDigested;
205 10 : if ( nDiff )
206 : {
207 10 : sal_Int32 nEat = ::std::min( nLength, nDiff );
208 10 : uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
209 10 : m_xDigestContext->updateDigest( aTmpSeq );
210 10 : m_nDigested = m_nDigested + static_cast< sal_Int16 >( nEat );
211 : }
212 :
213 : // FIXME64: uno::Sequence not 64bit safe.
214 10 : uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
215 :
216 10 : m_rChucker.WriteBytes( aEncryptionBuffer );
217 :
218 : // the sizes as well as checksum for encrypted streams is calculated here
219 10 : m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
220 10 : m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
221 10 : m_aCRC.update( aEncryptionBuffer );
222 : }
223 : else
224 : {
225 18000 : m_rChucker.WriteBytes ( aTmpBuffer );
226 18010 : }
227 : }
228 :
229 35778 : if ( m_aDeflater.finished() && m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
230 : {
231 : // FIXME64: sequence not 64bit safe.
232 10 : uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
233 10 : if ( aEncryptionBuffer.getLength() )
234 : {
235 10 : m_rChucker.WriteBytes( aEncryptionBuffer );
236 :
237 : // the sizes as well as checksum for encrypted streams is calculated hier
238 10 : m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
239 10 : m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
240 10 : m_aCRC.update( aEncryptionBuffer );
241 10 : }
242 : }
243 35778 : }
244 :
245 38328 : static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
246 : {
247 38328 : if( nNum >= 0xffffffff )
248 : {
249 0 : *pIsTruncated = true;
250 0 : return 0xffffffff;
251 : }
252 : else
253 38328 : return static_cast< sal_uInt32 >( nNum );
254 : }
255 :
256 17544 : void ZipOutputEntry::writeEXT( const ZipEntry &rEntry )
257 : throw(IOException, RuntimeException)
258 : {
259 17544 : bool bWrite64Header = false;
260 :
261 17544 : m_rChucker << EXTSIG;
262 17544 : m_rChucker << static_cast < sal_uInt32> ( rEntry.nCrc );
263 17544 : m_rChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
264 17544 : m_rChucker << getTruncated( rEntry.nSize, &bWrite64Header );
265 :
266 17544 : if( bWrite64Header )
267 : {
268 : // FIXME64: need to append a ZIP64 header instead of throwing
269 : // We're about to silently lose people's data - which they are
270 : // unlikely to appreciate so fail instead:
271 0 : throw IOException( "File contains streams that are too large." );
272 : }
273 17544 : }
274 :
275 19164 : sal_Int32 ZipOutputEntry::writeLOC( const ZipEntry &rEntry )
276 : throw(IOException, RuntimeException)
277 : {
278 19164 : if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
279 0 : throw IOException("Unexpected character is used in file name." );
280 :
281 19164 : OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
282 19164 : sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
283 :
284 19164 : m_rChucker << LOCSIG;
285 19164 : m_rChucker << rEntry.nVersion;
286 :
287 19164 : if (rEntry.nFlag & (1 << 4) )
288 : {
289 : // If it's an encrypted entry, we pretend its stored plain text
290 10 : sal_Int16 nTmpFlag = rEntry.nFlag;
291 10 : nTmpFlag &= ~(1 <<4 );
292 10 : m_rChucker << nTmpFlag;
293 10 : m_rChucker << static_cast < sal_Int16 > ( STORED );
294 : }
295 : else
296 : {
297 19154 : m_rChucker << rEntry.nFlag;
298 19154 : m_rChucker << rEntry.nMethod;
299 : }
300 :
301 19164 : bool bWrite64Header = false;
302 :
303 19164 : m_rChucker << static_cast < sal_uInt32 > (rEntry.nTime);
304 19164 : if ((rEntry.nFlag & 8) == 8 )
305 : {
306 17544 : m_rChucker << static_cast < sal_Int32 > (0);
307 17544 : m_rChucker << static_cast < sal_Int32 > (0);
308 17544 : m_rChucker << static_cast < sal_Int32 > (0);
309 : }
310 : else
311 : {
312 1620 : m_rChucker << static_cast < sal_uInt32 > (rEntry.nCrc);
313 1620 : m_rChucker << getTruncated( rEntry.nCompressedSize, &bWrite64Header );
314 1620 : m_rChucker << getTruncated( rEntry.nSize, &bWrite64Header );
315 : }
316 19164 : m_rChucker << nNameLength;
317 19164 : m_rChucker << static_cast < sal_Int16 > (0);
318 :
319 19164 : if( bWrite64Header )
320 : {
321 : // FIXME64: need to append a ZIP64 header instead of throwing
322 : // We're about to silently lose people's data - which they are
323 : // unlikely to appreciate so fail instead:
324 0 : throw IOException( "File contains streams that are too large." );
325 : }
326 :
327 38328 : Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() );
328 19164 : m_rChucker.WriteBytes( aSequence );
329 :
330 38328 : return LOCHDR + nNameLength;
331 : }
332 18986 : sal_uInt32 ZipOutputEntry::getCurrentDosTime( )
333 : {
334 : oslDateTime aDateTime;
335 : TimeValue aTimeValue;
336 18986 : osl_getSystemTime ( &aTimeValue );
337 18986 : osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
338 :
339 : // at year 2108, there is an overflow
340 : // -> some decision needs to be made
341 : // how to handle the ZIP file format (just overflow?)
342 :
343 : // if the current system time is before 1980,
344 : // then the time traveller will have to make a decision
345 : // how to handle the ZIP file format before it is invented
346 : // (just underflow?)
347 :
348 : assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
349 :
350 18986 : sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
351 :
352 18986 : if (nYear>=1980)
353 18986 : nYear-=1980;
354 0 : else if (nYear>=80)
355 : {
356 0 : nYear-=80;
357 : }
358 : sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
359 37972 : ( 32 * (aDateTime.Month)) +
360 37972 : ( 512 * nYear ) ) << 16) |
361 : ( ( aDateTime.Seconds/2) +
362 37972 : ( 32 * aDateTime.Minutes) +
363 37972 : ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
364 18986 : return nResult;
365 : }
366 :
367 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|