1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <ZipOutputEntry.hxx>
22 #include <com/sun/star/io/TempFile.hpp>
23 #include <com/sun/star/packages/zip/ZipConstants.hpp>
24 #include <com/sun/star/xml/crypto/CipherID.hpp>
26 #include <osl/diagnose.h>
28 #include <PackageConstants.hxx>
29 #include <ThreadedDeflater.hxx>
30 #include <ZipEntry.hxx>
31 #include <ZipFile.hxx>
32 #include <ZipPackageStream.hxx>
37 using namespace com::sun::star
;
38 using namespace com::sun::star::io
;
39 using namespace com::sun::star::uno
;
40 using namespace com::sun::star::packages::zip::ZipConstants
;
42 /** This class is used to deflate Zip entries
44 ZipOutputEntryBase::ZipOutputEntryBase(
45 css::uno::Reference
< css::io::XOutputStream
> xOutput
,
46 uno::Reference
< uno::XComponentContext
> xContext
,
48 ZipPackageStream
* pStream
,
51 : m_xContext(std::move(xContext
))
52 , m_xOutStream(std::move(xOutput
))
53 , m_pCurrentEntry(pEntry
)
55 , m_pCurrentStream(pStream
)
56 , m_bEncryptCurrentEntry(bEncrypt
)
59 assert(m_pCurrentEntry
->nMethod
== DEFLATED
&& "Use ZipPackageStream::rawWrite() for STORED entries");
61 assert(!checkStream
|| m_xOutStream
.is());
62 if (m_bEncryptCurrentEntry
)
64 m_xCipherContext
= ZipFile::StaticGetCipher( m_xContext
, pStream
->GetEncryptionData(), true );
65 if (pStream
->GetEncryptionData()->m_oCheckAlg
)
67 assert(pStream
->GetEncryptionData()->m_nEncAlg
!= xml::crypto::CipherID::AES_GCM_W3C
);
68 m_xDigestContext
= ZipFile::StaticGetDigestContextForChecksum(m_xContext
, pStream
->GetEncryptionData());
73 void ZipOutputEntryBase::closeEntry()
77 if ((m_pCurrentEntry
->nFlag
& 8) == 0)
79 if (m_pCurrentEntry
->nSize
!= getDeflaterTotalIn())
81 OSL_FAIL("Invalid entry size");
83 if (m_pCurrentEntry
->nCompressedSize
!= getDeflaterTotalOut())
85 // Different compression strategies make the merit of this
86 // test somewhat dubious
87 m_pCurrentEntry
->nCompressedSize
= getDeflaterTotalOut();
89 if (m_pCurrentEntry
->nCrc
!= m_aCRC
.getValue())
91 OSL_FAIL("Invalid entry CRC-32");
96 if ( !m_bEncryptCurrentEntry
)
98 m_pCurrentEntry
->nSize
= getDeflaterTotalIn();
99 m_pCurrentEntry
->nCompressedSize
= getDeflaterTotalOut();
101 m_pCurrentEntry
->nCrc
= m_aCRC
.getValue();
106 if (!m_bEncryptCurrentEntry
)
109 m_xCipherContext
.clear();
111 uno::Sequence
< sal_Int8
> aDigestSeq
;
112 if ( m_xDigestContext
.is() )
114 aDigestSeq
= m_xDigestContext
->finalizeDigestAndDispose();
115 m_xDigestContext
.clear();
118 if ( m_pCurrentStream
)
119 m_pCurrentStream
->setDigest( aDigestSeq
);
122 void ZipOutputEntryBase::processDeflated( const uno::Sequence
< sal_Int8
>& deflateBuffer
, sal_Int32 nLength
)
126 uno::Sequence
< sal_Int8
> aTmpBuffer( deflateBuffer
.getConstArray(), nLength
);
127 if (m_bEncryptCurrentEntry
&& m_xCipherContext
.is())
129 // Need to update our digest before encryption...
130 sal_Int32 nDiff
= n_ConstDigestLength
- m_nDigested
;
131 if (m_xDigestContext
.is() && nDiff
)
133 sal_Int32 nEat
= ::std::min( nLength
, nDiff
);
134 uno::Sequence
< sal_Int8
> aTmpSeq( aTmpBuffer
.getConstArray(), nEat
);
135 m_xDigestContext
->updateDigest( aTmpSeq
);
136 m_nDigested
= m_nDigested
+ static_cast< sal_Int16
>( nEat
);
139 // FIXME64: uno::Sequence not 64bit safe.
140 uno::Sequence
< sal_Int8
> aEncryptionBuffer
= m_xCipherContext
->convertWithCipherContext( aTmpBuffer
);
142 m_xOutStream
->writeBytes( aEncryptionBuffer
);
144 // the sizes as well as checksum for encrypted streams is calculated here
145 m_pCurrentEntry
->nCompressedSize
+= aEncryptionBuffer
.getLength();
146 m_pCurrentEntry
->nSize
= m_pCurrentEntry
->nCompressedSize
;
147 m_aCRC
.update( aEncryptionBuffer
);
151 m_xOutStream
->writeBytes ( aTmpBuffer
);
155 if (!(isDeflaterFinished() && m_bEncryptCurrentEntry
&& m_xCipherContext
.is()))
158 // FIXME64: sequence not 64bit safe.
159 uno::Sequence
< sal_Int8
> aEncryptionBuffer
= m_xCipherContext
->finalizeCipherContextAndDispose();
160 if ( aEncryptionBuffer
.hasElements() )
162 m_xOutStream
->writeBytes( aEncryptionBuffer
);
164 // the sizes as well as checksum for encrypted streams are calculated here
165 m_pCurrentEntry
->nCompressedSize
+= aEncryptionBuffer
.getLength();
166 m_pCurrentEntry
->nSize
= m_pCurrentEntry
->nCompressedSize
;
167 m_aCRC
.update( aEncryptionBuffer
);
171 void ZipOutputEntryBase::processInput( const uno::Sequence
< sal_Int8
>& rBuffer
)
173 if (!m_bEncryptCurrentEntry
)
174 m_aCRC
.updateSegment(rBuffer
, rBuffer
.getLength());
177 ZipOutputEntry::ZipOutputEntry(
178 const css::uno::Reference
< css::io::XOutputStream
>& rxOutput
,
179 const uno::Reference
< uno::XComponentContext
>& rxContext
,
181 ZipPackageStream
* pStream
,
184 : ZipOutputEntryBase(rxOutput
, rxContext
, pEntry
, pStream
, bEncrypt
, checkStream
)
185 , m_aDeflateBuffer(n_ConstBufferSize
)
186 , m_aDeflater(DEFAULT_COMPRESSION
, true)
190 ZipOutputEntry::ZipOutputEntry(
191 const css::uno::Reference
< css::io::XOutputStream
>& rxOutput
,
192 const uno::Reference
< uno::XComponentContext
>& rxContext
,
194 ZipPackageStream
* pStream
,
196 : ZipOutputEntry( rxOutput
, rxContext
, pEntry
, pStream
, bEncrypt
, true)
200 void ZipOutputEntry::write( const Sequence
< sal_Int8
>& rBuffer
)
202 if (!m_aDeflater
.finished())
204 m_aDeflater
.setInputSegment(rBuffer
);
205 while (!m_aDeflater
.needsInput())
207 processInput(rBuffer
);
211 void ZipOutputEntry::doDeflate()
213 sal_Int32 nLength
= m_aDeflater
.doDeflateSegment(m_aDeflateBuffer
, m_aDeflateBuffer
.getLength());
214 processDeflated( m_aDeflateBuffer
, nLength
);
217 void ZipOutputEntry::finishDeflater()
219 m_aDeflater
.finish();
220 while (!m_aDeflater
.finished())
224 sal_Int64
ZipOutputEntry::getDeflaterTotalIn() const
226 return m_aDeflater
.getTotalIn();
229 sal_Int64
ZipOutputEntry::getDeflaterTotalOut() const
231 return m_aDeflater
.getTotalOut();
234 void ZipOutputEntry::deflaterReset()
239 bool ZipOutputEntry::isDeflaterFinished() const
241 return m_aDeflater
.finished();
245 ZipOutputEntryInThread::ZipOutputEntryInThread(
246 const uno::Reference
< uno::XComponentContext
>& rxContext
,
247 std::unique_ptr
<ZipEntry
>&& pEntry
,
248 ZipPackageStream
* pStream
,
250 : ZipOutputEntry( uno::Reference
< css::io::XOutputStream
>(), rxContext
, pEntry
.get(), pStream
, bEncrypt
, false )
251 , m_pOwnedZipEntry(std::move(pEntry
))
256 void ZipOutputEntryInThread::createBufferFile()
258 assert(!m_xOutStream
&& !m_xTempFile
&&
259 "should only be called in the threaded mode where there is no existing stream yet");
260 m_xTempFile
= new utl::TempFileFastService
;
261 m_xOutStream
= m_xTempFile
->getOutputStream();
264 void ZipOutputEntryInThread::closeBufferFile()
266 m_xOutStream
->closeOutput();
267 m_xOutStream
.clear();
270 void ZipOutputEntryInThread::deleteBufferFile()
272 assert(!m_xOutStream
.is() && m_xTempFile
);
276 uno::Reference
< io::XInputStream
> ZipOutputEntryInThread::getData() const
278 return m_xTempFile
->getInputStream();
281 class ZipOutputEntryInThread::Task
: public comphelper::ThreadTask
283 ZipOutputEntryInThread
*mpEntry
;
284 uno::Reference
< io::XInputStream
> mxInStream
;
287 Task( const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
, ZipOutputEntryInThread
*pEntry
,
288 uno::Reference
< io::XInputStream
> xInStream
)
289 : comphelper::ThreadTask(pTag
)
291 , mxInStream(std::move(xInStream
))
295 virtual void doWork() override
299 mpEntry
->createBufferFile();
300 mpEntry
->writeStream(mxInStream
);
302 mpEntry
->closeBufferFile();
303 mpEntry
->setFinished();
307 mpEntry
->setParallelDeflateException(std::current_exception());
310 if (mpEntry
->m_xOutStream
.is())
311 mpEntry
->closeBufferFile();
312 if (mpEntry
->m_xTempFile
)
313 mpEntry
->deleteBufferFile();
315 catch (uno::Exception
const&)
318 mpEntry
->setFinished();
323 std::unique_ptr
<comphelper::ThreadTask
> ZipOutputEntryInThread::createTask(
324 const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
,
325 const uno::Reference
< io::XInputStream
>& xInStream
)
327 return std::make_unique
<Task
>(pTag
, this, xInStream
);
330 void ZipOutputEntry::writeStream(const uno::Reference
< io::XInputStream
>& xInStream
)
332 sal_Int32 nLength
= 0;
333 uno::Sequence
< sal_Int8
> aSeq(n_ConstBufferSize
);
336 nLength
= xInStream
->readBytes(aSeq
, n_ConstBufferSize
);
337 if (nLength
!= n_ConstBufferSize
)
338 aSeq
.realloc(nLength
);
342 while (nLength
== n_ConstBufferSize
);
347 ZipOutputEntryParallel::ZipOutputEntryParallel(
348 const css::uno::Reference
< css::io::XOutputStream
>& rxOutput
,
349 const uno::Reference
< uno::XComponentContext
>& rxContext
,
351 ZipPackageStream
* pStream
,
353 : ZipOutputEntryBase(rxOutput
, rxContext
, pEntry
, pStream
, bEncrypt
, true)
360 void ZipOutputEntryParallel::writeStream(const uno::Reference
< io::XInputStream
>& xInStream
)
362 ZipUtils::ThreadedDeflater
deflater( DEFAULT_COMPRESSION
);
363 deflater
.deflateWrite(xInStream
,
364 [this](const uno::Sequence
< sal_Int8
>& rBuffer
, sal_Int32 nLen
) {
365 if (!m_bEncryptCurrentEntry
)
366 m_aCRC
.updateSegment(rBuffer
, nLen
);
368 [this](const uno::Sequence
< sal_Int8
>& rBuffer
, sal_Int32 nLen
) {
369 processDeflated(rBuffer
, nLen
);
373 processDeflated( uno::Sequence
< sal_Int8
>(), 0 ); // finish encrypting, etc.
374 totalIn
= deflater
.getTotalIn();
375 totalOut
= deflater
.getTotalOut();
379 void ZipOutputEntryParallel::finishDeflater()
381 // ThreadedDeflater is called synchronously in one call, so nothing to do here.
384 sal_Int64
ZipOutputEntryParallel::getDeflaterTotalIn() const
389 sal_Int64
ZipOutputEntryParallel::getDeflaterTotalOut() const
394 void ZipOutputEntryParallel::deflaterReset()
401 bool ZipOutputEntryParallel::isDeflaterFinished() const
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */