summaryrefslogtreecommitdiffstats
path: root/package/source/zipapi
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--package/source/zipapi/ByteChucker.cxx51
-rw-r--r--package/source/zipapi/ByteGrabber.cxx125
-rw-r--r--package/source/zipapi/CRC32.cxx72
-rw-r--r--package/source/zipapi/Deflater.cxx148
-rw-r--r--package/source/zipapi/Inflater.cxx137
-rw-r--r--package/source/zipapi/MemoryByteGrabber.hxx88
-rw-r--r--package/source/zipapi/ThreadedDeflater.cxx220
-rw-r--r--package/source/zipapi/XBufferedThreadedStream.cxx189
-rw-r--r--package/source/zipapi/XBufferedThreadedStream.hxx83
-rw-r--r--package/source/zipapi/XUnbufferedStream.cxx323
-rw-r--r--package/source/zipapi/XUnbufferedStream.hxx93
-rw-r--r--package/source/zipapi/ZipEnumeration.cxx39
-rw-r--r--package/source/zipapi/ZipFile.cxx1263
-rw-r--r--package/source/zipapi/ZipOutputEntry.cxx400
-rw-r--r--package/source/zipapi/ZipOutputStream.cxx365
-rw-r--r--package/source/zipapi/blowfishcontext.cxx109
-rw-r--r--package/source/zipapi/blowfishcontext.hxx51
-rw-r--r--package/source/zipapi/sha1context.cxx119
-rw-r--r--package/source/zipapi/sha1context.hxx71
19 files changed, 3946 insertions, 0 deletions
diff --git a/package/source/zipapi/ByteChucker.cxx b/package/source/zipapi/ByteChucker.cxx
new file mode 100644
index 000000000..6c5994aa6
--- /dev/null
+++ b/package/source/zipapi/ByteChucker.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ByteChucker.hxx>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+
+ByteChucker::ByteChucker(Reference<XOutputStream> const & xOstream)
+: xStream(xOstream)
+, xSeek (xOstream, UNO_QUERY )
+, a2Sequence ( 2 )
+, a4Sequence ( 4 )
+, p2Sequence ( a2Sequence.getArray() )
+, p4Sequence ( a4Sequence.getArray() )
+{
+}
+
+ByteChucker::~ByteChucker()
+{
+}
+
+void ByteChucker::WriteBytes( const Sequence< sal_Int8 >& aData )
+{
+ xStream->writeBytes(aData);
+}
+
+sal_Int64 ByteChucker::GetPosition( )
+{
+ return xSeek->getPosition();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ByteGrabber.cxx b/package/source/zipapi/ByteGrabber.cxx
new file mode 100644
index 000000000..b58a7087f
--- /dev/null
+++ b/package/source/zipapi/ByteGrabber.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ByteGrabber.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+/** ByteGrabber implements the >> operators on an XOutputStream. This is
+ * potentially quite slow and may need to be optimised
+ */
+
+ByteGrabber::ByteGrabber(uno::Reference < io::XInputStream > const & xIstream)
+: xStream(xIstream)
+, xSeek (xIstream, uno::UNO_QUERY )
+, aSequence ( 4 )
+{
+ pSequence = aSequence.getArray();
+}
+
+ByteGrabber::~ByteGrabber()
+{
+}
+
+void ByteGrabber::setInputStream (const uno::Reference < io::XInputStream >& xNewStream)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ xStream = xNewStream;
+ xSeek.set(xNewStream, uno::UNO_QUERY);
+}
+
+// XInputStream chained
+sal_Int32 ByteGrabber::readBytes( uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return xStream->readBytes(aData, nBytesToRead );
+}
+
+// XSeekable chained...
+void ByteGrabber::seek( sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ sal_Int64 nLen = xSeek->getLength();
+ if ( location < 0 || location > nLen )
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ xSeek->seek( location );
+}
+
+sal_Int64 ByteGrabber::getPosition( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return xSeek->getPosition();
+}
+
+sal_Int64 ByteGrabber::getLength( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!xSeek.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return xSeek->getLength();
+}
+
+sal_uInt16 ByteGrabber::ReadUInt16()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (xStream->readBytes(aSequence, 2) != 2)
+ return 0;
+
+ pSequence = aSequence.getConstArray();
+ return static_cast <sal_uInt16>
+ ( (pSequence[0] & 0xFF)
+ | (pSequence[1] & 0xFF) << 8);
+}
+
+sal_uInt32 ByteGrabber::ReadUInt32()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (xStream->readBytes(aSequence, 4) != 4)
+ return 0;
+
+ pSequence = aSequence.getConstArray();
+ return static_cast < sal_uInt32 >
+ ( (pSequence[0] & 0xFF)
+ | ( pSequence[1] & 0xFF ) << 8
+ | ( pSequence[2] & 0xFF ) << 16
+ | ( pSequence[3] & 0xFF ) << 24 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/CRC32.cxx b/package/source/zipapi/CRC32.cxx
new file mode 100644
index 000000000..bd0682248
--- /dev/null
+++ b/package/source/zipapi/CRC32.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <CRC32.hxx>
+#include <PackageConstants.hxx>
+#include <rtl/crc.h>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+
+/** A class to compute the CRC32 value of a data stream
+ */
+
+CRC32::CRC32()
+: nCRC(0)
+{
+}
+void CRC32::reset()
+{
+ nCRC=0;
+}
+sal_Int32 CRC32::getValue() const
+{
+ return nCRC;
+}
+/** Update CRC32 with specified sequence of bytes
+ */
+void CRC32::updateSegment(const Sequence< sal_Int8 > &b, sal_Int32 len)
+{
+ nCRC = rtl_crc32(nCRC, b.getConstArray(), len );
+}
+/** Update CRC32 with specified sequence of bytes
+ */
+void CRC32::update(const Sequence< sal_Int8 > &b)
+{
+ nCRC = rtl_crc32(nCRC, b.getConstArray(),b.getLength());
+}
+
+sal_Int64 CRC32::updateStream( Reference < XInputStream > const & xStream )
+{
+ sal_Int32 nLength;
+ sal_Int64 nTotal = 0;
+ Sequence < sal_Int8 > aSeq ( n_ConstBufferSize );
+ do
+ {
+ nLength = xStream->readBytes ( aSeq, n_ConstBufferSize );
+ updateSegment ( aSeq, nLength );
+ nTotal += nLength;
+ }
+ while ( nLength == n_ConstBufferSize );
+
+ return nTotal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/Deflater.cxx b/package/source/zipapi/Deflater.cxx
new file mode 100644
index 000000000..5c0fd7012
--- /dev/null
+++ b/package/source/zipapi/Deflater.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <package/Deflater.hxx>
+#include <zlib.h>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <osl/diagnose.h>
+#include <string.h>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star;
+using namespace ZipUtils;
+
+/** Provides general purpose compression using the ZLIB compression
+ * library.
+ */
+
+Deflater::~Deflater()
+{
+ end();
+}
+void Deflater::init (sal_Int32 nLevelArg, bool bNowrap)
+{
+ pStream.reset(new z_stream);
+ /* Memset it to 0...sets zalloc/zfree/opaque to NULL */
+ memset (pStream.get(), 0, sizeof(*pStream));
+
+ switch (deflateInit2(pStream.get(), nLevelArg, Z_DEFLATED, bNowrap? -MAX_WBITS : MAX_WBITS,
+ DEF_MEM_LEVEL, DEFAULT_STRATEGY))
+ {
+ case Z_OK:
+ break;
+ case Z_MEM_ERROR:
+ pStream.reset();
+ break;
+ case Z_STREAM_ERROR:
+ pStream.reset();
+ break;
+ default:
+ break;
+ }
+}
+
+Deflater::Deflater(sal_Int32 nSetLevel, bool bNowrap)
+: bFinish(false)
+, bFinished(false)
+, nOffset(0)
+, nLength(0)
+{
+ init(nSetLevel, bNowrap);
+}
+
+sal_Int32 Deflater::doDeflateBytes (uno::Sequence < sal_Int8 > &rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength)
+{
+ sal_Int32 nResult;
+ pStream->next_in = reinterpret_cast<const unsigned char*>( sInBuffer.getConstArray() + nOffset );
+ pStream->next_out = reinterpret_cast<unsigned char*>(rBuffer.getArray())+nNewOffset;
+ pStream->avail_in = nLength;
+ pStream->avail_out = nNewLength;
+
+#if !defined Z_PREFIX
+ nResult = deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
+#else
+ nResult = z_deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
+#endif
+ switch (nResult)
+ {
+ case Z_STREAM_END:
+ bFinished = true;
+ [[fallthrough]];
+ case Z_OK:
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return nNewLength - pStream->avail_out;
+ default:
+ return 0;
+ }
+}
+
+void Deflater::setInputSegment( const uno::Sequence< sal_Int8 >& rBuffer )
+{
+ sInBuffer = rBuffer;
+ nOffset = 0;
+ nLength = rBuffer.getLength();
+}
+
+bool Deflater::needsInput() const
+{
+ return nLength <=0;
+}
+void Deflater::finish( )
+{
+ bFinish = true;
+}
+sal_Int32 Deflater::doDeflateSegment( uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewLength )
+{
+ OSL_ASSERT( !(nNewLength < 0 || nNewLength > rBuffer.getLength()));
+ return doDeflateBytes(rBuffer, /*nNewOffset*/0, nNewLength);
+}
+sal_Int64 Deflater::getTotalIn() const
+{
+ return pStream->total_in; // FIXME64: zlib doesn't look 64bit clean here
+}
+sal_Int64 Deflater::getTotalOut() const
+{
+ return pStream->total_out; // FIXME64: zlib doesn't look 64bit clean here
+}
+void Deflater::reset( )
+{
+#if !defined Z_PREFIX
+ deflateReset(pStream.get());
+#else
+ z_deflateReset(pStream.get());
+#endif
+ bFinish = false;
+ bFinished = false;
+ nOffset = nLength = 0;
+}
+void Deflater::end( )
+{
+ if (pStream)
+ {
+#if !defined Z_PREFIX
+ deflateEnd(pStream.get());
+#else
+ z_deflateEnd(pStream.get());
+#endif
+ pStream.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/Inflater.cxx b/package/source/zipapi/Inflater.cxx
new file mode 100644
index 000000000..d03fed8c0
--- /dev/null
+++ b/package/source/zipapi/Inflater.cxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <package/Inflater.hxx>
+#include <zlib.h>
+#include <string.h>
+
+using namespace com::sun::star::uno;
+using namespace ZipUtils;
+
+/** Provides general purpose decompression using the ZLIB library */
+
+Inflater::Inflater(bool bNoWrap)
+: bFinished(false),
+ bNeedDict(false),
+ nOffset(0),
+ nLength(0),
+ nLastInflateError(0)
+{
+ pStream.reset(new z_stream);
+ /* memset to 0 to set zalloc/opaque etc */
+ memset (pStream.get(), 0, sizeof(*pStream));
+ sal_Int32 nRes;
+ nRes = inflateInit2(pStream.get(), bNoWrap ? -MAX_WBITS : MAX_WBITS);
+ switch (nRes)
+ {
+ case Z_OK:
+ break;
+ case Z_MEM_ERROR:
+ pStream.reset();
+ break;
+ case Z_STREAM_ERROR:
+ pStream.reset();
+ break;
+ default:
+ break;
+ }
+}
+
+Inflater::~Inflater()
+{
+ end();
+}
+
+void Inflater::setInput( const Sequence< sal_Int8 >& rBuffer )
+{
+ sInBuffer = rBuffer;
+ nOffset = 0;
+ nLength = rBuffer.getLength();
+}
+
+
+sal_Int32 Inflater::doInflateSegment( Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength )
+{
+ if (nNewOffset < 0 || nNewLength < 0 || nNewOffset + nNewLength > rBuffer.getLength())
+ {
+ // do error handling
+ }
+ return doInflateBytes(rBuffer, nNewOffset, nNewLength);
+}
+
+void Inflater::end( )
+{
+ if (pStream)
+ {
+#if !defined Z_PREFIX
+ inflateEnd(pStream.get());
+#else
+ z_inflateEnd(pStream.get());
+#endif
+ pStream.reset();
+ }
+}
+
+sal_Int32 Inflater::doInflateBytes (Sequence < sal_Int8 > &rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength)
+{
+ if ( !pStream )
+ {
+ nLastInflateError = Z_STREAM_ERROR;
+ return 0;
+ }
+
+ nLastInflateError = 0;
+
+ pStream->next_in = reinterpret_cast<const unsigned char*>( sInBuffer.getConstArray() + nOffset );
+ pStream->avail_in = nLength;
+ pStream->next_out = reinterpret_cast < unsigned char* > ( rBuffer.getArray() + nNewOffset );
+ pStream->avail_out = nNewLength;
+
+#if !defined Z_PREFIX
+ sal_Int32 nResult = ::inflate(pStream.get(), Z_PARTIAL_FLUSH);
+#else
+ sal_Int32 nResult = ::z_inflate(pStream.get(), Z_PARTIAL_FLUSH);
+#endif
+
+ switch (nResult)
+ {
+ case Z_STREAM_END:
+ bFinished = true;
+ [[fallthrough]];
+ case Z_OK:
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return nNewLength - pStream->avail_out;
+
+ case Z_NEED_DICT:
+ bNeedDict = true;
+ nOffset += nLength - pStream->avail_in;
+ nLength = pStream->avail_in;
+ return 0;
+
+ default:
+ // it is no error, if there is no input or no output
+ if ( nLength && nNewLength )
+ nLastInflateError = nResult;
+ }
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/MemoryByteGrabber.hxx b/package/source/zipapi/MemoryByteGrabber.hxx
new file mode 100644
index 000000000..8dcf7f067
--- /dev/null
+++ b/package/source/zipapi/MemoryByteGrabber.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_MEMORYBYTEGRABBER_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_MEMORYBYTEGRABBER_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+
+class MemoryByteGrabber final
+{
+ const sal_Int8 *mpBuffer;
+ sal_Int32 mnCurrent, mnEnd;
+public:
+ MemoryByteGrabber ( const css::uno::Sequence < sal_Int8 > & rBuffer )
+ : mpBuffer ( rBuffer.getConstArray() )
+ , mnCurrent ( 0 )
+ , mnEnd ( rBuffer.getLength() )
+ {
+ }
+ MemoryByteGrabber(css::uno::Sequence<sal_Int8> &&) = delete;
+
+ const sal_Int8 * getCurrentPos () const { return mpBuffer + mnCurrent; }
+
+ sal_Int32 remainingSize() const { return mnEnd - mnCurrent; }
+
+ // XInputStream chained
+
+ /// @throws css::io::NotConnectedException
+ /// @throws css::io::BufferSizeExceededException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ void skipBytes( sal_Int32 nBytesToSkip )
+ {
+ mnCurrent += nBytesToSkip;
+ }
+
+ // XSeekable chained...
+ sal_Int16 ReadInt16()
+ {
+ if (mnCurrent + 2 > mnEnd )
+ return 0;
+ sal_Int16 nInt16 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt16 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 8;
+ return nInt16;
+ }
+ sal_Int32 ReadInt32()
+ {
+ if (mnCurrent + 4 > mnEnd )
+ return 0;
+
+ sal_Int32 nInt32 = mpBuffer[mnCurrent++] & 0xFF;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 8;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 16;
+ nInt32 |= ( mpBuffer[mnCurrent++] & 0xFF ) << 24;
+ return nInt32;
+ }
+
+ sal_uInt32 ReadUInt32()
+ {
+ if (mnCurrent + 4 > mnEnd )
+ return 0;
+
+ sal_uInt32 nInt32 = mpBuffer [mnCurrent++] & 0xFF;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 8;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 16;
+ nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 24;
+ return nInt32;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ThreadedDeflater.cxx b/package/source/zipapi/ThreadedDeflater.cxx
new file mode 100644
index 000000000..f574105ad
--- /dev/null
+++ b/package/source/zipapi/ThreadedDeflater.cxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ThreadedDeflater.hxx>
+#include <zlib.h>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <sal/log.hxx>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star;
+
+namespace ZipUtils
+{
+const sal_Int64 MaxBlockSize = 128 * 1024;
+
+// Parallel ZLIB compression using threads. The class internally splits the data into
+// blocks and spawns ThreadPool tasks to process them independently. This is achieved
+// in a similar way how pigz works, see comments from Mark Adler at
+// https://stackoverflow.com/questions/30294766/how-to-use-multiple-threads-for-zlib-compression
+// and
+// https://stackoverflow.com/questions/30794053/how-to-use-multiple-threads-for-zlib-compression-same-input-source
+
+// Everything here should be either read-only, or writing to distinct data, or atomic.
+
+class ThreadedDeflater::Task : public comphelper::ThreadTask
+{
+ z_stream stream;
+ ThreadedDeflater* deflater;
+ int sequence;
+ int blockSize;
+ bool firstTask : 1;
+ bool lastTask : 1;
+
+public:
+ Task(ThreadedDeflater* deflater_, int sequence_, int blockSize_, bool firstTask_,
+ bool lastTask_)
+ : comphelper::ThreadTask(deflater_->threadTaskTag)
+ , stream()
+ , deflater(deflater_)
+ , sequence(sequence_)
+ , blockSize(blockSize_)
+ , firstTask(firstTask_)
+ , lastTask(lastTask_)
+ {
+ }
+
+private:
+ virtual void doWork() override;
+};
+
+ThreadedDeflater::ThreadedDeflater(sal_Int32 nSetLevel)
+ : threadTaskTag(comphelper::ThreadPool::createThreadTaskTag())
+ , totalIn(0)
+ , totalOut(0)
+ , zlibLevel(nSetLevel)
+{
+}
+
+ThreadedDeflater::~ThreadedDeflater() COVERITY_NOEXCEPT_FALSE { clear(); }
+
+void ThreadedDeflater::deflateWrite(
+ const css::uno::Reference<css::io::XInputStream>& xInStream,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessInputFunc,
+ std::function<void(const css::uno::Sequence<sal_Int8>&, sal_Int32)> aProcessOutputFunc)
+{
+ sal_Int64 nThreadCount = comphelper::ThreadPool::getSharedOptimalPool().getWorkerCount();
+ sal_Int64 batchSize = MaxBlockSize * nThreadCount;
+ inBuffer.realloc(batchSize);
+ prevDataBlock.realloc(MaxBlockSize);
+ outBuffers.resize(nThreadCount);
+ maProcessOutputFunc = aProcessOutputFunc;
+ bool firstTask = true;
+
+ while (xInStream->available() > 0)
+ {
+ sal_Int64 inputBytes = xInStream->readBytes(inBuffer, batchSize);
+ aProcessInputFunc(inBuffer, inputBytes);
+ totalIn += inputBytes;
+ int sequence = 0;
+ bool lastBatch = xInStream->available() <= 0;
+ sal_Int64 bytesPending = inputBytes;
+ while (bytesPending > 0)
+ {
+ sal_Int64 taskSize = std::min(MaxBlockSize, bytesPending);
+ bytesPending -= taskSize;
+ bool lastTask = lastBatch && !bytesPending;
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(
+ std::make_unique<Task>(this, sequence++, taskSize, firstTask, lastTask));
+
+ if (firstTask)
+ firstTask = false;
+ }
+
+ assert(bytesPending == 0);
+
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(threadTaskTag);
+
+ if (!lastBatch)
+ {
+ assert(inputBytes == batchSize);
+ std::copy_n(std::cbegin(inBuffer) + (batchSize - MaxBlockSize), MaxBlockSize,
+ prevDataBlock.getArray());
+ }
+
+ processDeflatedBuffers();
+ }
+}
+
+void ThreadedDeflater::processDeflatedBuffers()
+{
+ sal_Int64 batchOutputSize = 0;
+ for (const auto& buffer : outBuffers)
+ batchOutputSize += buffer.size();
+
+ css::uno::Sequence<sal_Int8> outBuffer(batchOutputSize);
+
+ auto pos = outBuffer.getArray();
+ for (auto& buffer : outBuffers)
+ {
+ pos = std::copy(buffer.begin(), buffer.end(), pos);
+ buffer.clear();
+ }
+
+ maProcessOutputFunc(outBuffer, batchOutputSize);
+ totalOut += batchOutputSize;
+}
+
+void ThreadedDeflater::clear()
+{
+ inBuffer = uno::Sequence<sal_Int8>();
+ outBuffers.clear();
+}
+
+#if defined Z_PREFIX
+#define deflateInit2 z_deflateInit2
+#define deflateBound z_deflateBound
+#define deflateSetDictionary z_deflateSetDictionary
+#define deflate z_deflate
+#define deflateEnd z_deflateEnd
+#endif
+
+void ThreadedDeflater::Task::doWork()
+{
+ stream.zalloc = nullptr;
+ stream.zfree = nullptr;
+ stream.opaque = nullptr;
+ // -MAX_WBITS means 32k window size and raw stream
+ if (deflateInit2(&stream, deflater->zlibLevel, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY)
+ != Z_OK)
+ {
+ SAL_WARN("package.threadeddeflate", "deflateInit2() failed");
+ abort();
+ }
+ // Find out size for our output buffer to be large enough for deflate() needing to be called just once.
+ sal_Int64 outputMaxSize = deflateBound(&stream, blockSize);
+ // add extra size for Z_SYNC_FLUSH
+ outputMaxSize += 20;
+ deflater->outBuffers[sequence].resize(outputMaxSize);
+ sal_Int64 myInBufferStart = sequence * MaxBlockSize;
+ // zlib doesn't handle const properly
+ unsigned char* inBufferPtr = reinterpret_cast<unsigned char*>(
+ const_cast<signed char*>(deflater->inBuffer.getConstArray()));
+ if (!firstTask)
+ {
+ // the window size is 32k, so set last 32k of previous data as the dictionary
+ assert(MAX_WBITS == 15);
+ assert(MaxBlockSize >= 32768);
+ if (sequence > 0)
+ {
+ deflateSetDictionary(&stream, inBufferPtr + myInBufferStart - 32768, 32768);
+ }
+ else
+ {
+ unsigned char* prevBufferPtr = reinterpret_cast<unsigned char*>(
+ const_cast<signed char*>(deflater->prevDataBlock.getConstArray()));
+ deflateSetDictionary(&stream, prevBufferPtr + MaxBlockSize - 32768, 32768);
+ }
+ }
+ stream.next_in = inBufferPtr + myInBufferStart;
+ stream.avail_in = blockSize;
+ stream.next_out = reinterpret_cast<unsigned char*>(deflater->outBuffers[sequence].data());
+ stream.avail_out = outputMaxSize;
+
+ // The trick is in using Z_SYNC_FLUSH instead of Z_NO_FLUSH. It will align the data at a byte boundary,
+ // and since we use a raw stream, the data blocks then can be simply concatenated.
+ int res = deflate(&stream, lastTask ? Z_FINISH : Z_SYNC_FLUSH);
+ assert(stream.avail_in == 0); // Check that everything has been deflated.
+ if (lastTask ? res == Z_STREAM_END : res == Z_OK)
+ { // ok
+ sal_Int64 outSize = outputMaxSize - stream.avail_out;
+ deflater->outBuffers[sequence].resize(outSize);
+ }
+ else
+ {
+ SAL_WARN("package.threadeddeflate", "deflate() failed");
+ abort();
+ }
+ deflateEnd(&stream);
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XBufferedThreadedStream.cxx b/package/source/zipapi/XBufferedThreadedStream.cxx
new file mode 100644
index 000000000..d3bf995d9
--- /dev/null
+++ b/package/source/zipapi/XBufferedThreadedStream.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "XBufferedThreadedStream.hxx"
+
+using namespace css::uno;
+
+namespace {
+
+class UnzippingThread: public salhelper::Thread
+{
+ XBufferedThreadedStream &mxStream;
+public:
+ explicit UnzippingThread(XBufferedThreadedStream &xStream): Thread("Unzipping"), mxStream(xStream) {}
+private:
+ virtual void execute() override
+ {
+ try
+ {
+ mxStream.produce();
+ }
+ catch (...)
+ {
+ mxStream.saveException(std::current_exception());
+ }
+
+ mxStream.setTerminateThread();
+ }
+};
+
+}
+
+XBufferedThreadedStream::XBufferedThreadedStream(
+ const Reference<XInputStream>& xSrcStream,
+ sal_Int64 nStreamSize)
+: mxSrcStream( xSrcStream )
+, mnPos(0)
+, mnStreamSize( nStreamSize )
+, mnOffset( 0 )
+, mxUnzippingThread( new UnzippingThread(*this) )
+, mbTerminateThread( false )
+{
+ mxUnzippingThread->launch();
+}
+
+XBufferedThreadedStream::~XBufferedThreadedStream()
+{
+ setTerminateThread();
+ mxUnzippingThread->join();
+}
+
+/**
+ * Reads from UnbufferedStream in a separate thread and stores the buffer blocks
+ * in maPendingBuffers queue for further use.
+ */
+void XBufferedThreadedStream::produce()
+{
+ Buffer pProducedBuffer;
+ sal_Int64 nTotalBytesRead(0);
+ std::unique_lock<std::mutex> aGuard( maBufferProtector );
+ do
+ {
+ if( !maUsedBuffers.empty() )
+ {
+ pProducedBuffer = maUsedBuffers.front();
+ maUsedBuffers.pop();
+ }
+
+ aGuard.unlock();
+ nTotalBytesRead += mxSrcStream->readBytes( pProducedBuffer, nBufferSize );
+
+ aGuard.lock();
+ maPendingBuffers.push( pProducedBuffer );
+ maBufferConsumeResume.notify_one();
+
+ if (!mbTerminateThread)
+ maBufferProduceResume.wait( aGuard, [&]{return canProduce(); } );
+
+ } while( !mbTerminateThread && nTotalBytesRead < mnStreamSize );
+}
+
+/**
+ * Fetches next available block from maPendingBuffers for use in Reading thread.
+ */
+const Buffer& XBufferedThreadedStream::getNextBlock()
+{
+ std::unique_lock<std::mutex> aGuard( maBufferProtector );
+ const sal_Int32 nBufSize = maInUseBuffer.getLength();
+ if( nBufSize <= 0 || mnOffset >= nBufSize )
+ {
+ if( mnOffset >= nBufSize )
+ maUsedBuffers.push( maInUseBuffer );
+
+ maBufferConsumeResume.wait( aGuard, [&]{return canConsume(); } );
+
+ if( maPendingBuffers.empty() )
+ {
+ maInUseBuffer = Buffer();
+ if (maSavedException)
+ std::rethrow_exception(maSavedException);
+ }
+ else
+ {
+ maInUseBuffer = maPendingBuffers.front();
+ maPendingBuffers.pop();
+ mnOffset = 0;
+
+ if( maPendingBuffers.size() <= nBufferLowWater )
+ maBufferProduceResume.notify_one();
+ }
+ }
+
+ return maInUseBuffer;
+}
+
+void XBufferedThreadedStream::setTerminateThread()
+{
+ std::scoped_lock<std::mutex> aGuard( maBufferProtector );
+ mbTerminateThread = true;
+ maBufferProduceResume.notify_one();
+ maBufferConsumeResume.notify_one();
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::readBytes( Sequence< sal_Int8 >& rData, sal_Int32 nBytesToRead )
+{
+ if( !hasBytes() )
+ return 0;
+
+ const sal_Int32 nAvailableSize = static_cast< sal_Int32 > ( std::min< sal_Int64 >( nBytesToRead, remainingSize() ) );
+ rData.realloc( nAvailableSize );
+ auto pData = rData.getArray();
+ sal_Int32 i = 0, nPendingBytes = nAvailableSize;
+
+ while( nPendingBytes )
+ {
+ const Buffer &pBuffer = getNextBlock();
+ if( !pBuffer.hasElements() )
+ {
+ rData.realloc( nAvailableSize - nPendingBytes );
+ return nAvailableSize - nPendingBytes;
+ }
+ const sal_Int32 limit = std::min<sal_Int32>( nPendingBytes, pBuffer.getLength() - mnOffset );
+
+ memcpy( &pData[i], &pBuffer[mnOffset], limit );
+
+ nPendingBytes -= limit;
+ mnOffset += limit;
+ mnPos += limit;
+ i += limit;
+ }
+
+ return nAvailableSize;
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes( aData, nMaxBytesToRead );
+}
+void SAL_CALL XBufferedThreadedStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if( nBytesToSkip )
+ {
+ Sequence < sal_Int8 > aSequence( nBytesToSkip );
+ readBytes( aSequence, nBytesToSkip );
+ }
+}
+
+sal_Int32 SAL_CALL XBufferedThreadedStream::available()
+{
+ if( !hasBytes() )
+ return 0;
+
+ return static_cast< sal_Int32 > ( std::min< sal_Int64 >( SAL_MAX_INT32, remainingSize() ) );
+}
+
+void SAL_CALL XBufferedThreadedStream::closeInput()
+{
+ setTerminateThread();
+ mxUnzippingThread->join();
+ mxSrcStream->closeInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XBufferedThreadedStream.hxx b/package/source/zipapi/XBufferedThreadedStream.hxx
new file mode 100644
index 000000000..ad5d3b0ce
--- /dev/null
+++ b/package/source/zipapi/XBufferedThreadedStream.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_XBUFFEREDTHREADEDSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_XBUFFEREDTHREADEDSTREAM_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+
+#include <queue>
+#include <mutex>
+#include <condition_variable>
+
+typedef css::uno::Sequence< sal_Int8 > Buffer;
+
+class XBufferedThreadedStream : public cppu::WeakImplHelper< css::io::XInputStream >
+{
+private:
+ const css::uno::Reference<XInputStream> mxSrcStream;
+ sal_Int64 mnPos; /// position in stream
+ sal_Int64 mnStreamSize; /// available size of stream
+
+ Buffer maInUseBuffer; /// Buffer block in use
+ int mnOffset; /// position in maInUseBuffer
+ std::queue < Buffer > maPendingBuffers; /// Buffers that are available for use
+ std::queue < Buffer > maUsedBuffers;
+
+ rtl::Reference< salhelper::Thread > mxUnzippingThread;
+ std::mutex maBufferProtector; /// mutex protecting Buffer queues.
+ std::condition_variable maBufferConsumeResume;
+ std::condition_variable maBufferProduceResume;
+ bool mbTerminateThread; /// indicates the failure of one of the threads
+
+ std::exception_ptr maSavedException; /// exception caught during unzipping is saved to be thrown during reading
+
+ static const size_t nBufferLowWater = 2;
+ static const size_t nBufferHighWater = 4;
+ static const size_t nBufferSize = 32 * 1024;
+
+ const Buffer& getNextBlock();
+ sal_Int64 remainingSize() const { return mnStreamSize - mnPos; }
+ bool hasBytes() const { return mnPos < mnStreamSize; }
+
+ bool canProduce() const
+ {
+ return( mbTerminateThread || maPendingBuffers.size() < nBufferHighWater );
+ }
+
+ bool canConsume() const
+ {
+ return( mbTerminateThread || !maPendingBuffers.empty() );
+ }
+
+public:
+ XBufferedThreadedStream(
+ const css::uno::Reference<XInputStream>& xSrcStream,
+ sal_Int64 nStreamSize /* cf. sal_Int32 available(); */ );
+
+ virtual ~XBufferedThreadedStream() override;
+
+ void produce();
+ void setTerminateThread();
+ void saveException(const std::exception_ptr& exception) { maSavedException = exception; }
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XUnbufferedStream.cxx b/package/source/zipapi/XUnbufferedStream.cxx
new file mode 100644
index 000000000..56f864a2d
--- /dev/null
+++ b/package/source/zipapi/XUnbufferedStream.cxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+
+#include "XUnbufferedStream.hxx"
+#include <EncryptionData.hxx>
+#include <ZipFile.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <algorithm>
+#include <string.h>
+
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using com::sun::star::packages::zip::ZipIOException;
+
+XUnbufferedStream::XUnbufferedStream(
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ ZipEntry const & rEntry,
+ Reference < XInputStream > const & xNewZipStream,
+ const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const OUString& aMediaType,
+ bool bRecoveryMode )
+: maMutexHolder( aMutexHolder )
+, mxZipStream ( xNewZipStream )
+, mxZipSeek ( xNewZipStream, UNO_QUERY )
+, maEntry ( rEntry )
+, mnBlockSize( 1 )
+, maInflater ( true )
+, mbRawStream ( nStreamMode == UNBUFF_STREAM_RAW || nStreamMode == UNBUFF_STREAM_WRAPPEDRAW )
+, mbWrappedRaw ( nStreamMode == UNBUFF_STREAM_WRAPPEDRAW )
+, mnHeaderToRead ( 0 )
+, mnZipCurrent ( 0 )
+, mnZipEnd ( 0 )
+, mnZipSize ( 0 )
+, mnMyCurrent ( 0 )
+, mbCheckCRC(!bRecoveryMode)
+{
+ mnZipCurrent = maEntry.nOffset;
+ if ( mbRawStream )
+ {
+ mnZipSize = maEntry.nMethod == DEFLATED ? maEntry.nCompressedSize : maEntry.nSize;
+ mnZipEnd = maEntry.nOffset + mnZipSize;
+ }
+ else
+ {
+ mnZipSize = maEntry.nSize;
+ mnZipEnd = maEntry.nMethod == DEFLATED ? maEntry.nOffset + maEntry.nCompressedSize : maEntry.nOffset + maEntry.nSize;
+ }
+
+ if (mnZipSize < 0)
+ throw ZipIOException("The stream seems to be broken!");
+
+ bool bHaveEncryptData = rData.is() && rData->m_aInitVector.hasElements() &&
+ ((rData->m_aSalt.hasElements() && rData->m_nIterationCount != 0)
+ ||
+ rData->m_aKey.hasElements());
+ bool bMustDecrypt = nStreamMode == UNBUFF_STREAM_DATA && bHaveEncryptData && bIsEncrypted;
+
+ if ( bMustDecrypt )
+ {
+ m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
+ mnBlockSize = ( rData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 1 );
+ }
+
+ if ( !(bHaveEncryptData && mbWrappedRaw && bIsEncrypted) )
+ return;
+
+ // if we have the data needed to decrypt it, but didn't want it decrypted (or
+ // we couldn't decrypt it due to wrong password), then we prepend this
+ // data to the stream
+
+ // Make a buffer big enough to hold both the header and the data itself
+ maHeader.realloc ( n_ConstHeaderSize +
+ rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() +
+ rData->m_aDigest.getLength() +
+ aMediaType.getLength() * sizeof( sal_Unicode ) );
+ sal_Int8 * pHeader = maHeader.getArray();
+ ZipFile::StaticFillHeader( rData, rEntry.nSize, aMediaType, pHeader );
+ mnHeaderToRead = static_cast < sal_Int16 > ( maHeader.getLength() );
+ mnZipSize += mnHeaderToRead;
+}
+
+// allows to read package raw stream
+XUnbufferedStream::XUnbufferedStream(
+ const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ const Reference < XInputStream >& xRawStream,
+ const ::rtl::Reference< EncryptionData >& rData )
+: maMutexHolder( aMutexHolder )
+, mxZipStream ( xRawStream )
+, mxZipSeek ( xRawStream, UNO_QUERY )
+, mnBlockSize( 1 )
+, maInflater ( true )
+, mbRawStream ( false )
+, mbWrappedRaw ( false )
+, mnHeaderToRead ( 0 )
+, mnZipCurrent ( 0 )
+, mnZipEnd ( 0 )
+, mnZipSize ( 0 )
+, mnMyCurrent ( 0 )
+, mbCheckCRC( false )
+{
+ // for this scenario maEntry is not set !!!
+ OSL_ENSURE( mxZipSeek.is(), "The stream must be seekable!" );
+
+ // skip raw header, it must be already parsed to rData
+ mnZipCurrent = n_ConstHeaderSize + rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() + rData->m_aDigest.getLength();
+
+ try {
+ if ( mxZipSeek.is() )
+ mnZipSize = mxZipSeek->getLength();
+ } catch( const Exception& )
+ {
+ // in case of problem the size will stay set to 0
+ TOOLS_WARN_EXCEPTION("package", "ignoring");
+ }
+
+ mnZipEnd = mnZipCurrent + mnZipSize;
+
+ // the raw data will not be decrypted, no need for the cipher
+ // m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
+}
+
+XUnbufferedStream::~XUnbufferedStream()
+{
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ ::osl::MutexGuard aGuard( maMutexHolder->GetMutex() );
+
+ sal_Int32 nRequestedBytes = nBytesToRead;
+ OSL_ENSURE( !mnHeaderToRead || mbWrappedRaw, "Only encrypted raw stream can be provided with header!" );
+ if ( mnMyCurrent + nRequestedBytes > mnZipSize + maHeader.getLength() )
+ nRequestedBytes = static_cast < sal_Int32 > ( mnZipSize + maHeader.getLength() - mnMyCurrent );
+
+ sal_Int32 nTotal = 0;
+ aData.realloc ( nRequestedBytes );
+ if ( nRequestedBytes )
+ {
+ sal_Int32 nRead = 0;
+ sal_Int32 nLastRead = 0;
+ if ( mbRawStream )
+ {
+ sal_Int64 nDiff = mnZipEnd - mnZipCurrent;
+
+ if ( mbWrappedRaw && mnHeaderToRead )
+ {
+ sal_Int16 nHeadRead = static_cast< sal_Int16 >(( nRequestedBytes > mnHeaderToRead ?
+ mnHeaderToRead : nRequestedBytes ));
+ memcpy ( aData.getArray(), maHeader.getConstArray() + maHeader.getLength() - mnHeaderToRead, nHeadRead );
+ mnHeaderToRead = mnHeaderToRead - nHeadRead;
+
+ if ( nHeadRead < nRequestedBytes )
+ {
+ sal_Int32 nToRead = nRequestedBytes - nHeadRead;
+ nToRead = ( nDiff < nToRead ) ? sal::static_int_cast< sal_Int32 >( nDiff ) : nToRead;
+
+ Sequence< sal_Int8 > aPureData( nToRead );
+ mxZipSeek->seek ( mnZipCurrent );
+ nRead = mxZipStream->readBytes ( aPureData, nToRead );
+ mnZipCurrent += nRead;
+
+ aPureData.realloc( nRead );
+ if ( mbCheckCRC )
+ maCRC.update( aPureData );
+
+ aData.realloc( nHeadRead + nRead );
+
+ const sal_Int8* pPureBuffer = aPureData.getConstArray();
+ sal_Int8* pBuffer = aData.getArray();
+ for ( sal_Int32 nInd = 0; nInd < nRead; nInd++ )
+ pBuffer[ nHeadRead + nInd ] = pPureBuffer[ nInd ];
+ }
+
+ nRead += nHeadRead;
+ }
+ else
+ {
+ mxZipSeek->seek ( mnZipCurrent );
+
+ nRead = mxZipStream->readBytes (
+ aData,
+ std::min<sal_Int64>(nDiff, nRequestedBytes) );
+
+ mnZipCurrent += nRead;
+
+ aData.realloc( nRead );
+ if ( mbWrappedRaw && mbCheckCRC )
+ maCRC.update( aData );
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ nLastRead = maInflater.doInflateSegment( aData, nRead, aData.getLength() - nRead );
+ if ( 0 != nLastRead && ( nRead + nLastRead == nRequestedBytes || mnZipCurrent >= mnZipEnd ) )
+ break;
+ nRead += nLastRead;
+ if ( nRead > nRequestedBytes )
+ throw RuntimeException(
+ "Should not be possible to read more than requested!" );
+
+ if ( maInflater.finished() || maInflater.getLastInflateError() )
+ throw ZipIOException("The stream seems to be broken!" );
+
+ if ( maInflater.needsDictionary() )
+ throw ZipIOException("Dictionaries are not supported!" );
+
+ sal_Int32 nDiff = static_cast< sal_Int32 >( mnZipEnd - mnZipCurrent );
+ if ( nDiff <= 0 )
+ {
+ throw ZipIOException("The stream seems to be broken!" );
+ }
+
+ mxZipSeek->seek ( mnZipCurrent );
+
+ sal_Int32 nToRead = std::max( nRequestedBytes, static_cast< sal_Int32 >( 8192 ) );
+ if ( mnBlockSize > 1 )
+ nToRead = nToRead + mnBlockSize - nToRead % mnBlockSize;
+ nToRead = std::min( nDiff, nToRead );
+
+ sal_Int32 nZipRead = mxZipStream->readBytes( maCompBuffer, nToRead );
+ if ( nZipRead < nToRead )
+ throw ZipIOException("No expected data!" );
+
+ mnZipCurrent += nZipRead;
+ // maCompBuffer now has the data, check if we need to decrypt
+ // before passing to the Inflater
+ if ( m_xCipherContext.is() )
+ {
+ if ( mbCheckCRC )
+ maCRC.update( maCompBuffer );
+
+ maCompBuffer = m_xCipherContext->convertWithCipherContext( maCompBuffer );
+ if ( mnZipCurrent == mnZipEnd )
+ {
+ uno::Sequence< sal_Int8 > aSuffix = m_xCipherContext->finalizeCipherContextAndDispose();
+ if ( aSuffix.hasElements() )
+ {
+ sal_Int32 nOldLen = maCompBuffer.getLength();
+ maCompBuffer.realloc( nOldLen + aSuffix.getLength() );
+ memcpy( maCompBuffer.getArray() + nOldLen, aSuffix.getConstArray(), aSuffix.getLength() );
+ }
+ }
+ }
+ maInflater.setInput ( maCompBuffer );
+
+ }
+ }
+
+ mnMyCurrent += nRead + nLastRead;
+ nTotal = nRead + nLastRead;
+ if ( nTotal < nRequestedBytes)
+ aData.realloc ( nTotal );
+
+ if ( mbCheckCRC && ( !mbRawStream || mbWrappedRaw ) )
+ {
+ if ( !m_xCipherContext.is() && !mbWrappedRaw )
+ maCRC.update( aData );
+
+ if ( mnZipSize + maHeader.getLength() == mnMyCurrent && maCRC.getValue() != maEntry.nCrc )
+ throw ZipIOException("The stream seems to be broken!" );
+ }
+ }
+
+ return nTotal;
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes ( aData, nMaxBytesToRead );
+}
+void SAL_CALL XUnbufferedStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if ( nBytesToSkip )
+ {
+ Sequence < sal_Int8 > aSequence ( nBytesToSkip );
+ readBytes ( aSequence, nBytesToSkip );
+ }
+}
+
+sal_Int32 SAL_CALL XUnbufferedStream::available( )
+{
+ //available size must include the prepended header in case of wrapped raw stream
+ return static_cast< sal_Int32 > ( std::min< sal_Int64 >( SAL_MAX_INT32, (mnZipSize + mnHeaderToRead - mnMyCurrent) ) );
+}
+
+void SAL_CALL XUnbufferedStream::closeInput( )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/XUnbufferedStream.hxx b/package/source/zipapi/XUnbufferedStream.hxx
new file mode 100644
index 000000000..277f356ee
--- /dev/null
+++ b/package/source/zipapi/XUnbufferedStream.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_XUNBUFFEREDSTREAM_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_XUNBUFFEREDSTREAM_HXX
+
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <comphelper/refcountedmutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <package/Inflater.hxx>
+#include <ZipEntry.hxx>
+#include <CRC32.hxx>
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+#define UNBUFF_STREAM_DATA 0
+#define UNBUFF_STREAM_RAW 1
+#define UNBUFF_STREAM_WRAPPEDRAW 2
+
+class EncryptionData;
+class XUnbufferedStream final : public cppu::WeakImplHelper
+<
+ css::io::XInputStream
+>
+{
+ rtl::Reference<comphelper::RefCountedMutex> maMutexHolder;
+
+ css::uno::Reference < css::io::XInputStream > mxZipStream;
+ css::uno::Reference < css::io::XSeekable > mxZipSeek;
+ css::uno::Sequence < sal_Int8 > maCompBuffer, maHeader;
+ ZipEntry maEntry;
+ sal_Int32 mnBlockSize;
+ css::uno::Reference< css::xml::crypto::XCipherContext > m_xCipherContext;
+ ZipUtils::Inflater maInflater;
+ bool mbRawStream, mbWrappedRaw;
+ sal_Int16 mnHeaderToRead;
+ sal_Int64 mnZipCurrent, mnZipEnd, mnZipSize, mnMyCurrent;
+ CRC32 maCRC;
+ bool mbCheckCRC;
+
+public:
+ XUnbufferedStream(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ ZipEntry const & rEntry,
+ css::uno::Reference < css::io::XInputStream > const & xNewZipStream,
+ const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const OUString& aMediaType,
+ bool bRecoveryMode );
+
+ // allows to read package raw stream
+ XUnbufferedStream(
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ const css::uno::Reference < css::io::XInputStream >& xRawStream,
+ const ::rtl::Reference< EncryptionData >& rData );
+
+ sal_Int64 getSize() const { return mnZipSize; }
+
+ virtual ~XUnbufferedStream() override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipEnumeration.cxx b/package/source/zipapi/ZipEnumeration.cxx
new file mode 100644
index 000000000..0c036882f
--- /dev/null
+++ b/package/source/zipapi/ZipEnumeration.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipEnumeration.hxx>
+
+/** Provides an Enumeration over the contents of a Zip file */
+
+ZipEnumeration::ZipEnumeration(EntryHash& rNewEntryHash)
+ : rEntryHash(rNewEntryHash)
+ , aIterator(rEntryHash.begin())
+{
+}
+bool ZipEnumeration::hasMoreElements() { return (aIterator != rEntryHash.end()); }
+
+const ZipEntry* ZipEnumeration::nextElement()
+{
+ if (aIterator != rEntryHash.end())
+ return &((*aIterator++).second);
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx
new file mode 100644
index 000000000..79dc5bb68
--- /dev/null
+++ b/package/source/zipapi/ZipFile.cxx
@@ -0,0 +1,1263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <rtl/digest.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include "blowfishcontext.hxx"
+#include "sha1context.hxx"
+#include <ZipFile.hxx>
+#include <ZipEnumeration.hxx>
+#include "XUnbufferedStream.hxx"
+#include "XBufferedThreadedStream.hxx"
+#include <PackageConstants.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <EncryptionData.hxx>
+#include "MemoryByteGrabber.hxx"
+
+#include <CRC32.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+using ZipUtils::Inflater;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+/** This class is used to read entries from a zip file
+ */
+ZipFile::ZipFile( const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ uno::Reference < XInputStream > const &xInput,
+ const uno::Reference < XComponentContext > & rxContext,
+ bool bInitialise )
+: m_aMutexHolder( aMutexHolder )
+, aGrabber( xInput )
+, aInflater( true )
+, xStream(xInput)
+, m_xContext ( rxContext )
+, bRecoveryMode( false )
+{
+ if (bInitialise && readCEN() == -1 )
+ {
+ aEntries.clear();
+ throw ZipException( "stream data looks to be broken" );
+ }
+}
+
+ZipFile::ZipFile( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ uno::Reference < XInputStream > const &xInput,
+ const uno::Reference < XComponentContext > & rxContext,
+ bool bInitialise, bool bForceRecovery)
+: m_aMutexHolder( aMutexHolder )
+, aGrabber( xInput )
+, aInflater( true )
+, xStream(xInput)
+, m_xContext ( rxContext )
+, bRecoveryMode( bForceRecovery )
+{
+ if (bInitialise)
+ {
+ if ( bForceRecovery )
+ {
+ recover();
+ }
+ else if ( readCEN() == -1 )
+ {
+ aEntries.clear();
+ throw ZipException("stream data looks to be broken" );
+ }
+ }
+}
+
+ZipFile::~ZipFile()
+{
+ aEntries.clear();
+}
+
+void ZipFile::setInputStream ( const uno::Reference < XInputStream >& xNewStream )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ xStream = xNewStream;
+ aGrabber.setInputStream ( xStream );
+}
+
+uno::Reference< xml::crypto::XDigestContext > ZipFile::StaticGetDigestContextForChecksum( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData )
+{
+ uno::Reference< xml::crypto::XDigestContext > xDigestContext;
+ if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA256_1K )
+ {
+ uno::Reference< uno::XComponentContext > xContext = xArgContext;
+ if ( !xContext.is() )
+ xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference< xml::crypto::XNSSInitializer > xDigestContextSupplier = xml::crypto::NSSInitializer::create( xContext );
+
+ xDigestContext.set( xDigestContextSupplier->getDigestContext( xEncryptionData->m_nCheckAlg, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
+ }
+ else if ( xEncryptionData->m_nCheckAlg == xml::crypto::DigestID::SHA1_1K )
+ {
+ if (xEncryptionData->m_bTryWrongSHA1)
+ {
+ xDigestContext.set(StarOfficeSHA1DigestContext::Create(), uno::UNO_SET_THROW);
+ }
+ else
+ {
+ xDigestContext.set(CorrectSHA1DigestContext::Create(), uno::UNO_SET_THROW);
+ }
+ }
+
+ return xDigestContext;
+}
+
+uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const uno::Reference< uno::XComponentContext >& xArgContext, const ::rtl::Reference< EncryptionData >& xEncryptionData, bool bEncrypt )
+{
+ uno::Reference< xml::crypto::XCipherContext > xResult;
+
+ if (xEncryptionData->m_nDerivedKeySize < 0)
+ {
+ throw ZipIOException("Invalid derived key length!" );
+ }
+
+ uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
+ if ( !xEncryptionData->m_nIterationCount &&
+ xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength() )
+ {
+ // gpg4libre: no need to derive key, m_aKey is already
+ // usable as symmetric session key
+ aDerivedKey = xEncryptionData->m_aKey;
+ }
+ else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
+ aDerivedKey.getLength(),
+ reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
+ xEncryptionData->m_aKey.getLength(),
+ reinterpret_cast< const sal_uInt8 * > ( xEncryptionData->m_aSalt.getConstArray() ),
+ xEncryptionData->m_aSalt.getLength(),
+ xEncryptionData->m_nIterationCount ) )
+ {
+ throw ZipIOException("Can not create derived key!" );
+ }
+
+ if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
+ {
+ uno::Reference< uno::XComponentContext > xContext = xArgContext;
+ if ( !xContext.is() )
+ xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference< xml::crypto::XNSSInitializer > xCipherContextSupplier = xml::crypto::NSSInitializer::create( xContext );
+
+ xResult = xCipherContextSupplier->getCipherContext( xEncryptionData->m_nEncAlg, aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt, uno::Sequence< beans::NamedValue >() );
+ }
+ else if ( xEncryptionData->m_nEncAlg == xml::crypto::CipherID::BLOWFISH_CFB_8 )
+ {
+ xResult = BlowfishCFB8CipherContext::Create( aDerivedKey, xEncryptionData->m_aInitVector, bEncrypt );
+ }
+ else
+ {
+ throw ZipIOException("Unknown cipher algorithm is requested!" );
+ }
+
+ return xResult;
+}
+
+void ZipFile::StaticFillHeader( const ::rtl::Reference< EncryptionData >& rData,
+ sal_Int64 nSize,
+ const OUString& aMediaType,
+ sal_Int8 * & pHeader )
+{
+ // I think it's safe to restrict vector and salt length to 2 bytes !
+ sal_Int16 nIVLength = static_cast < sal_Int16 > ( rData->m_aInitVector.getLength() );
+ sal_Int16 nSaltLength = static_cast < sal_Int16 > ( rData->m_aSalt.getLength() );
+ sal_Int16 nDigestLength = static_cast < sal_Int16 > ( rData->m_aDigest.getLength() );
+ sal_Int16 nMediaTypeLength = static_cast < sal_Int16 > ( aMediaType.getLength() * sizeof( sal_Unicode ) );
+
+ // First the header
+ *(pHeader++) = ( n_ConstHeader >> 0 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 8 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 16 ) & 0xFF;
+ *(pHeader++) = ( n_ConstHeader >> 24 ) & 0xFF;
+
+ // Then the version
+ *(pHeader++) = ( n_ConstCurrentVersion >> 0 ) & 0xFF;
+ *(pHeader++) = ( n_ConstCurrentVersion >> 8 ) & 0xFF;
+
+ // Then the iteration Count
+ sal_Int32 nIterationCount = rData->m_nIterationCount;
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIterationCount >> 24 ) & 0xFF);
+
+ // FIXME64: need to handle larger sizes
+ // Then the size:
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSize >> 24 ) & 0xFF);
+
+ // Then the encryption algorithm
+ sal_Int32 nEncAlgID = rData->m_nEncAlg;
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nEncAlgID >> 24 ) & 0xFF);
+
+ // Then the checksum algorithm
+ sal_Int32 nChecksumAlgID = rData->m_nCheckAlg;
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nChecksumAlgID >> 24 ) & 0xFF);
+
+ // Then the derived key size
+ sal_Int32 nDerivedKeySize = rData->m_nDerivedKeySize;
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDerivedKeySize >> 24 ) & 0xFF);
+
+ // Then the start key generation algorithm
+ sal_Int32 nKeyAlgID = rData->m_nStartKeyGenID;
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 8 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 16 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nKeyAlgID >> 24 ) & 0xFF);
+
+ // Then the salt length
+ *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nSaltLength >> 8 ) & 0xFF);
+
+ // Then the IV length
+ *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nIVLength >> 8 ) & 0xFF);
+
+ // Then the digest length
+ *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nDigestLength >> 8 ) & 0xFF);
+
+ // Then the mediatype length
+ *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 0 ) & 0xFF);
+ *(pHeader++) = static_cast< sal_Int8 >(( nMediaTypeLength >> 8 ) & 0xFF);
+
+ // Then the salt content
+ memcpy ( pHeader, rData->m_aSalt.getConstArray(), nSaltLength );
+ pHeader += nSaltLength;
+
+ // Then the IV content
+ memcpy ( pHeader, rData->m_aInitVector.getConstArray(), nIVLength );
+ pHeader += nIVLength;
+
+ // Then the digest content
+ memcpy ( pHeader, rData->m_aDigest.getConstArray(), nDigestLength );
+ pHeader += nDigestLength;
+
+ // Then the mediatype itself
+ memcpy ( pHeader, aMediaType.getStr(), nMediaTypeLength );
+ pHeader += nMediaTypeLength;
+}
+
+bool ZipFile::StaticFillData ( ::rtl::Reference< BaseEncryptionData > const & rData,
+ sal_Int32 &rEncAlg,
+ sal_Int32 &rChecksumAlg,
+ sal_Int32 &rDerivedKeySize,
+ sal_Int32 &rStartKeyGenID,
+ sal_Int32 &rSize,
+ OUString& aMediaType,
+ const uno::Reference< XInputStream >& rStream )
+{
+ bool bOk = false;
+ const sal_Int32 nHeaderSize = n_ConstHeaderSize - 4;
+ Sequence < sal_Int8 > aBuffer ( nHeaderSize );
+ if ( nHeaderSize == rStream->readBytes ( aBuffer, nHeaderSize ) )
+ {
+ sal_Int16 nPos = 0;
+ sal_Int8 *pBuffer = aBuffer.getArray();
+ sal_Int16 nVersion = pBuffer[nPos++] & 0xFF;
+ nVersion |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ if ( nVersion == n_ConstCurrentVersion )
+ {
+ sal_Int32 nCount = pBuffer[nPos++] & 0xFF;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ nCount |= ( pBuffer[nPos++] & 0xFF ) << 24;
+ rData->m_nIterationCount = nCount;
+
+ rSize = pBuffer[nPos++] & 0xFF;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rSize |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rEncAlg = pBuffer[nPos++] & 0xFF;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rEncAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rChecksumAlg = pBuffer[nPos++] & 0xFF;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rChecksumAlg |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rDerivedKeySize = pBuffer[nPos++] & 0xFF;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rDerivedKeySize |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ rStartKeyGenID = pBuffer[nPos++] & 0xFF;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 16;
+ rStartKeyGenID |= ( pBuffer[nPos++] & 0xFF ) << 24;
+
+ sal_Int16 nSaltLength = pBuffer[nPos++] & 0xFF;
+ nSaltLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ sal_Int16 nIVLength = ( pBuffer[nPos++] & 0xFF );
+ nIVLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+ sal_Int16 nDigestLength = pBuffer[nPos++] & 0xFF;
+ nDigestLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+
+ sal_Int16 nMediaTypeLength = pBuffer[nPos++] & 0xFF;
+ nMediaTypeLength |= ( pBuffer[nPos++] & 0xFF ) << 8;
+
+ if ( nSaltLength == rStream->readBytes ( aBuffer, nSaltLength ) )
+ {
+ rData->m_aSalt.realloc ( nSaltLength );
+ memcpy ( rData->m_aSalt.getArray(), aBuffer.getConstArray(), nSaltLength );
+ if ( nIVLength == rStream->readBytes ( aBuffer, nIVLength ) )
+ {
+ rData->m_aInitVector.realloc ( nIVLength );
+ memcpy ( rData->m_aInitVector.getArray(), aBuffer.getConstArray(), nIVLength );
+ if ( nDigestLength == rStream->readBytes ( aBuffer, nDigestLength ) )
+ {
+ rData->m_aDigest.realloc ( nDigestLength );
+ memcpy ( rData->m_aDigest.getArray(), aBuffer.getConstArray(), nDigestLength );
+
+ if ( nMediaTypeLength == rStream->readBytes ( aBuffer, nMediaTypeLength ) )
+ {
+ aMediaType = OUString( reinterpret_cast<sal_Unicode const *>(aBuffer.getConstArray()),
+ nMediaTypeLength / sizeof( sal_Unicode ) );
+ bOk = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+uno::Reference< XInputStream > ZipFile::StaticGetDataFromRawStream( const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< XInputStream >& xStream,
+ const ::rtl::Reference< EncryptionData > &rData )
+{
+ if ( !rData.is() )
+ throw ZipIOException("Encrypted stream without encryption data!" );
+
+ if ( !rData->m_aKey.hasElements() )
+ throw packages::WrongPasswordException(THROW_WHERE );
+
+ uno::Reference< XSeekable > xSeek( xStream, UNO_QUERY );
+ if ( !xSeek.is() )
+ throw ZipIOException("The stream must be seekable!" );
+
+ // if we have a digest, then this file is an encrypted one and we should
+ // check if we can decrypt it or not
+ OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" );
+ if ( rData->m_aDigest.hasElements() )
+ {
+ sal_Int32 nSize = sal::static_int_cast< sal_Int32 >( xSeek->getLength() );
+ if ( nSize > n_ConstDigestLength + 32 )
+ nSize = n_ConstDigestLength + 32;
+
+ // skip header
+ xSeek->seek( n_ConstHeaderSize + rData->m_aInitVector.getLength() +
+ rData->m_aSalt.getLength() + rData->m_aDigest.getLength() );
+
+ // Only want to read enough to verify the digest
+ Sequence < sal_Int8 > aReadBuffer ( nSize );
+
+ xStream->readBytes( aReadBuffer, nSize );
+
+ if ( !StaticHasValidPassword( rxContext, aReadBuffer, rData ) )
+ throw packages::WrongPasswordException(THROW_WHERE );
+ }
+
+ return new XUnbufferedStream( aMutexHolder, xStream, rData );
+}
+
+#if 0
+// for debugging purposes
+void CheckSequence( const uno::Sequence< sal_Int8 >& aSequence )
+{
+ if ( aSequence.getLength() )
+ {
+ sal_Int32* pPointer = *( (sal_Int32**)&aSequence );
+ sal_Int32 nSize = *( pPointer + 1 );
+ sal_Int32 nMemSize = *( pPointer - 2 );
+ sal_Int32 nUsedMemSize = ( nSize + 4 * sizeof( sal_Int32 ) );
+ OSL_ENSURE( nSize == aSequence.getLength() && nUsedMemSize + 7 - ( nUsedMemSize - 1 ) % 8 == nMemSize, "Broken Sequence!" );
+ }
+}
+#endif
+
+bool ZipFile::StaticHasValidPassword( const uno::Reference< uno::XComponentContext >& rxContext, const Sequence< sal_Int8 > &aReadBuffer, const ::rtl::Reference< EncryptionData > &rData )
+{
+ if ( !rData.is() || !rData->m_aKey.hasElements() )
+ return false;
+
+ bool bRet = false;
+
+ uno::Reference< xml::crypto::XCipherContext > xCipher( StaticGetCipher( rxContext, rData, false ), uno::UNO_SET_THROW );
+
+ uno::Sequence< sal_Int8 > aDecryptBuffer;
+ uno::Sequence< sal_Int8 > aDecryptBuffer2;
+ try
+ {
+ aDecryptBuffer = xCipher->convertWithCipherContext( aReadBuffer );
+ aDecryptBuffer2 = xCipher->finalizeCipherContextAndDispose();
+ }
+ catch( uno::Exception& )
+ {
+ // decryption with padding will throw the exception in finalizing if the buffer represent only part of the stream
+ // it is no problem, actually this is why we read 32 additional bytes ( two of maximal possible encryption blocks )
+ }
+
+ if ( aDecryptBuffer2.hasElements() )
+ {
+ sal_Int32 nOldLen = aDecryptBuffer.getLength();
+ aDecryptBuffer.realloc( nOldLen + aDecryptBuffer2.getLength() );
+ memcpy( aDecryptBuffer.getArray() + nOldLen, aDecryptBuffer2.getConstArray(), aDecryptBuffer2.getLength() );
+ }
+
+ if ( aDecryptBuffer.getLength() > n_ConstDigestLength )
+ aDecryptBuffer.realloc( n_ConstDigestLength );
+
+ uno::Sequence< sal_Int8 > aDigestSeq;
+ uno::Reference< xml::crypto::XDigestContext > xDigestContext( StaticGetDigestContextForChecksum( rxContext, rData ), uno::UNO_SET_THROW );
+
+ xDigestContext->updateDigest( aDecryptBuffer );
+ aDigestSeq = xDigestContext->finalizeDigestAndDispose();
+
+ // If we don't have a digest, then we have to assume that the password is correct
+ if ( rData->m_aDigest.hasElements() &&
+ ( aDigestSeq.getLength() != rData->m_aDigest.getLength() ||
+ 0 != memcmp ( aDigestSeq.getConstArray(),
+ rData->m_aDigest.getConstArray(),
+ aDigestSeq.getLength() ) ) )
+ {
+ // We should probably tell the user that the password they entered was wrong
+ }
+ else
+ bRet = true;
+
+ return bRet;
+}
+
+bool ZipFile::hasValidPassword ( ZipEntry const & rEntry, const ::rtl::Reference< EncryptionData >& rData )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ bool bRet = false;
+ if ( rData.is() && rData->m_aKey.hasElements() )
+ {
+ css::uno::Reference < css::io::XSeekable > xSeek(xStream, UNO_QUERY_THROW);
+ xSeek->seek( rEntry.nOffset );
+ sal_Int64 nSize = rEntry.nMethod == DEFLATED ? rEntry.nCompressedSize : rEntry.nSize;
+
+ // Only want to read enough to verify the digest
+ if ( nSize > n_ConstDigestDecrypt )
+ nSize = n_ConstDigestDecrypt;
+
+ Sequence < sal_Int8 > aReadBuffer ( nSize );
+
+ xStream->readBytes( aReadBuffer, nSize );
+
+ bRet = StaticHasValidPassword( m_xContext, aReadBuffer, rData );
+ }
+
+ return bRet;
+}
+
+namespace {
+
+class XBufferedStream : public cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable>
+{
+ std::vector<sal_Int8> maBytes;
+ size_t mnPos;
+
+ size_t remainingSize() const
+ {
+ return maBytes.size() - mnPos;
+ }
+
+ bool hasBytes() const
+ {
+ return mnPos < maBytes.size();
+ }
+
+public:
+ XBufferedStream( const uno::Reference<XInputStream>& xSrcStream ) : mnPos(0)
+ {
+ sal_Int32 nRemaining = xSrcStream->available();
+ uno::Reference< css::lang::XUnoTunnel > xInputTunnel( xSrcStream, uno::UNO_QUERY );
+ comphelper::ByteReader* pByteReader = nullptr;
+ if (xInputTunnel)
+ pByteReader = reinterpret_cast< comphelper::ByteReader* >( xInputTunnel->getSomething( comphelper::ByteReader::getUnoTunnelId() ) );
+
+ if (pByteReader)
+ {
+ maBytes.resize(nRemaining);
+
+ sal_Int8* pData = maBytes.data();
+ while (nRemaining > 0)
+ {
+ sal_Int32 nRead = pByteReader->readSomeBytes(pData, nRemaining);
+ nRemaining -= nRead;
+ pData += nRead;
+ }
+ }
+ else
+ {
+ const sal_Int32 nBufSize = 8192;
+
+ sal_Int32 nRead = 0;
+ maBytes.reserve(nRemaining);
+ uno::Sequence<sal_Int8> aBuf(nBufSize);
+
+ auto readAndCopy = [&]( sal_Int32 nReadSize ) -> sal_Int32
+ {
+ sal_Int32 nBytes = xSrcStream->readBytes(aBuf, nReadSize);
+ const sal_Int8* p = aBuf.getConstArray();
+ const sal_Int8* pEnd = p + nBytes;
+ maBytes.insert( maBytes.end(), p, pEnd );
+ return nBytes;
+ };
+
+ while (nRemaining > nBufSize)
+ {
+ const auto nBytes = readAndCopy(nBufSize);
+ if (!nBytes)
+ break;
+ nRead += nBytes;
+ nRemaining -= nBytes;
+ }
+
+ if (nRemaining)
+ nRead += readAndCopy(nRemaining);
+ maBytes.resize(nRead);
+ }
+ }
+
+ virtual sal_Int32 SAL_CALL readBytes( uno::Sequence<sal_Int8>& rData, sal_Int32 nBytesToRead ) override
+ {
+ if (!hasBytes())
+ return 0;
+
+ sal_Int32 nReadSize = std::min<sal_Int32>(nBytesToRead, remainingSize());
+ rData.realloc(nReadSize);
+ auto pData = rData.getArray();
+ std::vector<sal_Int8>::const_iterator it = maBytes.cbegin();
+ std::advance(it, mnPos);
+ for (sal_Int32 i = 0; i < nReadSize; ++i, ++it)
+ pData[i] = *it;
+
+ mnPos += nReadSize;
+
+ return nReadSize;
+ }
+
+ virtual sal_Int32 SAL_CALL readSomeBytes( ::css::uno::Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead ) override
+ {
+ return readBytes(rData, nMaxBytesToRead);
+ }
+
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override
+ {
+ if (!hasBytes())
+ return;
+
+ mnPos += nBytesToSkip;
+ }
+
+ virtual sal_Int32 SAL_CALL available() override
+ {
+ if (!hasBytes())
+ return 0;
+
+ return remainingSize();
+ }
+
+ virtual void SAL_CALL closeInput() override
+ {
+ }
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override
+ {
+ if ( location < 0 || o3tl::make_unsigned(location) > maBytes.size() )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ mnPos = location;
+ }
+ virtual sal_Int64 SAL_CALL getPosition() override
+ {
+ return mnPos;
+ }
+ virtual sal_Int64 SAL_CALL getLength() override
+ {
+ return maBytes.size();
+ }
+};
+
+}
+
+uno::Reference< XInputStream > ZipFile::createStreamForZipEntry(
+ const rtl::Reference< comphelper::RefCountedMutex >& aMutexHolder,
+ ZipEntry const & rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ sal_Int8 nStreamMode,
+ bool bIsEncrypted,
+ const bool bUseBufferedStream,
+ const OUString& aMediaType )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ rtl::Reference< XUnbufferedStream > xSrcStream = new XUnbufferedStream(
+ m_xContext, aMutexHolder, rEntry, xStream, rData, nStreamMode, bIsEncrypted, aMediaType, bRecoveryMode);
+
+ if (!bUseBufferedStream)
+ return xSrcStream;
+
+ uno::Reference<io::XInputStream> xBufStream;
+#ifndef EMSCRIPTEN
+ static const sal_Int32 nThreadingThreshold = 10000;
+
+ if( xSrcStream->available() > nThreadingThreshold )
+ xBufStream = new XBufferedThreadedStream(xSrcStream, xSrcStream->getSize());
+ else
+#endif
+ xBufStream = new XBufferedStream(xSrcStream);
+
+ return xBufStream;
+}
+
+ZipEnumeration ZipFile::entries()
+{
+ return aEntries;
+}
+
+uno::Reference< XInputStream > ZipFile::getInputStream( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ // We want to return a rawStream if we either don't have a key or if the
+ // key is wrong
+
+ bool bNeedRawStream = rEntry.nMethod == STORED;
+
+ // if we have a digest, then this file is an encrypted one and we should
+ // check if we can decrypt it or not
+ if ( bIsEncrypted && rData.is() && rData->m_aDigest.hasElements() )
+ bNeedRawStream = !hasValidPassword ( rEntry, rData );
+
+ return createStreamForZipEntry ( aMutexHolder,
+ rEntry,
+ rData,
+ bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
+ bIsEncrypted );
+}
+
+uno::Reference< XInputStream > ZipFile::getDataStream( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData > &rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ // An exception must be thrown in case stream is encrypted and
+ // there is no key or the key is wrong
+ bool bNeedRawStream = false;
+ if ( bIsEncrypted )
+ {
+ // in case no digest is provided there is no way
+ // to detect password correctness
+ if ( !rData.is() )
+ throw ZipException("Encrypted stream without encryption data!" );
+
+ // if we have a digest, then this file is an encrypted one and we should
+ // check if we can decrypt it or not
+ OSL_ENSURE( rData->m_aDigest.hasElements(), "Can't detect password correctness without digest!" );
+ if ( rData->m_aDigest.hasElements() && !hasValidPassword ( rEntry, rData ) )
+ throw packages::WrongPasswordException(THROW_WHERE );
+ }
+ else
+ bNeedRawStream = ( rEntry.nMethod == STORED );
+
+ return createStreamForZipEntry ( aMutexHolder,
+ rEntry,
+ rData,
+ bNeedRawStream ? UNBUFF_STREAM_RAW : UNBUFF_STREAM_DATA,
+ bIsEncrypted );
+}
+
+uno::Reference< XInputStream > ZipFile::getRawData( ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData >& rData,
+ bool bIsEncrypted,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder,
+ const bool bUseBufferedStream )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_RAW, bIsEncrypted, bUseBufferedStream );
+}
+
+uno::Reference< XInputStream > ZipFile::getWrappedRawStream(
+ ZipEntry& rEntry,
+ const ::rtl::Reference< EncryptionData >& rData,
+ const OUString& aMediaType,
+ const rtl::Reference<comphelper::RefCountedMutex>& aMutexHolder )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( !rData.is() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ if ( rEntry.nOffset <= 0 )
+ readLOC( rEntry );
+
+ return createStreamForZipEntry ( aMutexHolder, rEntry, rData, UNBUFF_STREAM_WRAPPEDRAW, true, true, aMediaType );
+}
+
+void ZipFile::readLOC( ZipEntry &rEntry )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int64 nPos = -rEntry.nOffset;
+
+ aGrabber.seek(nPos);
+ sal_Int32 nTestSig = aGrabber.ReadInt32();
+ if (nTestSig != LOCSIG)
+ throw ZipIOException("Invalid LOC header (bad signature)" );
+
+ // Ignore all (duplicated) information from the local file header.
+ // various programs produced "broken" zip files; even LO at some point.
+ // Just verify the path and calculate the data offset and otherwise
+ // rely on the central directory info.
+
+ aGrabber.ReadInt16(); //version
+ aGrabber.ReadInt16(); //flag
+ aGrabber.ReadInt16(); //how
+ aGrabber.ReadInt32(); //time
+ aGrabber.ReadInt32(); //crc
+ aGrabber.ReadInt32(); //compressed size
+ aGrabber.ReadInt32(); //size
+ sal_Int16 nPathLen = aGrabber.ReadInt16();
+ sal_Int16 nExtraLen = aGrabber.ReadInt16();
+ rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
+
+ // FIXME64: need to read 64bit LOC
+
+ bool bBroken = false;
+
+ try
+ {
+ sal_Int16 nPathLenToRead = nPathLen;
+ const sal_Int64 nBytesAvailable = aGrabber.getLength() - aGrabber.getPosition();
+ if (nPathLenToRead > nBytesAvailable)
+ nPathLenToRead = nBytesAvailable;
+ else if (nPathLenToRead < 0)
+ nPathLenToRead = 0;
+
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ uno::Sequence<sal_Int8> aNameBuffer(nPathLenToRead);
+ sal_Int32 nRead = aGrabber.readBytes(aNameBuffer, nPathLenToRead);
+ if (nRead < aNameBuffer.getLength())
+ aNameBuffer.realloc(nRead);
+
+ OUString sLOCPath( reinterpret_cast<const char *>(aNameBuffer.getConstArray()),
+ aNameBuffer.getLength(),
+ RTL_TEXTENCODING_UTF8 );
+
+ if ( rEntry.nPathLen == -1 ) // the file was created
+ {
+ rEntry.nPathLen = nPathLen;
+ rEntry.sPath = sLOCPath;
+ }
+
+ bBroken = rEntry.nPathLen != nPathLen
+ || rEntry.sPath != sLOCPath;
+ }
+ catch(...)
+ {
+ bBroken = true;
+ }
+
+ if ( bBroken && !bRecoveryMode )
+ throw ZipIOException("The stream seems to be broken!" );
+}
+
+sal_Int32 ZipFile::findEND()
+{
+ // this method is called in constructor only, no need for mutex
+ sal_Int32 nPos, nEnd;
+ Sequence < sal_Int8 > aBuffer;
+ try
+ {
+ sal_Int32 nLength = static_cast <sal_Int32 > (aGrabber.getLength());
+ if (nLength < ENDHDR)
+ return -1;
+ nPos = nLength - ENDHDR - ZIP_MAXNAMELEN;
+ nEnd = nPos >= 0 ? nPos : 0 ;
+
+ aGrabber.seek( nEnd );
+
+ auto nSize = nLength - nEnd;
+ if (nSize != aGrabber.readBytes(aBuffer, nSize))
+ throw ZipException("Zip END signature not found!" );
+
+ const sal_Int8 *pBuffer = aBuffer.getConstArray();
+
+ nPos = nSize - ENDHDR;
+ while ( nPos >= 0 )
+ {
+ if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 5 && pBuffer[nPos+3] == 6 )
+ return nPos + nEnd;
+ nPos--;
+ }
+ }
+ catch ( IllegalArgumentException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( NotConnectedException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( BufferSizeExceededException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ throw ZipException("Zip END signature not found!" );
+}
+
+sal_Int32 ZipFile::readCEN()
+{
+ // this method is called in constructor only, no need for mutex
+ sal_Int32 nCenPos = -1, nLocPos;
+ sal_uInt16 nCount;
+
+ try
+ {
+ sal_Int32 nEndPos = findEND();
+ if (nEndPos == -1)
+ return -1;
+ aGrabber.seek(nEndPos + ENDTOT);
+ sal_uInt16 nTotal = aGrabber.ReadUInt16();
+ sal_Int32 nCenLen = aGrabber.ReadInt32();
+ sal_Int32 nCenOff = aGrabber.ReadInt32();
+
+ if ( nTotal * CENHDR > nCenLen )
+ throw ZipException("invalid END header (bad entry count)" );
+
+ if ( nTotal > ZIP_MAXENTRIES )
+ throw ZipException("too many entries in ZIP File" );
+
+ if ( nCenLen < 0 || nCenLen > nEndPos )
+ throw ZipException("Invalid END header (bad central directory size)" );
+
+ nCenPos = nEndPos - nCenLen;
+
+ if ( nCenOff < 0 || nCenOff > nCenPos )
+ throw ZipException("Invalid END header (bad central directory size)" );
+
+ nLocPos = nCenPos - nCenOff;
+ aGrabber.seek( nCenPos );
+ Sequence < sal_Int8 > aCENBuffer ( nCenLen );
+ sal_Int64 nRead = aGrabber.readBytes ( aCENBuffer, nCenLen );
+ if ( static_cast < sal_Int64 > ( nCenLen ) != nRead )
+ throw ZipException ("Error reading CEN into memory buffer!" );
+
+ MemoryByteGrabber aMemGrabber(aCENBuffer);
+
+ ZipEntry aEntry;
+ sal_Int16 nCommentLen;
+
+ aEntries.reserve(nTotal);
+ for (nCount = 0 ; nCount < nTotal; nCount++)
+ {
+ sal_Int32 nTestSig = aMemGrabber.ReadInt32();
+ if ( nTestSig != CENSIG )
+ throw ZipException("Invalid CEN header (bad signature)" );
+
+ aMemGrabber.skipBytes ( 2 );
+ aEntry.nVersion = aMemGrabber.ReadInt16();
+ aEntry.nFlag = aMemGrabber.ReadInt16();
+
+ if ( ( aEntry.nFlag & 1 ) == 1 )
+ throw ZipException("Invalid CEN header (encrypted entry)" );
+
+ aEntry.nMethod = aMemGrabber.ReadInt16();
+
+ if ( aEntry.nMethod != STORED && aEntry.nMethod != DEFLATED)
+ throw ZipException("Invalid CEN header (bad compression method)" );
+
+ aEntry.nTime = aMemGrabber.ReadInt32();
+ aEntry.nCrc = aMemGrabber.ReadInt32();
+
+ sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
+ sal_uInt32 nSize = aMemGrabber.ReadUInt32();
+ aEntry.nPathLen = aMemGrabber.ReadInt16();
+ aEntry.nExtraLen = aMemGrabber.ReadInt16();
+ nCommentLen = aMemGrabber.ReadInt16();
+ aMemGrabber.skipBytes ( 8 );
+ sal_uInt32 nOffset = aMemGrabber.ReadUInt32();
+
+ // FIXME64: need to read the 64bit header instead
+ if ( nSize == 0xffffffff ||
+ nOffset == 0xffffffff ||
+ nCompressedSize == 0xffffffff ) {
+ throw ZipException("PK64 zip file entry" );
+ }
+ aEntry.nCompressedSize = nCompressedSize;
+ aEntry.nSize = nSize;
+ aEntry.nOffset = nOffset;
+
+ aEntry.nOffset += nLocPos;
+ aEntry.nOffset *= -1;
+
+ if ( aEntry.nPathLen < 0 )
+ throw ZipException("unexpected name length" );
+
+ if ( nCommentLen < 0 )
+ throw ZipException("unexpected comment length" );
+
+ if ( aEntry.nExtraLen < 0 )
+ throw ZipException("unexpected extra header info length" );
+
+ if (aEntry.nPathLen > aMemGrabber.remainingSize())
+ throw ZipException("name too long");
+
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ aEntry.sPath = OUString( reinterpret_cast<char const *>(aMemGrabber.getCurrentPos()),
+ aEntry.nPathLen,
+ RTL_TEXTENCODING_UTF8 );
+
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
+ throw ZipException("Zip entry has an invalid name." );
+
+ aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
+ aEntries[aEntry.sPath] = aEntry;
+ }
+
+ if (nCount != nTotal)
+ throw ZipException("Count != Total" );
+ }
+ catch ( IllegalArgumentException & )
+ {
+ // seek can throw this...
+ nCenPos = -1; // make sure we return -1 to indicate an error
+ }
+ return nCenPos;
+}
+
+void ZipFile::recover()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int64 nLength;
+ Sequence < sal_Int8 > aBuffer;
+
+ try
+ {
+ nLength = aGrabber.getLength();
+ if (nLength < ENDHDR)
+ return;
+
+ aGrabber.seek( 0 );
+
+ const sal_Int64 nToRead = 32000;
+ for( sal_Int64 nGenPos = 0; aGrabber.readBytes( aBuffer, nToRead ) && aBuffer.getLength() > 16; )
+ {
+ const sal_Int8 *pBuffer = aBuffer.getConstArray();
+ sal_Int32 nBufSize = aBuffer.getLength();
+
+ sal_Int64 nPos = 0;
+ // the buffer should contain at least one header,
+ // or if it is end of the file, at least the postheader with sizes and hash
+ while( nPos < nBufSize - 30
+ || ( nBufSize < nToRead && nPos < nBufSize - 16 ) )
+
+ {
+ if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
+ {
+ ZipEntry aEntry;
+ Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 26);
+ MemoryByteGrabber aMemGrabber(aTmpBuffer);
+
+ aEntry.nVersion = aMemGrabber.ReadInt16();
+ aEntry.nFlag = aMemGrabber.ReadInt16();
+
+ if ( ( aEntry.nFlag & 1 ) != 1 )
+ {
+ aEntry.nMethod = aMemGrabber.ReadInt16();
+
+ if ( aEntry.nMethod == STORED || aEntry.nMethod == DEFLATED )
+ {
+ aEntry.nTime = aMemGrabber.ReadInt32();
+ aEntry.nCrc = aMemGrabber.ReadInt32();
+ sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
+ sal_uInt32 nSize = aMemGrabber.ReadUInt32();
+ aEntry.nPathLen = aMemGrabber.ReadInt16();
+ aEntry.nExtraLen = aMemGrabber.ReadInt16();
+
+ // FIXME64: need to read the 64bit header instead
+ if ( nSize == 0xffffffff ||
+ nCompressedSize == 0xffffffff ) {
+ throw ZipException("PK64 zip file entry" );
+ }
+ aEntry.nCompressedSize = nCompressedSize;
+ aEntry.nSize = nSize;
+
+ sal_Int32 nDescrLength =
+ ( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
+
+ sal_Int64 nDataSize = ( aEntry.nMethod == DEFLATED ) ? aEntry.nCompressedSize : aEntry.nSize;
+ sal_Int64 nBlockLength = nDataSize + aEntry.nPathLen + aEntry.nExtraLen + 30 + nDescrLength;
+ if ( aEntry.nPathLen >= 0 && aEntry.nExtraLen >= 0
+ && ( nGenPos + nPos + nBlockLength ) <= nLength )
+ {
+ // read always in UTF8, some tools seem not to set UTF8 bit
+ if( nPos + 30 + aEntry.nPathLen <= nBufSize )
+ aEntry.sPath = OUString ( reinterpret_cast<char const *>(&pBuffer[nPos + 30]),
+ aEntry.nPathLen,
+ RTL_TEXTENCODING_UTF8 );
+ else
+ {
+ Sequence < sal_Int8 > aFileName;
+ aGrabber.seek( nGenPos + nPos + 30 );
+ aGrabber.readBytes( aFileName, aEntry.nPathLen );
+ aEntry.sPath = OUString ( reinterpret_cast<const char *>(aFileName.getConstArray()),
+ aFileName.getLength(),
+ RTL_TEXTENCODING_UTF8 );
+ aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
+ }
+
+ aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
+
+ if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
+ {
+ aEntry.nCrc = 0;
+ aEntry.nCompressedSize = 0;
+ aEntry.nSize = 0;
+ }
+
+ aEntries.emplace( aEntry.sPath, aEntry );
+ }
+ }
+ }
+
+ nPos += 4;
+ }
+ else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
+ {
+ sal_Int64 nCompressedSize, nSize;
+ Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 12);
+ MemoryByteGrabber aMemGrabber(aTmpBuffer);
+ sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
+ sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32();
+ sal_uInt32 nSize32 = aMemGrabber.ReadUInt32();
+
+ // FIXME64: work to be done here ...
+ nCompressedSize = nCompressedSize32;
+ nSize = nSize32;
+
+ for( auto& rEntry : aEntries )
+ {
+ // this is a broken package, accept this block not only for DEFLATED streams
+ if( rEntry.second.nFlag & 8 )
+ {
+ sal_Int64 nStreamOffset = nGenPos + nPos - nCompressedSize;
+ if ( nStreamOffset == rEntry.second.nOffset && nCompressedSize > rEntry.second.nCompressedSize )
+ {
+ // only DEFLATED blocks need to be checked
+ bool bAcceptBlock = ( rEntry.second.nMethod == STORED && nCompressedSize == nSize );
+
+ if ( !bAcceptBlock )
+ {
+ sal_Int64 nRealSize = 0;
+ sal_Int32 nRealCRC = 0;
+ getSizeAndCRC( nStreamOffset, nCompressedSize, &nRealSize, &nRealCRC );
+ bAcceptBlock = ( nRealSize == nSize && nRealCRC == nCRC32 );
+ }
+
+ if ( bAcceptBlock )
+ {
+ rEntry.second.nCrc = nCRC32;
+ rEntry.second.nCompressedSize = nCompressedSize;
+ rEntry.second.nSize = nSize;
+ }
+ }
+#if 0
+// for now ignore clearly broken streams
+ else if( !rEntry.second.nCompressedSize )
+ {
+ rEntry.second.nCrc = nCRC32;
+ sal_Int32 nRealStreamSize = nGenPos + nPos - rEntry.second.nOffset;
+ rEntry.second.nCompressedSize = nRealStreamSize;
+ rEntry.second.nSize = nSize;
+ }
+#endif
+ }
+ }
+
+ nPos += 4;
+ }
+ else
+ nPos++;
+ }
+
+ nGenPos += nPos;
+ aGrabber.seek( nGenPos );
+ }
+ }
+ catch ( IllegalArgumentException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( NotConnectedException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+ catch ( BufferSizeExceededException& )
+ {
+ throw ZipException("Zip END signature not found!" );
+ }
+}
+
+bool ZipFile::checkSizeAndCRC( const ZipEntry& aEntry )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ sal_Int32 nCRC = 0;
+ sal_Int64 nSize = 0;
+
+ if( aEntry.nMethod == STORED )
+ return ( getCRC( aEntry.nOffset, aEntry.nSize ) == aEntry.nCrc );
+
+ getSizeAndCRC( aEntry.nOffset, aEntry.nCompressedSize, &nSize, &nCRC );
+ return ( aEntry.nSize == nSize && aEntry.nCrc == nCRC );
+}
+
+sal_Int32 ZipFile::getCRC( sal_Int64 nOffset, sal_Int64 nSize )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ Sequence < sal_Int8 > aBuffer;
+ CRC32 aCRC;
+ sal_Int64 nBlockSize = ::std::min(nSize, static_cast< sal_Int64 >(32000));
+
+ aGrabber.seek( nOffset );
+ for (sal_Int64 ind = 0;
+ aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nSize;
+ ++ind)
+ {
+ sal_Int64 nLen = ::std::min(nBlockSize, nSize - ind * nBlockSize);
+ aCRC.updateSegment(aBuffer, static_cast<sal_Int32>(nLen));
+ }
+
+ return aCRC.getValue();
+}
+
+void ZipFile::getSizeAndCRC( sal_Int64 nOffset, sal_Int64 nCompressedSize, sal_Int64 *nSize, sal_Int32 *nCRC )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ Sequence < sal_Int8 > aBuffer;
+ CRC32 aCRC;
+ sal_Int64 nRealSize = 0;
+ Inflater aInflaterLocal( true );
+ sal_Int32 nBlockSize = static_cast< sal_Int32 > (::std::min( nCompressedSize, static_cast< sal_Int64 >( 32000 ) ) );
+
+ aGrabber.seek( nOffset );
+ for ( sal_Int64 ind = 0;
+ !aInflaterLocal.finished() && aGrabber.readBytes( aBuffer, nBlockSize ) && ind * nBlockSize < nCompressedSize;
+ ind++ )
+ {
+ Sequence < sal_Int8 > aData( nBlockSize );
+ sal_Int32 nLastInflated = 0;
+ sal_Int64 nInBlock = 0;
+
+ aInflaterLocal.setInput( aBuffer );
+ do
+ {
+ nLastInflated = aInflaterLocal.doInflateSegment( aData, 0, nBlockSize );
+ aCRC.updateSegment( aData, nLastInflated );
+ nInBlock += nLastInflated;
+ } while( !aInflater.finished() && nLastInflated );
+
+ nRealSize += nInBlock;
+ }
+
+ *nSize = nRealSize;
+ *nCRC = aCRC.getValue();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipOutputEntry.cxx b/package/source/zipapi/ZipOutputEntry.cxx
new file mode 100644
index 000000000..897ea6212
--- /dev/null
+++ b/package/source/zipapi/ZipOutputEntry.cxx
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipOutputEntry.hxx>
+
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+
+#include <osl/diagnose.h>
+
+#include <PackageConstants.hxx>
+#include <ThreadedDeflater.hxx>
+#include <ZipEntry.hxx>
+#include <ZipFile.hxx>
+#include <ZipPackageStream.hxx>
+
+#include <algorithm>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+/** This class is used to deflate Zip entries
+ */
+ZipOutputEntryBase::ZipOutputEntryBase(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt,
+ bool checkStream)
+: m_xContext(rxContext)
+, m_xOutStream(rxOutput)
+, m_pCurrentEntry(&rEntry)
+, m_nDigested(0)
+, m_pCurrentStream(pStream)
+, m_bEncryptCurrentEntry(bEncrypt)
+{
+ assert(m_pCurrentEntry->nMethod == DEFLATED && "Use ZipPackageStream::rawWrite() for STORED entries");
+ (void)checkStream;
+ assert(!checkStream || m_xOutStream.is());
+ if (m_bEncryptCurrentEntry)
+ {
+ m_xCipherContext = ZipFile::StaticGetCipher( m_xContext, pStream->GetEncryptionData(), true );
+ m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( m_xContext, pStream->GetEncryptionData() );
+ }
+}
+
+void ZipOutputEntryBase::closeEntry()
+{
+ finishDeflater();
+
+ if ((m_pCurrentEntry->nFlag & 8) == 0)
+ {
+ if (m_pCurrentEntry->nSize != getDeflaterTotalIn())
+ {
+ OSL_FAIL("Invalid entry size");
+ }
+ if (m_pCurrentEntry->nCompressedSize != getDeflaterTotalOut())
+ {
+ // Different compression strategies make the merit of this
+ // test somewhat dubious
+ m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
+ }
+ if (m_pCurrentEntry->nCrc != m_aCRC.getValue())
+ {
+ OSL_FAIL("Invalid entry CRC-32");
+ }
+ }
+ else
+ {
+ if ( !m_bEncryptCurrentEntry )
+ {
+ m_pCurrentEntry->nSize = getDeflaterTotalIn();
+ m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
+ }
+ m_pCurrentEntry->nCrc = m_aCRC.getValue();
+ }
+ deflaterReset();
+ m_aCRC.reset();
+
+ if (!m_bEncryptCurrentEntry)
+ return;
+
+ m_xCipherContext.clear();
+
+ uno::Sequence< sal_Int8 > aDigestSeq;
+ if ( m_xDigestContext.is() )
+ {
+ aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
+ m_xDigestContext.clear();
+ }
+
+ if ( m_pCurrentStream )
+ m_pCurrentStream->setDigest( aDigestSeq );
+}
+
+void ZipOutputEntryBase::processDeflated( const uno::Sequence< sal_Int8 >& deflateBuffer, sal_Int32 nLength )
+{
+ if ( nLength > 0 )
+ {
+ uno::Sequence< sal_Int8 > aTmpBuffer( deflateBuffer.getConstArray(), nLength );
+ if ( m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() )
+ {
+ // Need to update our digest before encryption...
+ sal_Int32 nDiff = n_ConstDigestLength - m_nDigested;
+ if ( nDiff )
+ {
+ sal_Int32 nEat = ::std::min( nLength, nDiff );
+ uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
+ m_xDigestContext->updateDigest( aTmpSeq );
+ m_nDigested = m_nDigested + static_cast< sal_Int16 >( nEat );
+ }
+
+ // FIXME64: uno::Sequence not 64bit safe.
+ uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
+
+ m_xOutStream->writeBytes( aEncryptionBuffer );
+
+ // the sizes as well as checksum for encrypted streams is calculated here
+ m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
+ m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
+ m_aCRC.update( aEncryptionBuffer );
+ }
+ else
+ {
+ m_xOutStream->writeBytes ( aTmpBuffer );
+ }
+ }
+
+ if ( !(isDeflaterFinished() && m_bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is()) )
+ return;
+
+ // FIXME64: sequence not 64bit safe.
+ uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
+ if ( aEncryptionBuffer.hasElements() )
+ {
+ m_xOutStream->writeBytes( aEncryptionBuffer );
+
+ // the sizes as well as checksum for encrypted streams are calculated here
+ m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
+ m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
+ m_aCRC.update( aEncryptionBuffer );
+ }
+}
+
+void ZipOutputEntryBase::processInput( const uno::Sequence< sal_Int8 >& rBuffer )
+{
+ if (!m_bEncryptCurrentEntry)
+ m_aCRC.updateSegment(rBuffer, rBuffer.getLength());
+}
+
+ZipOutputEntry::ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt,
+ bool checkStream)
+: ZipOutputEntryBase(rxOutput, rxContext, rEntry, pStream, bEncrypt, checkStream)
+, m_aDeflateBuffer(n_ConstBufferSize)
+, m_aDeflater(DEFAULT_COMPRESSION, true)
+{
+}
+
+ZipOutputEntry::ZipOutputEntry(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntry( rxOutput, rxContext, rEntry, pStream, bEncrypt, true)
+{
+}
+
+void ZipOutputEntry::write( const Sequence< sal_Int8 >& rBuffer )
+{
+ if (!m_aDeflater.finished())
+ {
+ m_aDeflater.setInputSegment(rBuffer);
+ while (!m_aDeflater.needsInput())
+ doDeflate();
+ processInput(rBuffer);
+ }
+}
+
+void ZipOutputEntry::doDeflate()
+{
+ sal_Int32 nLength = m_aDeflater.doDeflateSegment(m_aDeflateBuffer, m_aDeflateBuffer.getLength());
+ processDeflated( m_aDeflateBuffer, nLength );
+}
+
+void ZipOutputEntry::finishDeflater()
+{
+ m_aDeflater.finish();
+ while (!m_aDeflater.finished())
+ doDeflate();
+}
+
+sal_Int64 ZipOutputEntry::getDeflaterTotalIn() const
+{
+ return m_aDeflater.getTotalIn();
+}
+
+sal_Int64 ZipOutputEntry::getDeflaterTotalOut() const
+{
+ return m_aDeflater.getTotalOut();
+}
+
+void ZipOutputEntry::deflaterReset()
+{
+ m_aDeflater.reset();
+}
+
+bool ZipOutputEntry::isDeflaterFinished() const
+{
+ return m_aDeflater.finished();
+}
+
+
+ZipOutputEntryInThread::ZipOutputEntryInThread(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntry( uno::Reference< css::io::XOutputStream >(), rxContext, rEntry, pStream, bEncrypt, false )
+, m_bFinished(false)
+{
+}
+
+void ZipOutputEntryInThread::createBufferFile()
+{
+ assert(!m_xOutStream && !m_xTempFile &&
+ "should only be called in the threaded mode where there is no existing stream yet");
+ m_xTempFile.set(
+ io::TempFile::create(m_xContext),
+ uno::UNO_SET_THROW );
+ m_xOutStream = m_xTempFile->getOutputStream();
+}
+
+void ZipOutputEntryInThread::closeBufferFile()
+{
+ m_xOutStream->closeOutput();
+ m_xOutStream.clear();
+}
+
+void ZipOutputEntryInThread::deleteBufferFile()
+{
+ assert(!m_xOutStream.is() && m_xTempFile);
+ m_xTempFile.clear();
+}
+
+uno::Reference< io::XInputStream > ZipOutputEntryInThread::getData() const
+{
+ return m_xTempFile->getInputStream();
+}
+
+class ZipOutputEntryInThread::Task : public comphelper::ThreadTask
+{
+ ZipOutputEntryInThread *mpEntry;
+ uno::Reference< io::XInputStream > mxInStream;
+
+public:
+ Task( const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, ZipOutputEntryInThread *pEntry,
+ const uno::Reference< io::XInputStream >& xInStream )
+ : comphelper::ThreadTask(pTag)
+ , mpEntry(pEntry)
+ , mxInStream(xInStream)
+ {}
+
+private:
+ virtual void doWork() override
+ {
+ try
+ {
+ mpEntry->createBufferFile();
+ mpEntry->writeStream(mxInStream);
+ mxInStream.clear();
+ mpEntry->closeBufferFile();
+ mpEntry->setFinished();
+ }
+ catch (...)
+ {
+ mpEntry->setParallelDeflateException(std::current_exception());
+ try
+ {
+ if (mpEntry->m_xOutStream.is())
+ mpEntry->closeBufferFile();
+ if (mpEntry->m_xTempFile)
+ mpEntry->deleteBufferFile();
+ }
+ catch (uno::Exception const&)
+ {
+ }
+ mpEntry->setFinished();
+ }
+ }
+};
+
+std::unique_ptr<comphelper::ThreadTask> ZipOutputEntryInThread::createTask(
+ const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
+ const uno::Reference< io::XInputStream >& xInStream )
+{
+ return std::make_unique<Task>(pTag, this, xInStream);
+}
+
+void ZipOutputEntry::writeStream(const uno::Reference< io::XInputStream >& xInStream)
+{
+ sal_Int32 nLength = 0;
+ uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
+ do
+ {
+ nLength = xInStream->readBytes(aSeq, n_ConstBufferSize);
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ write(aSeq);
+ }
+ while (nLength == n_ConstBufferSize);
+ closeEntry();
+}
+
+
+ZipOutputEntryParallel::ZipOutputEntryParallel(
+ const css::uno::Reference< css::io::XOutputStream >& rxOutput,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ZipEntry& rEntry,
+ ZipPackageStream* pStream,
+ bool bEncrypt)
+: ZipOutputEntryBase(rxOutput, rxContext, rEntry, pStream, bEncrypt, true)
+, totalIn(0)
+, totalOut(0)
+, finished(false)
+{
+}
+
+void ZipOutputEntryParallel::writeStream(const uno::Reference< io::XInputStream >& xInStream)
+{
+ ZipUtils::ThreadedDeflater deflater( DEFAULT_COMPRESSION );
+ deflater.deflateWrite(xInStream,
+ [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
+ if (!m_bEncryptCurrentEntry)
+ m_aCRC.updateSegment(rBuffer, nLen);
+ },
+ [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
+ processDeflated(rBuffer, nLen);
+ }
+ );
+ finished = true;
+ processDeflated( uno::Sequence< sal_Int8 >(), 0 ); // finish encrypting, etc.
+ totalIn = deflater.getTotalIn();
+ totalOut = deflater.getTotalOut();
+ closeEntry();
+}
+
+void ZipOutputEntryParallel::finishDeflater()
+{
+ // ThreadedDeflater is called synchronously in one call, so nothing to do here.
+}
+
+sal_Int64 ZipOutputEntryParallel::getDeflaterTotalIn() const
+{
+ return totalIn;
+}
+
+sal_Int64 ZipOutputEntryParallel::getDeflaterTotalOut() const
+{
+ return totalOut;
+}
+
+void ZipOutputEntryParallel::deflaterReset()
+{
+ totalIn = 0;
+ totalOut = 0;
+ finished = false;
+}
+
+bool ZipOutputEntryParallel::isDeflaterFinished() const
+{
+ return finished;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/ZipOutputStream.cxx b/package/source/zipapi/ZipOutputStream.cxx
new file mode 100644
index 000000000..33321627b
--- /dev/null
+++ b/package/source/zipapi/ZipOutputStream.cxx
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ZipOutputStream.hxx>
+
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <comphelper/storagehelper.hxx>
+
+#include <osl/time.h>
+#include <osl/thread.hxx>
+
+#include <PackageConstants.hxx>
+#include <ZipEntry.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipPackageStream.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+/** This class is used to write Zip files
+ */
+ZipOutputStream::ZipOutputStream( const uno::Reference < io::XOutputStream > &xOStream )
+: m_xStream(xOStream)
+, mpThreadTaskTag( comphelper::ThreadPool::createThreadTaskTag() )
+, m_aChucker(xOStream)
+, m_pCurrentEntry(nullptr)
+{
+}
+
+ZipOutputStream::~ZipOutputStream()
+{
+}
+
+void ZipOutputStream::setEntry( ZipEntry *pEntry )
+{
+ if (pEntry->nTime == -1)
+ pEntry->nTime = getCurrentDosTime();
+ if (pEntry->nMethod == -1)
+ pEntry->nMethod = DEFLATED;
+ pEntry->nVersion = 20;
+ pEntry->nFlag = 1 << 11;
+ if (pEntry->nSize == -1 || pEntry->nCompressedSize == -1 ||
+ pEntry->nCrc == -1)
+ {
+ pEntry->nSize = pEntry->nCompressedSize = 0;
+ pEntry->nFlag |= 8;
+ }
+}
+
+void ZipOutputStream::addDeflatingThreadTask( ZipOutputEntryInThread *pEntry, std::unique_ptr<comphelper::ThreadTask> pTask )
+{
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(std::move(pTask));
+ m_aEntries.push_back(pEntry);
+}
+
+void ZipOutputStream::rawWrite( const Sequence< sal_Int8 >& rBuffer )
+{
+ m_aChucker.WriteBytes( rBuffer );
+}
+
+void ZipOutputStream::rawCloseEntry( bool bEncrypt )
+{
+ assert(m_pCurrentEntry && "Forgot to call writeLOC()?");
+ if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
+ writeEXT(*m_pCurrentEntry);
+
+ if (bEncrypt)
+ m_pCurrentEntry->nMethod = STORED;
+
+ m_pCurrentEntry = nullptr;
+}
+
+void ZipOutputStream::consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> pCandidate)
+{
+ //Any exceptions thrown in the threads were caught and stored for now
+ const std::exception_ptr& rCaughtException(pCandidate->getParallelDeflateException());
+ if (rCaughtException)
+ {
+ m_aDeflateException = rCaughtException; // store it for later throwing
+ // the exception handler in DeflateThreadTask should have cleaned temp file
+ return;
+ }
+
+ writeLOC(pCandidate->getZipEntry(), pCandidate->isEncrypt());
+
+ sal_Int32 nRead;
+ uno::Sequence< sal_Int8 > aSequence(n_ConstBufferSize);
+ uno::Reference< io::XInputStream > xInput = pCandidate->getData();
+ do
+ {
+ nRead = xInput->readBytes(aSequence, n_ConstBufferSize);
+ if (nRead < n_ConstBufferSize)
+ aSequence.realloc(nRead);
+
+ rawWrite(aSequence);
+ }
+ while (nRead == n_ConstBufferSize);
+ xInput.clear();
+
+ rawCloseEntry(pCandidate->isEncrypt());
+
+ pCandidate->getZipPackageStream()->successfullyWritten(pCandidate->getZipEntry());
+ pCandidate->deleteBufferFile();
+}
+
+void ZipOutputStream::consumeFinishedScheduledThreadTaskEntries()
+{
+ std::vector< ZipOutputEntryInThread* > aNonFinishedEntries;
+
+ for(ZipOutputEntryInThread* pEntry : m_aEntries)
+ {
+ if(pEntry->isFinished())
+ {
+ consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread>(pEntry));
+ }
+ else
+ {
+ aNonFinishedEntries.push_back(pEntry);
+ }
+ }
+
+ // always reset to non-consumed entries
+ m_aEntries = aNonFinishedEntries;
+}
+
+void ZipOutputStream::reduceScheduledThreadTasksToGivenNumberOrLess(std::size_t nThreadTasks)
+{
+ while(m_aEntries.size() > nThreadTasks)
+ {
+ consumeFinishedScheduledThreadTaskEntries();
+
+ if(m_aEntries.size() > nThreadTasks)
+ {
+ osl::Thread::wait(std::chrono::microseconds(100));
+ }
+ }
+}
+
+void ZipOutputStream::finish()
+{
+ assert(!m_aZipList.empty() && "Zip file must have at least one entry!");
+
+ // Wait for all thread tasks to finish & write
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(mpThreadTaskTag);
+
+ // consume all processed entries
+ while(!m_aEntries.empty())
+ {
+ ZipOutputEntryInThread* pCandidate = m_aEntries.back();
+ m_aEntries.pop_back();
+ consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread>(pCandidate));
+ }
+
+ sal_Int32 nOffset= static_cast < sal_Int32 > (m_aChucker.GetPosition());
+ for (ZipEntry* p : m_aZipList)
+ {
+ writeCEN( *p );
+ delete p;
+ }
+ writeEND( nOffset, static_cast < sal_Int32 > (m_aChucker.GetPosition()) - nOffset);
+ m_xStream->flush();
+ m_aZipList.clear();
+
+ if (m_aDeflateException)
+ { // throw once all thread tasks are finished and m_aEntries can be released
+ std::rethrow_exception(m_aDeflateException);
+ }
+}
+
+const css::uno::Reference< css::io::XOutputStream >& ZipOutputStream::getStream() const
+{
+ return m_xStream;
+}
+
+void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength)
+{
+ m_aChucker.WriteInt32( ENDSIG );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( m_aZipList.size() );
+ m_aChucker.WriteInt16( m_aZipList.size() );
+ m_aChucker.WriteUInt32( nLength );
+ m_aChucker.WriteUInt32( nOffset );
+ m_aChucker.WriteInt16( 0 );
+}
+
+static sal_uInt32 getTruncated( sal_Int64 nNum, bool *pIsTruncated )
+{
+ if( nNum >= 0xffffffff )
+ {
+ *pIsTruncated = true;
+ return 0xffffffff;
+ }
+ else
+ return static_cast< sal_uInt32 >( nNum );
+}
+
+void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
+{
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
+ throw IOException("Unexpected character is used in file name." );
+
+ OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
+ sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
+
+ m_aChucker.WriteInt32( CENSIG );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+ m_aChucker.WriteInt16( rEntry.nFlag );
+ m_aChucker.WriteInt16( rEntry.nMethod );
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteUInt32( rEntry.nTime );
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
+ m_aChucker.WriteInt16( nNameLength );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt16( 0 );
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nOffset, &bWrite64Header ) );
+
+ if( bWrite64Header )
+ {
+ // FIXME64: need to append a ZIP64 header instead of throwing
+ // We're about to silently lose people's data - which they are
+ // unlikely to appreciate so fail instead:
+ throw IOException( "File contains streams that are too large." );
+ }
+
+ Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
+ m_aChucker.WriteBytes( aSequence );
+}
+
+void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
+{
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteInt32( EXTSIG );
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
+
+ if( bWrite64Header )
+ {
+ // FIXME64: need to append a ZIP64 header instead of throwing
+ // We're about to silently lose people's data - which they are
+ // unlikely to appreciate so fail instead:
+ throw IOException( "File contains streams that are too large." );
+ }
+}
+
+void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
+{
+ assert(!m_pCurrentEntry && "Forgot to close an entry with rawCloseEntry()?");
+ m_pCurrentEntry = pEntry;
+ m_aZipList.push_back( m_pCurrentEntry );
+ const ZipEntry &rEntry = *m_pCurrentEntry;
+
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, true ) )
+ throw IOException("Unexpected character is used in file name." );
+
+ OString sUTF8Name = OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 );
+ sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() );
+
+ m_aChucker.WriteInt32( LOCSIG );
+ m_aChucker.WriteInt16( rEntry.nVersion );
+
+ m_aChucker.WriteInt16( rEntry.nFlag );
+ // If it's an encrypted entry, we pretend its stored plain text
+ if (bEncrypt)
+ m_aChucker.WriteInt16( STORED );
+ else
+ m_aChucker.WriteInt16( rEntry.nMethod );
+
+ bool bWrite64Header = false;
+
+ m_aChucker.WriteUInt32( rEntry.nTime );
+ if ((rEntry.nFlag & 8) == 8 )
+ {
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteInt32( 0 );
+ m_aChucker.WriteInt32( 0 );
+ }
+ else
+ {
+ m_aChucker.WriteUInt32( rEntry.nCrc );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
+ m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
+ }
+ m_aChucker.WriteInt16( nNameLength );
+ m_aChucker.WriteInt16( 0 );
+
+ if( bWrite64Header )
+ {
+ // FIXME64: need to append a ZIP64 header instead of throwing
+ // We're about to silently lose people's data - which they are
+ // unlikely to appreciate so fail instead:
+ throw IOException( "File contains streams that are too large." );
+ }
+
+ Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
+ m_aChucker.WriteBytes( aSequence );
+
+ m_pCurrentEntry->nOffset = m_aChucker.GetPosition() - (LOCHDR + nNameLength);
+}
+
+sal_uInt32 ZipOutputStream::getCurrentDosTime()
+{
+ oslDateTime aDateTime;
+ TimeValue aTimeValue;
+ osl_getSystemTime ( &aTimeValue );
+ osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime);
+
+ // at year 2108, there is an overflow
+ // -> some decision needs to be made
+ // how to handle the ZIP file format (just overflow?)
+
+ // if the current system time is before 1980,
+ // then the time traveller will have to make a decision
+ // how to handle the ZIP file format before it is invented
+ // (just underflow?)
+
+ assert(aDateTime.Year > 1980 && aDateTime.Year < 2108);
+
+ sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year);
+
+ if (nYear>=1980)
+ nYear-=1980;
+ else if (nYear>=80)
+ {
+ nYear-=80;
+ }
+ sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) +
+ ( 32 * (aDateTime.Month)) +
+ ( 512 * nYear ) ) << 16) |
+ ( ( aDateTime.Seconds/2) +
+ ( 32 * aDateTime.Minutes) +
+ ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) );
+ return nResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/blowfishcontext.cxx b/package/source/zipapi/blowfishcontext.cxx
new file mode 100644
index 000000000..1b5ed4a14
--- /dev/null
+++ b/package/source/zipapi/blowfishcontext.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <rtl/cipher.h>
+#include <rtl/ref.hxx>
+
+#include "blowfishcontext.hxx"
+
+using namespace ::com::sun::star;
+
+// static
+uno::Reference< xml::crypto::XCipherContext > BlowfishCFB8CipherContext::Create( const uno::Sequence< sal_Int8 >& aDerivedKey, const uno::Sequence< sal_Int8 >& aInitVector, bool bEncrypt )
+{
+ ::rtl::Reference< BlowfishCFB8CipherContext > xResult = new BlowfishCFB8CipherContext();
+ xResult->m_pCipher = rtl_cipher_create( rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
+ if ( !xResult->m_pCipher )
+ throw uno::RuntimeException("Can not create cipher!" );
+
+ if ( rtl_Cipher_E_None != rtl_cipher_init(
+ xResult->m_pCipher,
+ bEncrypt ? rtl_Cipher_DirectionEncode : rtl_Cipher_DirectionDecode,
+ reinterpret_cast< const sal_uInt8* >( aDerivedKey.getConstArray() ),
+ aDerivedKey.getLength(),
+ reinterpret_cast< const sal_uInt8* >( aInitVector.getConstArray() ),
+ aInitVector.getLength() ) )
+ {
+ throw uno::RuntimeException("Can not initialize cipher!" );
+ }
+
+ xResult->m_bEncrypt = bEncrypt;
+
+ return xResult;
+}
+
+BlowfishCFB8CipherContext::~BlowfishCFB8CipherContext()
+{
+ if ( m_pCipher )
+ {
+ rtl_cipher_destroy ( m_pCipher );
+ m_pCipher = nullptr;
+ }
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL BlowfishCFB8CipherContext::convertWithCipherContext( const uno::Sequence< ::sal_Int8 >& aData )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pCipher )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aResult( aData.getLength() );
+ rtlCipherError nError = rtl_Cipher_E_None;
+
+ if ( m_bEncrypt )
+ {
+ nError = rtl_cipher_encode( m_pCipher,
+ aData.getConstArray(),
+ aData.getLength(),
+ reinterpret_cast< sal_uInt8* >( aResult.getArray() ),
+ aResult.getLength() );
+ }
+ else
+ {
+ nError = rtl_cipher_decode( m_pCipher,
+ aData.getConstArray(),
+ aData.getLength(),
+ reinterpret_cast< sal_uInt8* >( aResult.getArray() ),
+ aResult.getLength() );
+ }
+
+ if ( rtl_Cipher_E_None != nError )
+ {
+ throw uno::RuntimeException("Can not decrypt/encrypt with cipher!" );
+ }
+
+ return aResult;
+}
+
+uno::Sequence< ::sal_Int8 > SAL_CALL BlowfishCFB8CipherContext::finalizeCipherContextAndDispose()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pCipher )
+ throw lang::DisposedException();
+
+ rtl_cipher_destroy ( m_pCipher );
+ m_pCipher = nullptr;
+
+ return uno::Sequence< sal_Int8 >();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/blowfishcontext.hxx b/package/source/zipapi/blowfishcontext.hxx
new file mode 100644
index 000000000..c0b603c15
--- /dev/null
+++ b/package/source/zipapi/blowfishcontext.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+
+#include <com/sun/star/xml/crypto/XCipherContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+
+class BlowfishCFB8CipherContext : public cppu::WeakImplHelper< css::xml::crypto::XCipherContext >
+{
+ std::mutex m_aMutex;
+ void* m_pCipher;
+ bool m_bEncrypt;
+
+ BlowfishCFB8CipherContext()
+ : m_pCipher( nullptr )
+ , m_bEncrypt( false )
+ {}
+
+public:
+
+ virtual ~BlowfishCFB8CipherContext() override;
+
+ static css::uno::Reference< css::xml::crypto::XCipherContext >
+ Create( const css::uno::Sequence< sal_Int8 >& aDerivedKey, const css::uno::Sequence< sal_Int8 >& aInitVector, bool bEncrypt );
+
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL convertWithCipherContext( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeCipherContextAndDispose( ) override;
+};
+
+#endif // INCLUDED_PACKAGE_SOURCE_ZIPAPI_BLOWFISHCONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/sha1context.cxx b/package/source/zipapi/sha1context.cxx
new file mode 100644
index 000000000..4b6cbd2d3
--- /dev/null
+++ b/package/source/zipapi/sha1context.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <rtl/digest.h>
+#include <rtl/ref.hxx>
+
+#include "sha1context.hxx"
+
+using namespace ::com::sun::star;
+
+// static
+uno::Reference<xml::crypto::XDigestContext> StarOfficeSHA1DigestContext::Create()
+{
+ ::rtl::Reference<StarOfficeSHA1DigestContext> xResult = new StarOfficeSHA1DigestContext();
+ xResult->m_pDigest = rtl_digest_createSHA1();
+ if ( !xResult->m_pDigest )
+ throw uno::RuntimeException("Can not create cipher!" );
+
+ return xResult;
+}
+
+StarOfficeSHA1DigestContext::~StarOfficeSHA1DigestContext()
+{
+ if ( m_pDigest )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+ }
+}
+
+void SAL_CALL StarOfficeSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& aData)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pDigest )
+ throw lang::DisposedException();
+
+ if ( rtl_Digest_E_None != rtl_digest_updateSHA1( m_pDigest, aData.getConstArray(), aData.getLength() ) )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ throw uno::RuntimeException();
+ }
+}
+
+uno::Sequence<::sal_Int8> SAL_CALL StarOfficeSHA1DigestContext::finalizeDigestAndDispose()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_pDigest )
+ throw lang::DisposedException();
+
+ uno::Sequence< sal_Int8 > aResult( RTL_DIGEST_LENGTH_SHA1 );
+ if ( rtl_Digest_E_None != rtl_digest_getSHA1( m_pDigest, reinterpret_cast< sal_uInt8* >( aResult.getArray() ), aResult.getLength() ) )
+ {
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ throw uno::RuntimeException();
+ }
+
+ rtl_digest_destroySHA1( m_pDigest );
+ m_pDigest = nullptr;
+
+ return aResult;
+}
+
+uno::Reference<xml::crypto::XDigestContext> CorrectSHA1DigestContext::Create()
+{
+ return new CorrectSHA1DigestContext();
+}
+
+CorrectSHA1DigestContext::CorrectSHA1DigestContext()
+{
+}
+
+CorrectSHA1DigestContext::~CorrectSHA1DigestContext()
+{
+}
+
+void SAL_CALL CorrectSHA1DigestContext::updateDigest(const uno::Sequence<::sal_Int8>& rData)
+{
+ std::scoped_lock aGuard(m_Mutex);
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ m_Hash.update(reinterpret_cast<unsigned char const*>(rData.getConstArray()), rData.getLength());
+}
+
+uno::Sequence<::sal_Int8> SAL_CALL CorrectSHA1DigestContext::finalizeDigestAndDispose()
+{
+ std::scoped_lock aGuard(m_Mutex);
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ m_bDisposed = true;
+ std::vector<unsigned char> const sha1(m_Hash.finalize());
+ return uno::Sequence<sal_Int8>(reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zipapi/sha1context.hxx b/package/source/zipapi/sha1context.hxx
new file mode 100644
index 000000000..6cc09da01
--- /dev/null
+++ b/package/source/zipapi/sha1context.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_PACKAGE_SOURCE_ZIPAPI_SHA1CONTEXT_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPAPI_SHA1CONTEXT_HXX
+
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+
+#include <comphelper/hash.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+
+class StarOfficeSHA1DigestContext
+ : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+ std::mutex m_aMutex;
+ void* m_pDigest;
+
+ StarOfficeSHA1DigestContext()
+ : m_pDigest( nullptr )
+ {}
+
+public:
+
+ virtual ~StarOfficeSHA1DigestContext() override;
+
+ static css::uno::Reference< css::xml::crypto::XDigestContext > Create();
+
+ virtual void SAL_CALL updateDigest( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL finalizeDigestAndDispose() override;
+
+};
+
+class CorrectSHA1DigestContext
+ : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+ std::mutex m_Mutex;
+ ::comphelper::Hash m_Hash{::comphelper::HashType::SHA1};
+ bool m_bDisposed{false};
+
+ CorrectSHA1DigestContext();
+
+public:
+
+ virtual ~CorrectSHA1DigestContext() override;
+
+ static css::uno::Reference<css::xml::crypto::XDigestContext> Create();
+
+ virtual void SAL_CALL updateDigest(const css::uno::Sequence<::sal_Int8>& rData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeDigestAndDispose() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */