summaryrefslogtreecommitdiffstats
path: root/package/source/zippackage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--package/source/zippackage/ZipPackage.cxx1837
-rw-r--r--package/source/zippackage/ZipPackageBuffer.cxx128
-rw-r--r--package/source/zippackage/ZipPackageEntry.cxx125
-rw-r--r--package/source/zippackage/ZipPackageFolder.cxx424
-rw-r--r--package/source/zippackage/ZipPackageFolderEnumeration.cxx70
-rw-r--r--package/source/zippackage/ZipPackageFolderEnumeration.hxx49
-rw-r--r--package/source/zippackage/ZipPackageSink.cxx37
-rw-r--r--package/source/zippackage/ZipPackageSink.hxx38
-rw-r--r--package/source/zippackage/ZipPackageStream.cxx1315
-rw-r--r--package/source/zippackage/wrapstreamforshare.cxx151
-rw-r--r--package/source/zippackage/wrapstreamforshare.hxx59
-rw-r--r--package/source/zippackage/zipfileaccess.cxx479
12 files changed, 4712 insertions, 0 deletions
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
new file mode 100644
index 000000000..9270bb705
--- /dev/null
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -0,0 +1,1837 @@
+/* -*- 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 <ZipPackage.hxx>
+#include "ZipPackageSink.hxx"
+#include <ZipEnumeration.hxx>
+#include <ZipPackageStream.hxx>
+#include <ZipPackageFolder.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageBuffer.hxx>
+#include <ZipFile.hxx>
+#include <PackageConstants.hxx>
+#include <com/sun/star/beans/PropertyValue.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/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <comphelper/fileurl.hxx>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/embed/UseBackupException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/random.h>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/io/XAsyncOutputMonitor.hpp>
+
+#include <string_view>
+
+#include <comphelper/seekableinput.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+
+using namespace osl;
+using namespace cppu;
+using namespace ucbhelper;
+using namespace com::sun::star;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::util;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::task;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::container;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::manifest;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+namespace {
+
+class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
+{
+ uno::Reference< XStream > mStream;
+public:
+
+ virtual uno::Reference< XStream > SAL_CALL getStream() override
+ { return mStream; }
+
+ virtual void SAL_CALL setStream( const uno::Reference< XStream >& stream ) override
+ { mStream = stream; }
+};
+
+class DummyInputStream : public ::cppu::WeakImplHelper< XInputStream >
+{
+ virtual sal_Int32 SAL_CALL readBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
+ { return 0; }
+
+ virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
+ { return 0; }
+
+ virtual void SAL_CALL skipBytes( sal_Int32 ) override
+ {}
+
+ virtual sal_Int32 SAL_CALL available() override
+ { return 0; }
+
+ virtual void SAL_CALL closeInput() override
+ {}
+};
+
+}
+
+ZipPackage::ZipPackage ( const uno::Reference < XComponentContext > &xContext )
+: m_aMutexHolder( new comphelper::RefCountedMutex )
+, m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
+, m_nChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
+, m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
+, m_bHasEncryptedEntries ( false )
+, m_bHasNonEncryptedEntries ( false )
+, m_bInconsistent ( false )
+, m_bForceRecovery ( false )
+, m_bMediaTypeFallbackUsed ( false )
+, m_nFormat( embed::StorageFormats::PACKAGE ) // package is the default format
+, m_bAllowRemoveOnInsert( true )
+, m_eMode ( e_IMode_None )
+, m_xContext( xContext )
+{
+ m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+}
+
+ZipPackage::~ZipPackage()
+{
+}
+
+bool ZipPackage::isLocalFile() const
+{
+ return comphelper::isFileUrl(m_aURL);
+}
+
+void ZipPackage::parseManifest()
+{
+ if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ return;
+
+ bool bManifestParsed = false;
+ static const OUStringLiteral sMeta (u"META-INF");
+ if ( m_xRootFolder->hasByName( sMeta ) )
+ {
+ try {
+ static const OUStringLiteral sManifest (u"manifest.xml");
+ uno::Reference< XUnoTunnel > xTunnel;
+ Any aAny = m_xRootFolder->getByName( sMeta );
+ aAny >>= xTunnel;
+ uno::Reference< XNameContainer > xMetaInfFolder( xTunnel, UNO_QUERY );
+ if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
+ {
+ aAny = xMetaInfFolder->getByName( sManifest );
+ aAny >>= xTunnel;
+ uno::Reference < XActiveDataSink > xSink ( xTunnel, UNO_QUERY );
+ if ( xSink.is() )
+ {
+ uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );
+
+ static const OUStringLiteral sPropFullPath (u"FullPath");
+ static const OUStringLiteral sPropVersion (u"Version");
+ static const OUStringLiteral sPropMediaType (u"MediaType");
+ static const OUStringLiteral sPropInitialisationVector (u"InitialisationVector");
+ static const OUStringLiteral sPropSalt (u"Salt");
+ static const OUStringLiteral sPropIterationCount (u"IterationCount");
+ static const OUStringLiteral sPropSize (u"Size");
+ static const OUStringLiteral sPropDigest (u"Digest");
+ static const OUStringLiteral sPropDerivedKeySize (u"DerivedKeySize");
+ static const OUStringLiteral sPropDigestAlgorithm (u"DigestAlgorithm");
+ static const OUStringLiteral sPropEncryptionAlgorithm (u"EncryptionAlgorithm");
+ static const OUStringLiteral sPropStartKeyAlgorithm (u"StartKeyAlgorithm");
+ static const OUStringLiteral sKeyInfo (u"KeyInfo");
+
+ const uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
+ const Any *pKeyInfo = nullptr;
+
+ for ( const uno::Sequence<PropertyValue>& rSequence : aManifestSequence )
+ {
+ OUString sPath, sMediaType, sVersion;
+ const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
+ for ( const PropertyValue& rValue : rSequence )
+ {
+ if ( rValue.Name == sPropFullPath )
+ rValue.Value >>= sPath;
+ else if ( rValue.Name == sPropVersion )
+ rValue.Value >>= sVersion;
+ else if ( rValue.Name == sPropMediaType )
+ rValue.Value >>= sMediaType;
+ else if ( rValue.Name == sPropSalt )
+ pSalt = &( rValue.Value );
+ else if ( rValue.Name == sPropInitialisationVector )
+ pVector = &( rValue.Value );
+ else if ( rValue.Name == sPropIterationCount )
+ pCount = &( rValue.Value );
+ else if ( rValue.Name == sPropSize )
+ pSize = &( rValue.Value );
+ else if ( rValue.Name == sPropDigest )
+ pDigest = &( rValue.Value );
+ else if ( rValue.Name == sPropDigestAlgorithm )
+ pDigestAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropEncryptionAlgorithm )
+ pEncryptionAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropStartKeyAlgorithm )
+ pStartKeyAlg = &( rValue.Value );
+ else if ( rValue.Name == sPropDerivedKeySize )
+ pDerivedKeySize = &( rValue.Value );
+ else if ( rValue.Name == sKeyInfo )
+ pKeyInfo = &( rValue.Value );
+ }
+
+ if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
+ {
+ aAny = getByHierarchicalName( sPath );
+ uno::Reference < XUnoTunnel > xUnoTunnel;
+ aAny >>= xUnoTunnel;
+ if (auto pFolder = comphelper::getFromUnoTunnel<ZipPackageFolder>(xUnoTunnel))
+ {
+ pFolder->SetMediaType ( sMediaType );
+ pFolder->SetVersion ( sVersion );
+ }
+ else if (auto pStream = comphelper::getFromUnoTunnel<ZipPackageStream>(xUnoTunnel))
+ {
+ pStream->SetMediaType ( sMediaType );
+ pStream->SetFromManifest( true );
+
+ if ( pKeyInfo && pVector && pSize && pDigest && pDigestAlg && pEncryptionAlg )
+ {
+ uno::Sequence < sal_Int8 > aSequence;
+ sal_Int64 nSize = 0;
+ sal_Int32 nDigestAlg = 0, nEncryptionAlg = 0;
+
+ pStream->SetToBeEncrypted ( true );
+
+ *pVector >>= aSequence;
+ pStream->setInitialisationVector ( aSequence );
+
+ *pSize >>= nSize;
+ pStream->setSize ( nSize );
+
+ *pDigest >>= aSequence;
+ pStream->setDigest ( aSequence );
+
+ *pDigestAlg >>= nDigestAlg;
+ pStream->SetImportedChecksumAlgorithm( nDigestAlg );
+
+ *pEncryptionAlg >>= nEncryptionAlg;
+ pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
+
+ *pKeyInfo >>= m_aGpgProps;
+
+ pStream->SetToBeCompressed ( true );
+ pStream->SetToBeEncrypted ( true );
+ pStream->SetIsEncrypted ( true );
+ pStream->setIterationCount(0);
+
+ // clamp to default SHA256 start key magic value,
+ // c.f. ZipPackageStream::GetEncryptionKey()
+ // trying to get key value from properties
+ const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
+ pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
+
+ if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
+ {
+ m_bHasEncryptedEntries = true;
+ m_nChecksumDigestID = nDigestAlg;
+ m_nCommonEncryptionID = nEncryptionAlg;
+ m_nStartKeyGenerationID = nStartKeyAlg;
+ }
+ }
+ else if ( pSalt && pVector && pCount && pSize && pDigest && pDigestAlg && pEncryptionAlg )
+ {
+ uno::Sequence < sal_Int8 > aSequence;
+ sal_Int64 nSize = 0;
+ sal_Int32 nCount = 0, nDigestAlg = 0, nEncryptionAlg = 0;
+ sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
+
+ pStream->SetToBeEncrypted ( true );
+
+ *pSalt >>= aSequence;
+ pStream->setSalt ( aSequence );
+
+ *pVector >>= aSequence;
+ pStream->setInitialisationVector ( aSequence );
+
+ *pCount >>= nCount;
+ pStream->setIterationCount ( nCount );
+
+ *pSize >>= nSize;
+ pStream->setSize ( nSize );
+
+ *pDigest >>= aSequence;
+ pStream->setDigest ( aSequence );
+
+ *pDigestAlg >>= nDigestAlg;
+ pStream->SetImportedChecksumAlgorithm( nDigestAlg );
+
+ *pEncryptionAlg >>= nEncryptionAlg;
+ pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
+
+ if ( pDerivedKeySize )
+ *pDerivedKeySize >>= nDerivedKeySize;
+ pStream->SetImportedDerivedKeySize( nDerivedKeySize );
+
+ if ( pStartKeyAlg )
+ *pStartKeyAlg >>= nStartKeyAlg;
+ pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
+
+ pStream->SetToBeCompressed ( true );
+ pStream->SetToBeEncrypted ( true );
+ pStream->SetIsEncrypted ( true );
+ if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
+ {
+ m_bHasEncryptedEntries = true;
+ m_nStartKeyGenerationID = nStartKeyAlg;
+ m_nChecksumDigestID = nDigestAlg;
+ m_nCommonEncryptionID = nEncryptionAlg;
+ }
+ }
+ else
+ m_bHasNonEncryptedEntries = true;
+ }
+ else
+ throw ZipIOException(THROW_WHERE "Wrong content");
+ }
+ }
+
+ bManifestParsed = true;
+ }
+
+ // now hide the manifest.xml file from user
+ xMetaInfFolder->removeByName( sManifest );
+ }
+ }
+ catch( Exception& )
+ {
+ if ( !m_bForceRecovery )
+ throw;
+ }
+ }
+
+ if ( !bManifestParsed && !m_bForceRecovery )
+ throw ZipIOException(
+ THROW_WHERE "Could not parse manifest.xml" );
+
+ static const OUStringLiteral sMimetype (u"mimetype");
+ if ( m_xRootFolder->hasByName( sMimetype ) )
+ {
+ // get mediatype from the "mimetype" stream
+ OUString aPackageMediatype;
+ uno::Reference< lang::XUnoTunnel > xMimeTypeTunnel;
+ m_xRootFolder->getByName( sMimetype ) >>= xMimeTypeTunnel;
+ uno::Reference < io::XActiveDataSink > xMimeSink( xMimeTypeTunnel, UNO_QUERY );
+ if ( xMimeSink.is() )
+ {
+ uno::Reference< io::XInputStream > xMimeInStream = xMimeSink->getInputStream();
+ if ( xMimeInStream.is() )
+ {
+ // Mediatypes longer than 1024 symbols should not appear here
+ uno::Sequence< sal_Int8 > aData( 1024 );
+ sal_Int32 nRead = xMimeInStream->readBytes( aData, 1024 );
+ if ( nRead > aData.getLength() )
+ nRead = aData.getLength();
+
+ if ( nRead )
+ aPackageMediatype = OUString( reinterpret_cast<char const *>(aData.getConstArray()), nRead, RTL_TEXTENCODING_ASCII_US );
+ }
+ }
+
+ if ( !bManifestParsed )
+ {
+ // the manifest.xml could not be successfully parsed, this is an inconsistent package
+ if ( aPackageMediatype.startsWith("application/vnd.") )
+ {
+ // accept only types that look similar to own mediatypes
+ m_xRootFolder->SetMediaType( aPackageMediatype );
+ m_bMediaTypeFallbackUsed = true;
+ }
+ }
+ else if ( !m_bForceRecovery )
+ {
+ // the mimetype stream should contain the information from manifest.xml
+ if ( m_xRootFolder->GetMediaType() != aPackageMediatype )
+ throw ZipIOException(
+ THROW_WHERE
+ "mimetype conflicts with manifest.xml, \""
+ + m_xRootFolder->GetMediaType() + "\" vs. \""
+ + aPackageMediatype + "\"" );
+ }
+
+ m_xRootFolder->removeByName( sMimetype );
+ }
+
+ m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams( std::u16string_view() );
+
+ bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 );
+ if ( !m_bForceRecovery && bODF12AndNewer )
+ {
+ if ( m_bInconsistent )
+ {
+ // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
+ // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
+ // should be checked later
+ throw ZipIOException(
+ THROW_WHERE "there are streams not referred in manifest.xml" );
+ }
+ // all the streams should be encrypted with the same StartKey in ODF1.2
+ // TODO/LATER: in future the exception should be thrown
+ // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
+ }
+
+ // in case it is a correct ODF1.2 document, the version must be set
+ // and the META-INF folder is reserved for package format
+ if ( bODF12AndNewer )
+ m_xRootFolder->removeByName( sMeta );
+}
+
+void ZipPackage::parseContentType()
+{
+ if ( m_nFormat != embed::StorageFormats::OFOPXML )
+ return;
+
+ try {
+ static const OUStringLiteral aContentTypes(u"[Content_Types].xml");
+ // the content type must exist in OFOPXML format!
+ if ( !m_xRootFolder->hasByName( aContentTypes ) )
+ throw io::IOException(THROW_WHERE "Wrong format!" );
+
+ uno::Reference< lang::XUnoTunnel > xTunnel;
+ uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
+ aAny >>= xTunnel;
+ uno::Reference < io::XActiveDataSink > xSink( xTunnel, UNO_QUERY );
+ if ( xSink.is() )
+ {
+ uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
+ if ( xInStream.is() )
+ {
+ // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
+ const uno::Sequence< uno::Sequence< beans::StringPair > > aContentTypeInfo =
+ ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream, m_xContext );
+
+ if ( aContentTypeInfo.getLength() != 2 )
+ throw io::IOException(THROW_WHERE );
+
+ // set the implicit types first
+ for ( const auto& rPair : aContentTypeInfo[0] )
+ m_xRootFolder->setChildStreamsTypeByExtension( rPair );
+
+ // now set the explicit types
+ for ( const auto& rPair : aContentTypeInfo[1] )
+ {
+ OUString aPath;
+ if ( rPair.First.toChar() == '/' )
+ aPath = rPair.First.copy( 1 );
+ else
+ aPath = rPair.First;
+
+ if ( !aPath.isEmpty() && hasByHierarchicalName( aPath ) )
+ {
+ uno::Any aIterAny = getByHierarchicalName( aPath );
+ uno::Reference < lang::XUnoTunnel > xIterTunnel;
+ aIterAny >>= xIterTunnel;
+ if (auto pStream = comphelper::getFromUnoTunnel<ZipPackageStream>(xIterTunnel))
+ {
+ // this is a package stream, in OFOPXML format only streams can have mediatype
+ pStream->SetMediaType( rPair.Second );
+ }
+ }
+ }
+ }
+ }
+
+ m_xRootFolder->removeByName( aContentTypes );
+ }
+ catch( uno::Exception& )
+ {
+ if ( !m_bForceRecovery )
+ throw;
+ }
+}
+
+void ZipPackage::getZipFileContents()
+{
+ ZipEnumeration aEnum = m_pZipFile->entries();
+ OUString sTemp, sDirName;
+ sal_Int32 nOldIndex, nStreamIndex;
+ FolderHash::iterator aIter;
+
+ while (aEnum.hasMoreElements())
+ {
+ nOldIndex = 0;
+ ZipPackageFolder* pCurrent = m_xRootFolder.get();
+ const ZipEntry & rEntry = *aEnum.nextElement();
+ OUString rName = rEntry.sPath;
+
+ if ( m_bForceRecovery )
+ {
+ // the PKZIP Application note version 6.2 does not allows to use '\' as separator
+ // unfortunately it is used by some implementations, so we have to support it in recovery mode
+ rName = rName.replace( '\\', '/' );
+ }
+
+ nStreamIndex = rName.lastIndexOf ( '/' );
+ if ( nStreamIndex != -1 )
+ {
+ sDirName = rName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ pCurrent = ( *aIter ).second;
+ }
+
+ if ( pCurrent == m_xRootFolder.get() )
+ {
+ sal_Int32 nIndex;
+ while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
+ {
+ sTemp = rName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( !pCurrent->hasByName( sTemp ) )
+ {
+ rtl::Reference<ZipPackageFolder> pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
+ pPkgFolder->setName( sTemp );
+ pPkgFolder->doSetParent( pCurrent );
+ pCurrent = pPkgFolder.get();
+ }
+ else
+ {
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ }
+ nOldIndex = nIndex+1;
+ }
+ if ( nStreamIndex != -1 && !sDirName.isEmpty() )
+ m_aRecent [ sDirName ] = pCurrent;
+ }
+ if ( rName.getLength() -1 != nStreamIndex )
+ {
+ nStreamIndex++;
+ sTemp = rName.copy( nStreamIndex );
+
+ if (!pCurrent->hasByName(sTemp))
+ {
+ rtl::Reference<ZipPackageStream> pPkgStream = new ZipPackageStream(*this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
+ pPkgStream->SetPackageMember(true);
+ pPkgStream->setZipEntryOnLoading(rEntry);
+ pPkgStream->setName(sTemp);
+ pPkgStream->doSetParent(pCurrent);
+ }
+ }
+ }
+
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ parseManifest();
+ else if ( m_nFormat == embed::StorageFormats::OFOPXML )
+ parseContentType();
+}
+
+void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
+{
+ beans::NamedValue aNamedValue;
+
+ if ( !aArguments.hasElements() )
+ return;
+
+ bool bHaveZipFile = true;
+
+ for( const auto& rArgument : aArguments )
+ {
+ OUString aParamUrl;
+ if ( rArgument >>= aParamUrl )
+ {
+ m_eMode = e_IMode_URL;
+ try
+ {
+ sal_Int32 nParam = aParamUrl.indexOf( '?' );
+ if ( nParam >= 0 )
+ {
+ m_aURL = aParamUrl.copy( 0, nParam );
+ std::u16string_view aParam = aParamUrl.subView( nParam + 1 );
+
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::u16string_view aCommand = o3tl::getToken(aParam, 0, '&', nIndex );
+ if ( aCommand == u"repairpackage" )
+ {
+ m_bForceRecovery = true;
+ break;
+ }
+ else if ( aCommand == u"purezip" )
+ {
+ m_nFormat = embed::StorageFormats::ZIP;
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ break;
+ }
+ else if ( aCommand == u"ofopxml" )
+ {
+ m_nFormat = embed::StorageFormats::OFOPXML;
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ break;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ else
+ m_aURL = aParamUrl;
+
+ Content aContent(
+ m_aURL, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+ Any aAny = aContent.getPropertyValue("Size");
+ sal_uInt64 aSize = 0;
+ // kind of optimization: treat empty files as nonexistent files
+ // and write to such files directly. Note that "Size" property is optional.
+ bool bHasSizeProperty = aAny >>= aSize;
+ if( !bHasSizeProperty || aSize )
+ {
+ uno::Reference < XActiveDataSink > xSink = new ZipPackageSink;
+ if ( aContent.openStream ( xSink ) )
+ m_xContentStream = xSink->getInputStream();
+ }
+ else
+ bHaveZipFile = false;
+ }
+ catch ( css::uno::Exception& )
+ {
+ // Exception derived from uno::Exception thrown. This probably
+ // means the file doesn't exist...we'll create it at
+ // commitChanges time
+ bHaveZipFile = false;
+ }
+ }
+ else if ( rArgument >>= m_xStream )
+ {
+ // a writable stream can implement both XStream & XInputStream
+ m_eMode = e_IMode_XStream;
+ m_xContentStream = m_xStream->getInputStream();
+ }
+ else if ( rArgument >>= m_xContentStream )
+ {
+ m_eMode = e_IMode_XInputStream;
+ }
+ else if ( rArgument >>= aNamedValue )
+ {
+ if ( aNamedValue.Name == "RepairPackage" )
+ aNamedValue.Value >>= m_bForceRecovery;
+ else if ( aNamedValue.Name == "PackageFormat" )
+ {
+ // setting this argument to true means Package format
+ // setting it to false means plain Zip format
+
+ bool bPackFormat = true;
+ aNamedValue.Value >>= bPackFormat;
+ if ( !bPackFormat )
+ m_nFormat = embed::StorageFormats::ZIP;
+
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ }
+ else if ( aNamedValue.Name == "StorageFormat" )
+ {
+ OUString aFormatName;
+ sal_Int32 nFormatID = 0;
+ if ( aNamedValue.Value >>= aFormatName )
+ {
+ if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::PACKAGE;
+ else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::ZIP;
+ else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
+ m_nFormat = embed::StorageFormats::OFOPXML;
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ }
+ else if ( aNamedValue.Value >>= nFormatID )
+ {
+ if (nFormatID != embed::StorageFormats::PACKAGE
+ && nFormatID != embed::StorageFormats::ZIP
+ && nFormatID != embed::StorageFormats::OFOPXML)
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ m_nFormat = nFormatID;
+ }
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ m_xRootFolder->setPackageFormat_Impl( m_nFormat );
+ }
+ else if ( aNamedValue.Name == "AllowRemoveOnInsert" )
+ {
+ aNamedValue.Value >>= m_bAllowRemoveOnInsert;
+ m_xRootFolder->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert );
+ }
+ else if (aNamedValue.Name == "NoFileSync")
+ aNamedValue.Value >>= m_bDisableFileSync;
+
+ // for now the progress handler is not used, probably it will never be
+ // if ( aNamedValue.Name == "ProgressHandler" )
+ }
+ else
+ {
+ // The URL is not acceptable
+ throw css::uno::Exception (THROW_WHERE "Bad arguments.",
+ static_cast < ::cppu::OWeakObject * > ( this ) );
+ }
+ }
+
+ try
+ {
+ if ( m_xContentStream.is() )
+ {
+ // the stream must be seekable, if it is not it will be wrapped
+ m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
+ m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW );
+ if ( !m_xContentSeek->getLength() )
+ bHaveZipFile = false;
+ }
+ else
+ bHaveZipFile = false;
+ }
+ catch ( css::uno::Exception& )
+ {
+ // Exception derived from uno::Exception thrown. This probably
+ // means the file doesn't exist...we'll create it at
+ // commitChanges time
+ bHaveZipFile = false;
+ }
+ if ( !bHaveZipFile )
+ return;
+
+ bool bBadZipFile = false;
+ OUString message;
+ try
+ {
+ m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, true, m_bForceRecovery);
+ getZipFileContents();
+ }
+ catch ( IOException & e )
+ {
+ bBadZipFile = true;
+ message = "IOException: " + e.Message;
+ }
+ catch ( ZipException & e )
+ {
+ bBadZipFile = true;
+ message = "ZipException: " + e.Message;
+ }
+ catch ( Exception & )
+ {
+ m_pZipFile.reset();
+ throw;
+ }
+
+ if ( bBadZipFile )
+ {
+ // clean up the memory, and tell the UCB about the error
+ m_pZipFile.reset();
+
+ throw css::packages::zip::ZipIOException (
+ THROW_WHERE "Bad Zip File, " + message,
+ static_cast < ::cppu::OWeakObject * > ( this ) );
+ }
+}
+
+Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
+{
+ OUString sTemp, sDirName;
+ sal_Int32 nOldIndex, nStreamIndex;
+ FolderHash::iterator aIter;
+
+ sal_Int32 nIndex = aName.getLength();
+
+ if (aName == "/")
+ // root directory.
+ return Any ( uno::Reference < XUnoTunnel > ( m_xRootFolder ) );
+
+ nStreamIndex = aName.lastIndexOf ( '/' );
+ bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
+
+ if ( nStreamIndex != -1 )
+ {
+ // The name contains '/'.
+ sDirName = aName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ {
+ // There is a cached entry for this name.
+
+ ZipPackageFolder* pFolder = aIter->second;
+
+ if ( bFolder )
+ {
+ // Determine the directory name.
+ sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
+ sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
+
+ if (pFolder && sTemp == pFolder->getName())
+ return Any(uno::Reference<XUnoTunnel>(pFolder));
+ }
+ else
+ {
+ // Determine the file name.
+ sTemp = aName.copy ( nStreamIndex + 1 );
+
+ if (pFolder && pFolder->hasByName(sTemp))
+ return pFolder->getByName(sTemp);
+ }
+
+ m_aRecent.erase( aIter );
+ }
+ }
+ else if ( m_xRootFolder->hasByName ( aName ) )
+ // top-level element.
+ return m_xRootFolder->getByName ( aName );
+
+ // Not in the cache. Search normally.
+
+ nOldIndex = 0;
+ ZipPackageFolder * pCurrent = m_xRootFolder.get();
+ ZipPackageFolder * pPrevious = nullptr;
+
+ // Find the right directory for the given path.
+
+ while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
+ {
+ sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( !pCurrent->hasByName( sTemp ) )
+ throw NoSuchElementException(THROW_WHERE );
+
+ pPrevious = pCurrent;
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ nOldIndex = nIndex+1;
+ }
+
+ if ( bFolder )
+ {
+ if ( nStreamIndex != -1 )
+ m_aRecent[sDirName] = pPrevious; // cache it.
+ return Any ( uno::Reference < XUnoTunnel > ( pCurrent ) );
+ }
+
+ sTemp = aName.copy( nOldIndex );
+
+ if ( pCurrent->hasByName ( sTemp ) )
+ {
+ if ( nStreamIndex != -1 )
+ m_aRecent[sDirName] = pCurrent; // cache it.
+ return pCurrent->getByName( sTemp );
+ }
+
+ throw NoSuchElementException(THROW_WHERE);
+}
+
+sal_Bool SAL_CALL ZipPackage::hasByHierarchicalName( const OUString& aName )
+{
+ OUString sTemp;
+ sal_Int32 nOldIndex;
+ FolderHash::iterator aIter;
+
+ sal_Int32 nIndex = aName.getLength();
+
+ if (aName == "/")
+ // root directory
+ return true;
+
+ try
+ {
+ OUString sDirName;
+ sal_Int32 nStreamIndex;
+ nStreamIndex = aName.lastIndexOf ( '/' );
+ bool bFolder = nStreamIndex == nIndex-1;
+ if ( nStreamIndex != -1 )
+ {
+ sDirName = aName.copy ( 0, nStreamIndex );
+ aIter = m_aRecent.find ( sDirName );
+ if ( aIter != m_aRecent.end() )
+ {
+ if ( bFolder )
+ {
+ sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
+ sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
+ if ( sTemp == ( *aIter ).second->getName() )
+ return true;
+ else
+ m_aRecent.erase ( aIter );
+ }
+ else
+ {
+ sTemp = aName.copy ( nStreamIndex + 1 );
+ if ( ( *aIter ).second->hasByName( sTemp ) )
+ return true;
+ else
+ m_aRecent.erase( aIter );
+ }
+ }
+ }
+ else
+ {
+ if ( m_xRootFolder->hasByName ( aName ) )
+ return true;
+ }
+ ZipPackageFolder * pCurrent = m_xRootFolder.get();
+ ZipPackageFolder * pPrevious = nullptr;
+ nOldIndex = 0;
+ while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
+ {
+ sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
+ if ( nIndex == nOldIndex )
+ break;
+ if ( pCurrent->hasByName( sTemp ) )
+ {
+ pPrevious = pCurrent;
+ ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
+ if (!rInfo.bFolder)
+ throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
+ pCurrent = rInfo.pFolder;
+ }
+ else
+ return false;
+ nOldIndex = nIndex+1;
+ }
+ if ( bFolder )
+ {
+ m_aRecent[sDirName] = pPrevious;
+ return true;
+ }
+ else
+ {
+ sTemp = aName.copy( nOldIndex );
+
+ if ( pCurrent->hasByName( sTemp ) )
+ {
+ m_aRecent[sDirName] = pCurrent;
+ return true;
+ }
+ }
+ }
+ catch (const uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ uno::Any e(::cppu::getCaughtException());
+ throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e);
+ }
+ return false;
+}
+
+uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
+{
+ uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
+ return xRef;
+}
+
+uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
+{
+ bool bArg = false;
+ uno::Reference < XInterface > xRef;
+ if ( aArguments.hasElements() )
+ aArguments[0] >>= bArg;
+ if ( bArg )
+ xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+ else
+ xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
+
+ return xRef;
+}
+
+void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream& aZipOut )
+{
+ static const OUStringLiteral sMime (u"mimetype");
+ if ( m_xRootFolder->hasByName( sMime ) )
+ m_xRootFolder->removeByName( sMime );
+
+ ZipEntry * pEntry = new ZipEntry;
+ sal_Int32 nBufferLength = m_xRootFolder->GetMediaType().getLength();
+ OString sMediaType = OUStringToOString( m_xRootFolder->GetMediaType(), RTL_TEXTENCODING_ASCII_US );
+ const uno::Sequence< sal_Int8 > aType( reinterpret_cast<sal_Int8 const *>(sMediaType.getStr()),
+ nBufferLength );
+
+ pEntry->sPath = sMime;
+ pEntry->nMethod = STORED;
+ pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ CRC32 aCRC32;
+ aCRC32.update( aType );
+ pEntry->nCrc = aCRC32.getValue();
+
+ try
+ {
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ aZipOut.rawWrite(aType);
+ aZipOut.rawCloseEntry();
+ }
+ catch ( const css::io::IOException & )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(
+ THROW_WHERE "Error adding mimetype to the ZipOutputStream!",
+ static_cast < OWeakObject * > ( this ),
+ anyEx );
+ }
+}
+
+void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
+{
+ // Write the manifest
+ uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
+ ZipEntry * pEntry = new ZipEntry;
+ rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
+
+ pEntry->sPath = "META-INF/manifest.xml";
+ pEntry->nMethod = DEFLATED;
+ pEntry->nCrc = -1;
+ pEntry->nSize = pEntry->nCompressedSize = -1;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ xWriter->writeManifestSequence ( pBuffer, comphelper::containerToSequence(aManList) );
+
+ sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
+ pBuffer->realloc( nBufferLength );
+
+ // the manifest.xml is never encrypted - so pass an empty reference
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
+ aZipEntry.write(pBuffer->getSequence());
+ aZipEntry.closeEntry();
+ aZipOut.rawCloseEntry();
+}
+
+void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
+{
+ ZipEntry* pEntry = new ZipEntry;
+ rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
+
+ pEntry->sPath = "[Content_Types].xml";
+ pEntry->nMethod = DEFLATED;
+ pEntry->nCrc = -1;
+ pEntry->nSize = pEntry->nCompressedSize = -1;
+ pEntry->nTime = ZipOutputStream::getCurrentDosTime();
+
+ // Add default entries, the count must be updated manually when appending.
+ // Add at least the standard default entries.
+ uno::Sequence< beans::StringPair > aDefaultsSequence
+ {
+ { "xml", "application/xml" },
+ { "rels", "application/vnd.openxmlformats-package.relationships+xml" },
+ { "png", "image/png" },
+ { "jpeg", "image/jpeg" }
+ };
+
+ uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
+ auto aOverridesSequenceRange = asNonConstRange(aOverridesSequence);
+ sal_Int32 nOverSeqLength = 0;
+ for (const auto& rMan : aManList)
+ {
+ OUString aType;
+ OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath",
+ "The mediatype sequence format is wrong!" );
+ rMan[PKG_MNFST_MEDIATYPE].Value >>= aType;
+ if ( !aType.isEmpty() )
+ {
+ OUString aPath;
+ // only nonempty type makes sense here
+ rMan[PKG_MNFST_FULLPATH].Value >>= aPath;
+ //FIXME: For now we have no way of differentiating defaults from others.
+ aOverridesSequenceRange[nOverSeqLength].First = "/" + aPath;
+ aOverridesSequenceRange[nOverSeqLength].Second = aType;
+ ++nOverSeqLength;
+ }
+ }
+ aOverridesSequence.realloc(nOverSeqLength);
+
+ ::comphelper::OFOPXMLHelper::WriteContentSequence(
+ pBuffer, aDefaultsSequence, aOverridesSequence, m_xContext );
+
+ sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
+ pBuffer->realloc( nBufferLength );
+
+ // there is no encryption in this format currently
+ ZipOutputStream::setEntry(pEntry);
+ aZipOut.writeLOC(pEntry);
+ ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
+ aZipEntry.write(pBuffer->getSequence());
+ aZipEntry.closeEntry();
+ aZipOut.rawCloseEntry();
+}
+
+void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
+{
+ m_xContentSeek.set( xInStream, uno::UNO_QUERY_THROW );
+ m_xContentStream = xInStream;
+
+ // seek back to the beginning of the temp file so we can read segments from it
+ m_xContentSeek->seek( 0 );
+ if ( m_pZipFile )
+ m_pZipFile->setInputStream( m_xContentStream );
+ else
+ m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, false);
+}
+
+namespace
+{
+ class RandomPool
+ {
+ private:
+ rtlRandomPool m_aRandomPool;
+ public:
+ RandomPool() : m_aRandomPool(rtl_random_createPool ())
+ {
+ }
+ rtlRandomPool get()
+ {
+ return m_aRandomPool;
+ }
+ ~RandomPool()
+ {
+ // Clean up random pool memory
+ rtl_random_destroyPool(m_aRandomPool);
+ }
+ };
+}
+
+uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
+{
+ // In case the target local file does not exist or empty
+ // write directly to it otherwise create a temporary file to write to.
+ // If a temporary file is created it is returned back by the method.
+ // If the data written directly, xComponentStream will be switched here
+
+ bool bUseTemp = true;
+ uno::Reference < io::XInputStream > xResult;
+ uno::Reference < io::XInputStream > xTempIn;
+
+ uno::Reference < io::XOutputStream > xTempOut;
+ uno::Reference< io::XActiveDataStreamer > xSink;
+
+ if ( m_eMode == e_IMode_URL && !m_pZipFile && isLocalFile() )
+ {
+ xSink = openOriginalForOutput();
+ if( xSink.is() )
+ {
+ uno::Reference< io::XStream > xStr = xSink->getStream();
+ if( xStr.is() )
+ {
+ xTempOut = xStr->getOutputStream();
+ if( xTempOut.is() )
+ bUseTemp = false;
+ }
+ }
+ }
+ else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
+ {
+ // write directly to an empty stream
+ xTempOut = m_xStream->getOutputStream();
+ if( xTempOut.is() )
+ bUseTemp = false;
+ }
+
+ if( bUseTemp )
+ {
+ // create temporary file
+ uno::Reference < io::XTempFile > xTempFile( io::TempFile::create(m_xContext) );
+ xTempOut.set( xTempFile->getOutputStream(), UNO_SET_THROW );
+ xTempIn.set( xTempFile->getInputStream(), UNO_SET_THROW );
+ }
+
+ // Hand it to the ZipOutputStream:
+ ZipOutputStream aZipOut( xTempOut );
+ try
+ {
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ // Remove the old manifest.xml file as the
+ // manifest will be re-generated and the
+ // META-INF directory implicitly created if does not exist
+ static constexpr OUStringLiteral sMeta (u"META-INF");
+
+ if ( m_xRootFolder->hasByName( sMeta ) )
+ {
+ static const OUStringLiteral sManifest (u"manifest.xml");
+
+ uno::Reference< XUnoTunnel > xTunnel;
+ Any aAny = m_xRootFolder->getByName( sMeta );
+ aAny >>= xTunnel;
+ uno::Reference< XNameContainer > xMetaInfFolder( xTunnel, UNO_QUERY );
+ if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
+ xMetaInfFolder->removeByName( sManifest );
+ }
+
+ // Write a magic file with mimetype
+ WriteMimetypeMagicFile( aZipOut );
+ }
+ else if ( m_nFormat == embed::StorageFormats::OFOPXML )
+ {
+ // Remove the old [Content_Types].xml file as the
+ // file will be re-generated
+
+ static constexpr OUStringLiteral aContentTypes(u"[Content_Types].xml");
+
+ if ( m_xRootFolder->hasByName( aContentTypes ) )
+ m_xRootFolder->removeByName( aContentTypes );
+ }
+
+ // Create a vector to store data for the manifest.xml file
+ std::vector < uno::Sequence < PropertyValue > > aManList;
+
+ static constexpr OUStringLiteral sMediaType(u"MediaType");
+ static constexpr OUStringLiteral sVersion(u"Version");
+ static constexpr OUStringLiteral sFullPath(u"FullPath");
+ const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
+
+ if ( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ uno::Sequence < PropertyValue > aPropSeq(
+ bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
+ auto pPropSeq = aPropSeq.getArray();
+ pPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
+ pPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
+ pPropSeq [PKG_MNFST_VERSION].Name = sVersion;
+ pPropSeq [PKG_MNFST_VERSION].Value <<= m_xRootFolder->GetVersion();
+ pPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
+ pPropSeq [PKG_MNFST_FULLPATH].Value <<= OUString("/");
+
+ if( bIsGpgEncrypt )
+ {
+ pPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
+ pPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
+ }
+ aManList.push_back( aPropSeq );
+ }
+
+ {
+ // This will be used to generate random salt and initialisation vectors
+ // for encrypted streams
+ RandomPool aRandomPool;
+
+ sal_Int32 const nPBKDF2IterationCount = 100000;
+
+ // call saveContents ( it will recursively save sub-directories
+ m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(), bIsGpgEncrypt ? 0 : nPBKDF2IterationCount, aRandomPool.get());
+ }
+
+ if( m_nFormat == embed::StorageFormats::PACKAGE )
+ {
+ WriteManifest( aZipOut, aManList );
+ }
+ else if( m_nFormat == embed::StorageFormats::OFOPXML )
+ {
+ WriteContentTypes( aZipOut, aManList );
+ }
+
+ aZipOut.finish();
+
+ if( bUseTemp )
+ xResult = xTempIn;
+
+ // Update our References to point to the new temp file
+ if( !bUseTemp )
+ {
+ // the case when the original contents were written directly
+ xTempOut->flush();
+
+ // in case the stream is based on a file it will implement the following interface
+ // the call should be used to be sure that the contents are written to the file system
+ uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY );
+ if (asyncOutputMonitor.is() && !m_bDisableFileSync)
+ asyncOutputMonitor->waitForCompletion();
+
+ // no need to postpone switching to the new stream since the target was written directly
+ uno::Reference< io::XInputStream > xNewStream;
+ if ( m_eMode == e_IMode_URL )
+ xNewStream = xSink->getStream()->getInputStream();
+ else if ( m_eMode == e_IMode_XStream && m_xStream.is() )
+ xNewStream = m_xStream->getInputStream();
+
+ if ( xNewStream.is() )
+ ConnectTo( xNewStream );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ if( bUseTemp )
+ {
+ // no information loss appears, thus no special handling is required
+ uno::Any aCaught( ::cppu::getCaughtException() );
+
+ // it is allowed to throw WrappedTargetException
+ WrappedTargetException aException;
+ if ( aCaught >>= aException )
+ throw aException;
+
+ throw WrappedTargetException(
+ THROW_WHERE "Problem writing the original content!",
+ static_cast < OWeakObject * > ( this ),
+ aCaught );
+ }
+ else
+ {
+ // the document is written directly, although it was empty it is important to notify that the writing has failed
+ // TODO/LATER: let the package be able to recover in this situation
+ OUString aErrTxt(THROW_WHERE "This package is unusable!");
+ embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() );
+ throw WrappedTargetException( aErrTxt,
+ static_cast < OWeakObject * > ( this ),
+ Any ( aException ) );
+ }
+ }
+
+ return xResult;
+}
+
+uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
+{
+ // open and truncate the original file
+ Content aOriginalContent(
+ m_aURL, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+ uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
+
+ if ( m_eMode == e_IMode_URL )
+ {
+ try
+ {
+ bool bTruncSuccess = false;
+
+ try
+ {
+ Exception aDetect;
+ Any aAny = aOriginalContent.setPropertyValue("Size", Any( sal_Int64(0) ) );
+ if( !( aAny >>= aDetect ) )
+ bTruncSuccess = true;
+ }
+ catch( Exception& )
+ {
+ }
+
+ if( !bTruncSuccess )
+ {
+ // the file is not accessible
+ // just try to write an empty stream to it
+
+ uno::Reference< XInputStream > xTempIn = new DummyInputStream; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
+ aOriginalContent.writeStream( xTempIn , true );
+ }
+
+ OpenCommandArgument2 aArg;
+ aArg.Mode = OpenMode::DOCUMENT;
+ aArg.Priority = 0; // unused
+ aArg.Sink = xSink;
+ aArg.Properties = uno::Sequence< Property >( 0 ); // unused
+
+ aOriginalContent.executeCommand("open", Any( aArg ) );
+ }
+ catch( Exception& )
+ {
+ // seems to be nonlocal file
+ // temporary file mechanics should be used
+ }
+ }
+
+ return xSink;
+}
+
+void SAL_CALL ZipPackage::commitChanges()
+{
+ // lock the component for the time of committing
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_eMode == e_IMode_XInputStream )
+ {
+ IOException aException;
+ throw WrappedTargetException(THROW_WHERE "This package is read only!",
+ static_cast < OWeakObject * > ( this ), Any ( aException ) );
+ }
+ // first the writeTempFile is called, if it returns a stream the stream should be written to the target
+ // if no stream was returned, the file was written directly, nothing should be done
+ uno::Reference< io::XInputStream > xTempInStream;
+ try
+ {
+ xTempInStream = writeTempFile();
+ }
+ catch (const ucb::ContentCreationException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be creatable!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+ if ( xTempInStream.is() )
+ {
+ uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
+
+ try
+ {
+ xTempSeek->seek( 0 );
+ }
+ catch( const uno::Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be seekable!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+
+ try
+ {
+ // connect to the temporary stream
+ ConnectTo( xTempInStream );
+ }
+ catch( const io::IOException& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+
+ if ( m_eMode == e_IMode_XStream )
+ {
+ // First truncate our output stream
+ uno::Reference < XOutputStream > xOutputStream;
+
+ // preparation for copy step
+ try
+ {
+ xOutputStream = m_xStream->getOutputStream();
+
+ // Make sure we avoid a situation where the current position is
+ // not zero, but the underlying file is truncated in the
+ // meantime.
+ uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
+ if (xSeekable.is())
+ xSeekable->seek(0);
+
+ uno::Reference < XTruncate > xTruncate ( xOutputStream, UNO_QUERY_THROW );
+
+ // after successful truncation the original file contents are already lost
+ xTruncate->truncate();
+ }
+ catch( const uno::Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(THROW_WHERE "This package is read only!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+
+ try
+ {
+ // then copy the contents of the tempfile to our output stream
+ ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
+ xOutputStream->flush();
+ uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
+ xOutputStream, uno::UNO_QUERY );
+ if ( asyncOutputMonitor.is() ) {
+ asyncOutputMonitor->waitForCompletion();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // if anything goes wrong in this block the target file becomes corrupted
+ // so an exception should be thrown as a notification about it
+ // and the package must disconnect from the stream
+ DisconnectFromTargetAndThrowException_Impl( xTempInStream );
+ }
+ }
+ else if ( m_eMode == e_IMode_URL )
+ {
+ uno::Reference< XOutputStream > aOrigFileStream;
+ bool bCanBeCorrupted = false;
+
+ if( isLocalFile() )
+ {
+ // write directly in case of local file
+ uno::Reference< css::ucb::XSimpleFileAccess3 > xSimpleAccess(
+ SimpleFileAccess::create( m_xContext ) );
+ OSL_ENSURE( xSimpleAccess.is(), "Can't instantiate SimpleFileAccess service!" );
+ uno::Reference< io::XTruncate > xOrigTruncate;
+ if ( xSimpleAccess.is() )
+ {
+ try
+ {
+ aOrigFileStream = xSimpleAccess->openFileWrite( m_aURL );
+ xOrigTruncate.set( aOrigFileStream, uno::UNO_QUERY_THROW );
+ // after successful truncation the file is already corrupted
+ xOrigTruncate->truncate();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if( xOrigTruncate.is() )
+ {
+ try
+ {
+ ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, aOrigFileStream );
+ aOrigFileStream->closeOutput();
+ }
+ catch( uno::Exception& )
+ {
+ try {
+ aOrigFileStream->closeOutput();
+ } catch ( uno::Exception& ) {}
+
+ aOrigFileStream.clear();
+ // the original file can already be corrupted
+ bCanBeCorrupted = true;
+ }
+ }
+ }
+
+ if( !aOrigFileStream.is() )
+ {
+ try
+ {
+ uno::Reference < XPropertySet > xPropSet ( xTempInStream, UNO_QUERY_THROW );
+
+ OUString sTargetFolder = m_aURL.copy ( 0, m_aURL.lastIndexOf ( u'/' ) );
+ Content aContent(
+ sTargetFolder, uno::Reference< XCommandEnvironment >(),
+ m_xContext );
+
+ OUString sTempURL;
+ Any aAny = xPropSet->getPropertyValue ("Uri");
+ aAny >>= sTempURL;
+
+ TransferInfo aInfo;
+ aInfo.NameClash = NameClash::OVERWRITE;
+ aInfo.MoveData = false;
+ aInfo.SourceURL = sTempURL;
+ aInfo.NewTitle = rtl::Uri::decode ( m_aURL.copy ( 1 + m_aURL.lastIndexOf ( u'/' ) ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 );
+ // if the file is still not corrupted, it can become after the next step
+ aContent.executeCommand ("transfer", Any(aInfo) );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ if ( bCanBeCorrupted )
+ DisconnectFromTargetAndThrowException_Impl( xTempInStream );
+
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException(
+ THROW_WHERE "This package may be read only!",
+ static_cast < OWeakObject * > ( this ),
+ anyEx );
+ }
+ }
+ }
+ }
+
+ // after successful storing it can be set to false
+ m_bMediaTypeFallbackUsed = false;
+}
+
+void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
+{
+ m_xStream.set( xTempStream, uno::UNO_QUERY );
+ if ( m_xStream.is() )
+ m_eMode = e_IMode_XStream;
+ else
+ m_eMode = e_IMode_XInputStream;
+
+ OUString aTempURL;
+ try {
+ uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
+ uno::Any aUrl = xTempFile->getPropertyValue("Uri");
+ aUrl >>= aTempURL;
+ xTempFile->setPropertyValue("RemoveFile",
+ uno::Any( false ) );
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "These calls are pretty simple, they should not fail!" );
+ }
+
+ OUString aErrTxt(THROW_WHERE "This package is read only!");
+ embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
+ throw WrappedTargetException( aErrTxt,
+ static_cast < OWeakObject * > ( this ),
+ Any ( aException ) );
+}
+
+uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey()
+{
+ uno::Sequence< sal_Int8 > aResult;
+
+ if ( m_aStorageEncryptionKeys.hasElements() )
+ {
+ OUString aNameToFind;
+ if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
+ else
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+
+ for ( const auto& rKey : std::as_const(m_aStorageEncryptionKeys) )
+ if ( rKey.Name == aNameToFind )
+ rKey.Value >>= aResult;
+ }
+ else
+ aResult = m_aEncryptionKey;
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
+{
+ return false;
+}
+Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
+{
+ return uno::Sequence < ElementChange > ();
+}
+
+
+OUString ZipPackage::getImplementationName()
+{
+ return "com.sun.star.packages.comp.ZipPackage";
+}
+
+Sequence< OUString > ZipPackage::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.Package" };
+}
+
+sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+const Sequence< sal_Int8 > & ZipPackage::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit implId;
+ return implId.getSeq();
+}
+
+sal_Int64 SAL_CALL ZipPackage::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
+{
+ return uno::Reference < XPropertySetInfo > ();
+}
+
+void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ throw UnknownPropertyException(aPropertyName);
+
+ if (aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
+ ||aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
+ ||aPropertyName == IS_INCONSISTENT_PROPERTY
+ ||aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY)
+ throw PropertyVetoException(THROW_WHERE );
+ else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ if ( !( aValue >>= m_aEncryptionKey ) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+ else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ // this property is only necessary to support raw passwords in storage API;
+ // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
+ // when this support is removed, the storage will get only one key from outside
+ if ( !( aValue >>= m_aStorageEncryptionKeys ) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
+
+ m_aEncryptionKey.realloc( 0 );
+ }
+ else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
+ {
+ uno::Sequence< beans::NamedValue > aAlgorithms;
+ if ( m_pZipFile || !( aValue >>= aAlgorithms ) || !aAlgorithms.hasElements() )
+ {
+ // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
+ throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+
+ for ( const auto& rAlgorithm : std::as_const(aAlgorithms) )
+ {
+ if ( rAlgorithm.Name == "StartKeyGenerationAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if ( !( rAlgorithm.Value >>= nID )
+ || ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) )
+ throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
+
+ m_nStartKeyGenerationID = nID;
+ }
+ else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if ( !( rAlgorithm.Value >>= nID )
+ || ( nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING && nID != xml::crypto::CipherID::BLOWFISH_CFB_8 ) )
+ throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
+
+ m_nCommonEncryptionID = nID;
+ }
+ else if ( rAlgorithm.Name == "ChecksumAlgorithm" )
+ {
+ sal_Int32 nID = 0;
+ if ( !( rAlgorithm.Value >>= nID )
+ || ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) )
+ throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
+
+ m_nChecksumDigestID = nID;
+ }
+ else
+ {
+ OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
+ throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+ }
+ }
+ else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
+ {
+ uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
+ if ( !( aValue >>= aGpgProps ) || !aGpgProps.hasElements() )
+ {
+ throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+
+ m_aGpgProps = aGpgProps;
+
+ // override algorithm defaults (which are some legacy ODF
+ // defaults) with reasonable values
+ m_nStartKeyGenerationID = 0; // this is unused for PGP
+ m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ m_nChecksumDigestID = xml::crypto::DigestID::SHA512_1K;
+ }
+ else
+ throw UnknownPropertyException(aPropertyName);
+}
+
+Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
+{
+ // TODO/LATER: Activate the check when zip-ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ return Any(m_aEncryptionKey);
+ }
+ else if ( PropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
+ {
+ ::comphelper::SequenceAsHashMap aAlgorithms;
+ aAlgorithms["StartKeyGenerationAlgorithm"] <<= m_nStartKeyGenerationID;
+ aAlgorithms["EncryptionAlgorithm"] <<= m_nCommonEncryptionID;
+ aAlgorithms["ChecksumAlgorithm"] <<= m_nChecksumDigestID;
+ return Any(aAlgorithms.getAsConstNamedValueList());
+ }
+ if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ return Any(m_aStorageEncryptionKeys);
+ }
+ else if ( PropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY )
+ {
+ return Any(m_bHasEncryptedEntries);
+ }
+ else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
+ {
+ return Any(m_aGpgProps);
+ }
+ else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
+ {
+ return Any(m_bHasNonEncryptedEntries);
+ }
+ else if ( PropertyName == IS_INCONSISTENT_PROPERTY )
+ {
+ return Any(m_bInconsistent);
+ }
+ else if ( PropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
+ {
+ return Any(m_bMediaTypeFallbackUsed);
+ }
+ throw UnknownPropertyException(PropertyName);
+}
+void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+void SAL_CALL ZipPackage::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackage::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackage::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_ZipPackage_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ZipPackage(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageBuffer.cxx b/package/source/zippackage/ZipPackageBuffer.cxx
new file mode 100644
index 000000000..810daa737
--- /dev/null
+++ b/package/source/zippackage/ZipPackageBuffer.cxx
@@ -0,0 +1,128 @@
+/* -*- 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 <ZipPackageBuffer.hxx>
+#include <PackageConstants.hxx>
+#include <string.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using com::sun::star::lang::IllegalArgumentException;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageBuffer::ZipPackageBuffer()
+: m_nBufferSize (n_ConstBufferSize)
+, m_nEnd(0)
+, m_nCurrent(0)
+, m_bMustInitBuffer ( true )
+{
+}
+ZipPackageBuffer::~ZipPackageBuffer()
+{
+}
+
+sal_Int32 SAL_CALL ZipPackageBuffer::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ if (nBytesToRead < 0)
+ throw BufferSizeExceededException(THROW_WHERE, *this );
+
+ if (nBytesToRead + m_nCurrent > m_nEnd)
+ nBytesToRead = static_cast < sal_Int32 > (m_nEnd - m_nCurrent);
+
+ aData.realloc ( nBytesToRead );
+ memcpy(aData.getArray(), m_aBuffer.getConstArray() + m_nCurrent, nBytesToRead);
+ m_nCurrent +=nBytesToRead;
+ return nBytesToRead;
+}
+
+sal_Int32 SAL_CALL ZipPackageBuffer::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes(aData, nMaxBytesToRead);
+}
+void SAL_CALL ZipPackageBuffer::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if (nBytesToSkip < 0)
+ throw BufferSizeExceededException(THROW_WHERE, *this );
+
+ if (nBytesToSkip + m_nCurrent > m_nEnd)
+ nBytesToSkip = static_cast < sal_Int32 > (m_nEnd - m_nCurrent);
+
+ m_nCurrent+=nBytesToSkip;
+}
+sal_Int32 SAL_CALL ZipPackageBuffer::available( )
+{
+ return std::min<sal_Int64>(SAL_MAX_INT32, m_nEnd - m_nCurrent);
+}
+void SAL_CALL ZipPackageBuffer::closeInput( )
+{
+}
+void SAL_CALL ZipPackageBuffer::writeBytes( const Sequence< sal_Int8 >& aData )
+{
+ sal_Int64 nDataLen = aData.getLength(), nCombined = m_nEnd + nDataLen;
+
+ if ( nCombined > m_nBufferSize)
+ {
+ do
+ m_nBufferSize *=2;
+ while (nCombined > m_nBufferSize);
+ m_aBuffer.realloc(static_cast < sal_Int32 > (m_nBufferSize));
+ m_bMustInitBuffer = false;
+ }
+ else if (m_bMustInitBuffer)
+ {
+ m_aBuffer.realloc ( static_cast < sal_Int32 > ( m_nBufferSize ) );
+ m_bMustInitBuffer = false;
+ }
+ memcpy( m_aBuffer.getArray() + m_nCurrent, aData.getConstArray(), static_cast < sal_Int32 > (nDataLen));
+ m_nCurrent+=nDataLen;
+ if (m_nCurrent>m_nEnd)
+ m_nEnd = m_nCurrent;
+}
+void SAL_CALL ZipPackageBuffer::flush( )
+{
+}
+void SAL_CALL ZipPackageBuffer::closeOutput( )
+{
+}
+void SAL_CALL ZipPackageBuffer::seek( sal_Int64 location )
+{
+ if ( location > m_nEnd || location < 0 )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+ m_nCurrent = location;
+}
+sal_Int64 SAL_CALL ZipPackageBuffer::getPosition( )
+{
+ return m_nCurrent;
+}
+sal_Int64 SAL_CALL ZipPackageBuffer::getLength( )
+{
+ return m_nEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageEntry.cxx b/package/source/zippackage/ZipPackageEntry.cxx
new file mode 100644
index 000000000..df18f0874
--- /dev/null
+++ b/package/source/zippackage/ZipPackageEntry.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 <ZipPackageEntry.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <ZipPackageFolder.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages::zip::ZipConstants;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageEntry::ZipPackageEntry()
+: mbIsFolder( false )
+, mbAllowRemoveOnInsert(false)
+, mpParent ( nullptr )
+, m_nFormat(0)
+{
+}
+
+ZipPackageEntry::~ZipPackageEntry()
+{
+ // When the entry is destroyed it must be already disconnected from the parent
+ OSL_ENSURE( !mpParent, "The parent must be disconnected already! Memory corruption is possible!" );
+}
+
+// XChild
+OUString SAL_CALL ZipPackageEntry::getName( )
+{
+ return msName;
+}
+void SAL_CALL ZipPackageEntry::setName( const OUString& aName )
+{
+ if ( mpParent && !msName.isEmpty() && mpParent->hasByName ( msName ) )
+ mpParent->removeByName ( msName );
+
+ // unfortunately no other exception than RuntimeException can be thrown here
+ // usually the package is used through storage implementation, the problem should be detected there
+ if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aName, true ) )
+ throw RuntimeException(THROW_WHERE "Unexpected character is used in file name." );
+
+ msName = aName;
+
+ if ( mpParent )
+ mpParent->doInsertByName ( this, false );
+}
+uno::Reference< XInterface > SAL_CALL ZipPackageEntry::getParent( )
+{
+ // return uno::Reference< XInterface >( xParent, UNO_QUERY );
+ return uno::Reference< XInterface >( static_cast< ::cppu::OWeakObject* >( mpParent ), UNO_QUERY );
+}
+
+void ZipPackageEntry::doSetParent ( ZipPackageFolder * pNewParent )
+{
+ // xParent = mpParent = pNewParent;
+ mpParent = pNewParent;
+ if ( !msName.isEmpty() && !pNewParent->hasByName ( msName ) )
+ pNewParent->doInsertByName ( this, false );
+}
+
+void SAL_CALL ZipPackageEntry::setParent( const uno::Reference< XInterface >& xNewParent )
+{
+ if ( !xNewParent.is() )
+ throw NoSupportException(THROW_WHERE );
+ ZipPackageFolder* pNewParent = comphelper::getFromUnoTunnel<ZipPackageFolder>(xNewParent);
+ if (!pNewParent)
+ throw NoSupportException(THROW_WHERE );
+
+ if ( pNewParent != mpParent )
+ {
+ if ( mpParent && !msName.isEmpty() && mpParent->hasByName ( msName ) && mbAllowRemoveOnInsert )
+ mpParent->removeByName( msName );
+ doSetParent ( pNewParent );
+ }
+}
+ //XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ZipPackageEntry::getPropertySetInfo( )
+{
+ return uno::Reference < beans::XPropertySetInfo > ();
+}
+void SAL_CALL ZipPackageEntry::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+void SAL_CALL ZipPackageEntry::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolder.cxx b/package/source/zippackage/ZipPackageFolder.cxx
new file mode 100644
index 000000000..7e7168805
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolder.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 <ZipPackageFolder.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageStream.hxx>
+#include <PackageConstants.hxx>
+#include "ZipPackageFolderEnumeration.hxx"
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace com::sun::star;
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::io;
+using namespace cppu;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert )
+{
+ m_xContext = xContext;
+ m_nFormat = nFormat;
+ mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
+ SetFolder ( true );
+ aEntry.nVersion = -1;
+ aEntry.nFlag = 0;
+ aEntry.nMethod = STORED;
+ aEntry.nTime = -1;
+ aEntry.nCrc = 0;
+ aEntry.nCompressedSize = 0;
+ aEntry.nSize = 0;
+ aEntry.nOffset = -1;
+}
+
+ZipPackageFolder::~ZipPackageFolder()
+{
+}
+
+bool ZipPackageFolder::LookForUnexpectedODF12Streams( std::u16string_view aPath )
+{
+ bool bHasUnexpected = false;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ // META-INF is not allowed to contain subfolders
+ bHasUnexpected = true;
+ }
+ else
+ {
+ OUString sOwnPath = aPath + rShortName + "/";
+ bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams( sOwnPath );
+ }
+ }
+ else
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ if ( rShortName != "manifest.xml"
+ && rShortName.indexOf( "signatures" ) == -1 )
+ {
+ // a stream from META-INF with unexpected name
+ bHasUnexpected = true;
+ }
+
+ // streams from META-INF with expected names are allowed not to be registered in manifest.xml
+ }
+ else if ( !rInfo.pStream->IsFromManifest() )
+ {
+ // the stream is not in META-INF and is not registered in manifest.xml,
+ // check whether it is an internal part of the package format
+ if ( !aPath.empty() || rShortName != "mimetype" )
+ {
+ // if it is not "mimetype" from the root it is not a part of the package
+ bHasUnexpected = true;
+ }
+ }
+ }
+
+ if (bHasUnexpected)
+ break;
+ }
+
+ return bHasUnexpected;
+}
+
+void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
+{
+ OUString aExt;
+ if ( aPair.First.toChar() == '.' )
+ aExt = aPair.First;
+ else
+ aExt = "." + aPair.First;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
+ else
+ {
+ sal_Int32 nPathLength = rShortName.getLength();
+ sal_Int32 nExtLength = aExt.getLength();
+ if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
+ rInfo.pStream->SetMediaType( aPair.Second );
+ }
+ }
+}
+
+const css::uno::Sequence < sal_Int8 > & ZipPackageFolder::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit lcl_CachedImplId;
+ return lcl_CachedImplId.getSeq();
+}
+
+ // XNameContainer
+void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if (hasByName(aName))
+ throw ElementExistException(THROW_WHERE );
+
+ uno::Reference < XUnoTunnel > xRef;
+ aElement >>= xRef;
+ if ( !(aElement >>= xRef) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ ZipPackageEntry* pEntry = comphelper::getFromUnoTunnel<ZipPackageFolder>(xRef);
+ if (!pEntry)
+ pEntry = comphelper::getFromUnoTunnel<ZipPackageStream>(xRef);
+ if (!pEntry)
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ if (pEntry->getName() != aName )
+ pEntry->setName (aName);
+ doInsertByName ( pEntry, true );
+}
+
+void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
+{
+ ContentHash::iterator aIter = maContents.find ( Name );
+ if ( aIter == maContents.end() )
+ throw NoSuchElementException(THROW_WHERE );
+ maContents.erase( aIter );
+}
+ // XEnumerationAccess
+uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
+{
+ return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
+}
+ // XElementAccess
+uno::Type SAL_CALL ZipPackageFolder::getElementType( )
+{
+ return cppu::UnoType<XUnoTunnel>::get();
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
+{
+ return !maContents.empty();
+}
+ // XNameAccess
+ZipContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
+{
+ ContentHash::iterator aIter = maContents.find ( aName );
+ if ( aIter == maContents.end())
+ throw NoSuchElementException(THROW_WHERE );
+ return aIter->second;
+}
+
+uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
+{
+ return uno::Any ( doGetByName ( aName ).xTunnel );
+}
+uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(maContents);
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
+{
+ return maContents.find ( aName ) != maContents.end ();
+}
+ // XNameReplace
+void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ if ( !hasByName( aName ) )
+ throw NoSuchElementException(THROW_WHERE );
+
+ removeByName( aName );
+ insertByName(aName, aElement);
+}
+
+bool ZipPackageFolder::saveChild(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
+ const rtlRandomPool &rRandomPool)
+{
+ uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
+ OUString sTempName = rPath + "/";
+
+ if ( !GetMediaType().isEmpty() )
+ {
+ auto pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
+ pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType();
+ pPropSet[PKG_MNFST_VERSION].Name = "Version";
+ pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion();
+ pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
+ pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
+ }
+ else
+ aPropSet.realloc( 0 );
+
+ saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
+
+ // folder can have a mediatype only in package format
+ if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
+ rManList.push_back( aPropSet );
+
+ return true;
+}
+
+void ZipPackageFolder::saveContents(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
+ const rtlRandomPool &rRandomPool ) const
+{
+ if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
+ {
+ // it is an empty subfolder, use workaround to store it
+ ZipEntry* pTempEntry = new ZipEntry(aEntry);
+ pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
+ pTempEntry->nExtraLen = -1;
+ pTempEntry->sPath = rPath;
+
+ try
+ {
+ ZipOutputStream::setEntry(pTempEntry);
+ rZipOut.writeLOC(pTempEntry);
+ rZipOut.rawCloseEntry();
+ }
+ catch ( ZipException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ catch ( IOException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+
+ bool bMimeTypeStreamStored = false;
+ OUString aMimeTypeStreamName("mimetype");
+ if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
+ {
+ // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
+ ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName );
+ if ( aIter != maContents.end() && !(*aIter).second.bFolder )
+ {
+ bMimeTypeStreamStored = true;
+ if( !aIter->second.pStream->saveChild(
+ rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
+ {
+ if (rInfo.bFolder)
+ {
+ if( !rInfo.pFolder->saveChild(
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ else
+ {
+ if( !rInfo.pStream->saveChild(
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+ }
+}
+
+sal_Int64 SAL_CALL ZipPackageFolder::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ aValue >>= msMediaType;
+ }
+ else if ( aPropertyName == "Version" )
+ aValue >>= m_sVersion;
+ else if ( aPropertyName == "Size" )
+ aValue >>= aEntry.nSize;
+ else
+ throw UnknownPropertyException(aPropertyName);
+}
+uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ return uno::Any ( msMediaType );
+ }
+ else if ( PropertyName == "Version" )
+ return uno::Any( m_sVersion );
+ else if ( PropertyName == "Size" )
+ return uno::Any ( aEntry.nSize );
+ else
+ throw UnknownPropertyException(PropertyName);
+}
+
+void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
+{
+ if ( pEntry->IsFolder() )
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry)));
+ else
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry)));
+ if ( bSetParent )
+ pEntry->setParent ( *this );
+}
+
+OUString ZipPackageFolder::getImplementationName()
+{
+ return "ZipPackageFolder";
+}
+
+uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.PackageFolder" };
+}
+
+sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+
+ZipContentInfo::ZipContentInfo ( ZipPackageStream * pNewStream )
+: xTunnel ( pNewStream )
+, bFolder ( false )
+, pStream ( pNewStream )
+{
+}
+
+ZipContentInfo::ZipContentInfo ( ZipPackageFolder * pNewFolder )
+: xTunnel ( pNewFolder )
+, bFolder ( true )
+, pFolder ( pNewFolder )
+{
+}
+
+ZipContentInfo::ZipContentInfo( const ZipContentInfo& ) = default;
+ZipContentInfo::ZipContentInfo( ZipContentInfo&& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( const ZipContentInfo& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( ZipContentInfo&& ) = default;
+
+ZipContentInfo::~ZipContentInfo()
+{
+ if ( bFolder )
+ pFolder->clearParent();
+ else
+ pStream->clearParent();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolderEnumeration.cxx b/package/source/zippackage/ZipPackageFolderEnumeration.cxx
new file mode 100644
index 000000000..7b08e1890
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolderEnumeration.cxx
@@ -0,0 +1,70 @@
+/* -*- 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 "ZipPackageFolderEnumeration.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageFolderEnumeration::ZipPackageFolderEnumeration(ContentHash& rInput)
+ : rContents(rInput)
+ , aIterator(rContents.begin())
+{
+}
+
+ZipPackageFolderEnumeration::~ZipPackageFolderEnumeration() {}
+
+sal_Bool SAL_CALL ZipPackageFolderEnumeration::hasMoreElements()
+{
+ return (aIterator != rContents.end());
+}
+uno::Any SAL_CALL ZipPackageFolderEnumeration::nextElement()
+{
+ uno::Any aAny;
+ if (aIterator == rContents.end())
+ throw container::NoSuchElementException(THROW_WHERE);
+ aAny <<= (*aIterator).second.xTunnel;
+ ++aIterator;
+ return aAny;
+}
+
+OUString ZipPackageFolderEnumeration::getImplementationName()
+{
+ return "ZipPackageFolderEnumeration";
+}
+
+uno::Sequence<OUString> ZipPackageFolderEnumeration::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aNames{ "com.sun.star.packages.PackageFolderEnumeration" };
+ return aNames;
+}
+
+sal_Bool SAL_CALL ZipPackageFolderEnumeration::supportsService(OUString const& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageFolderEnumeration.hxx b/package/source/zippackage/ZipPackageFolderEnumeration.hxx
new file mode 100644
index 000000000..f3b805b2a
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolderEnumeration.hxx
@@ -0,0 +1,49 @@
+/* -*- 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_ZIPPACKAGE_ZIPPACKAGEFOLDERENUMERATION_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGEFOLDERENUMERATION_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <ZipPackageFolder.hxx>
+
+class ZipPackageFolderEnumeration final
+ : public cppu::WeakImplHelper<css::container::XEnumeration, css::lang::XServiceInfo>
+{
+ ContentHash& rContents;
+ ContentHash::const_iterator aIterator;
+
+public:
+ //ZipPackageFolderEnumeration (unordered_map < OUString, css::uno::Reference < css::container::XNamed >, hashFunc, eqFunc > &rInput);
+ ZipPackageFolderEnumeration(ContentHash& rInput);
+ virtual ~ZipPackageFolderEnumeration() override;
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageSink.cxx b/package/source/zippackage/ZipPackageSink.cxx
new file mode 100644
index 000000000..103af9d6e
--- /dev/null
+++ b/package/source/zippackage/ZipPackageSink.cxx
@@ -0,0 +1,37 @@
+/* -*- 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 "ZipPackageSink.hxx"
+
+ZipPackageSink::ZipPackageSink()
+ : xStream(css::uno::Reference<css::io::XInputStream>(nullptr))
+{
+}
+ZipPackageSink::~ZipPackageSink() {}
+void SAL_CALL
+ZipPackageSink::setInputStream(const css::uno::Reference<css::io::XInputStream>& aStream)
+{
+ xStream = aStream;
+}
+css::uno::Reference<css::io::XInputStream> SAL_CALL ZipPackageSink::getInputStream()
+{
+ return xStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageSink.hxx b/package/source/zippackage/ZipPackageSink.hxx
new file mode 100644
index 000000000..9bc406c89
--- /dev/null
+++ b/package/source/zippackage/ZipPackageSink.hxx
@@ -0,0 +1,38 @@
+/* -*- 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_ZIPPACKAGE_ZIPPACKAGESINK_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_ZIPPACKAGESINK_HXX
+
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class ZipPackageSink final : public ::cppu::WeakImplHelper<css::io::XActiveDataSink>
+{
+ css::uno::Reference<css::io::XInputStream> xStream;
+
+public:
+ ZipPackageSink();
+ virtual ~ZipPackageSink() override;
+ virtual void SAL_CALL
+ setInputStream(const css::uno::Reference<css::io::XInputStream>& aStream) override;
+ virtual css::uno::Reference<css::io::XInputStream> SAL_CALL getInputStream() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx
new file mode 100644
index 000000000..b63bf24ea
--- /dev/null
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -0,0 +1,1315 @@
+/* -*- 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 <ZipPackageStream.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/packages/NoRawFormatException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+
+#include <CRC32.hxx>
+#include <ZipOutputEntry.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackage.hxx>
+#include <ZipFile.hxx>
+#include <EncryptedDataHeader.hxx>
+#include <osl/diagnose.h>
+#include "wrapstreamforshare.hxx"
+
+#include <comphelper/seekableinput.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/random.h>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <PackageConstants.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star;
+using namespace cppu;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+const css::uno::Sequence < sal_Int8 > & ZipPackageStream::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit lcl_CachedImplId;
+ return lcl_CachedImplId.getSeq();
+}
+
+ZipPackageStream::ZipPackageStream ( ZipPackage & rNewPackage,
+ const uno::Reference< XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert )
+: m_rZipPackage( rNewPackage )
+, m_bToBeCompressed ( true )
+, m_bToBeEncrypted ( false )
+, m_bHaveOwnKey ( false )
+, m_bIsEncrypted ( false )
+, m_nImportedStartKeyAlgorithm( 0 )
+, m_nImportedEncryptionAlgorithm( 0 )
+, m_nImportedChecksumAlgorithm( 0 )
+, m_nImportedDerivedKeySize( 0 )
+, m_nStreamMode( PACKAGE_STREAM_NOTSET )
+, m_nMagicalHackPos( 0 )
+, m_nMagicalHackSize( 0 )
+, m_nOwnStreamOrigSize( 0 )
+, m_bHasSeekable( false )
+, m_bCompressedIsSetFromOutside( false )
+, m_bFromManifest( false )
+, m_bUseWinEncoding( false )
+, m_bRawStream( false )
+{
+ m_xContext = xContext;
+ m_nFormat = nFormat;
+ mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
+ SetFolder ( false );
+ aEntry.nVersion = -1;
+ aEntry.nFlag = 0;
+ aEntry.nMethod = -1;
+ aEntry.nTime = -1;
+ aEntry.nCrc = -1;
+ aEntry.nCompressedSize = -1;
+ aEntry.nSize = -1;
+ aEntry.nOffset = -1;
+ aEntry.nPathLen = -1;
+ aEntry.nExtraLen = -1;
+}
+
+ZipPackageStream::~ZipPackageStream()
+{
+}
+
+void ZipPackageStream::setZipEntryOnLoading( const ZipEntry &rInEntry )
+{
+ aEntry.nVersion = rInEntry.nVersion;
+ aEntry.nFlag = rInEntry.nFlag;
+ aEntry.nMethod = rInEntry.nMethod;
+ aEntry.nTime = rInEntry.nTime;
+ aEntry.nCrc = rInEntry.nCrc;
+ aEntry.nCompressedSize = rInEntry.nCompressedSize;
+ aEntry.nSize = rInEntry.nSize;
+ aEntry.nOffset = rInEntry.nOffset;
+ aEntry.sPath = rInEntry.sPath;
+ aEntry.nPathLen = rInEntry.nPathLen;
+ aEntry.nExtraLen = rInEntry.nExtraLen;
+
+ if ( aEntry.nMethod == STORED )
+ m_bToBeCompressed = false;
+}
+
+uno::Reference< io::XInputStream > const & ZipPackageStream::GetOwnSeekStream()
+{
+ if ( !m_bHasSeekable && m_xStream.is() )
+ {
+ // The package component requires that every stream either be FROM a package or it must support XSeekable!
+ // The only exception is a nonseekable stream that is provided only for storing, if such a stream
+ // is accessed before commit it MUST be wrapped.
+ // Wrap the stream in case it is not seekable
+ m_xStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xStream, m_xContext );
+ uno::Reference< io::XSeekable > xSeek( m_xStream, UNO_QUERY_THROW );
+
+ m_bHasSeekable = true;
+ }
+
+ return m_xStream;
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::GetRawEncrStreamNoHeaderCopy()
+{
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW || !GetOwnSeekStream().is() )
+ throw io::IOException(THROW_WHERE );
+
+ if ( m_xBaseEncryptionData.is() )
+ throw ZipIOException(THROW_WHERE "Encrypted stream without encryption data!" );
+
+ uno::Reference< io::XSeekable > xSeek( GetOwnSeekStream(), UNO_QUERY );
+ if ( !xSeek.is() )
+ throw ZipIOException(THROW_WHERE "The stream must be seekable!" );
+
+ // skip header
+ xSeek->seek( n_ConstHeaderSize + m_xBaseEncryptionData->m_aInitVector.getLength() +
+ m_xBaseEncryptionData->m_aSalt.getLength() + m_xBaseEncryptionData->m_aDigest.getLength() );
+
+ // create temporary stream
+ uno::Reference < io::XTempFile > xTempFile = io::TempFile::create(m_xContext);
+ uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream();
+ uno::Reference < io::XInputStream > xTempIn = xTempFile->getInputStream();
+ uno::Reference < io::XSeekable > xTempSeek( xTempOut, UNO_QUERY_THROW );
+
+ // copy the raw stream to the temporary file starting from the current position
+ ::comphelper::OStorageHelper::CopyInputToOutput( GetOwnSeekStream(), xTempOut );
+ xTempOut->closeOutput();
+ xTempSeek->seek( 0 );
+
+ return xTempIn;
+}
+
+sal_Int32 ZipPackageStream::GetEncryptionAlgorithm() const
+{
+ return m_nImportedEncryptionAlgorithm ? m_nImportedEncryptionAlgorithm : m_rZipPackage.GetEncAlgID();
+}
+
+sal_Int32 ZipPackageStream::GetBlockSize() const
+{
+ return GetEncryptionAlgorithm() == css::xml::crypto::CipherID::AES_CBC_W3C_PADDING ? 16 : 8;
+}
+
+::rtl::Reference<EncryptionData> ZipPackageStream::GetEncryptionData(Bugs const bugs)
+{
+ ::rtl::Reference< EncryptionData > xResult;
+ if ( m_xBaseEncryptionData.is() )
+ xResult = new EncryptionData(
+ *m_xBaseEncryptionData,
+ GetEncryptionKey(bugs),
+ GetEncryptionAlgorithm(),
+ m_nImportedChecksumAlgorithm ? m_nImportedChecksumAlgorithm : m_rZipPackage.GetChecksumAlgID(),
+ m_nImportedDerivedKeySize ? m_nImportedDerivedKeySize : m_rZipPackage.GetDefaultDerivedKeySize(),
+ GetStartKeyGenID(),
+ bugs != Bugs::None);
+
+ return xResult;
+}
+
+uno::Sequence<sal_Int8> ZipPackageStream::GetEncryptionKey(Bugs const bugs)
+{
+ uno::Sequence< sal_Int8 > aResult;
+ sal_Int32 nKeyGenID = GetStartKeyGenID();
+ bool const bUseWinEncoding = (bugs == Bugs::WinEncodingWrongSHA1 || m_bUseWinEncoding);
+
+ if ( m_bHaveOwnKey && m_aStorageEncryptionKeys.hasElements() )
+ {
+ OUString aNameToFind;
+ if ( nKeyGenID == xml::crypto::DigestID::SHA256 )
+ aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ else if ( nKeyGenID == xml::crypto::DigestID::SHA1 )
+ {
+ aNameToFind = bUseWinEncoding
+ ? OUString(PACKAGE_ENCRYPTIONDATA_SHA1MS1252)
+ : (bugs == Bugs::WrongSHA1)
+ ? OUString(PACKAGE_ENCRYPTIONDATA_SHA1UTF8)
+ : OUString(PACKAGE_ENCRYPTIONDATA_SHA1CORRECT);
+ }
+ else
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+
+ for ( const auto& rKey : std::as_const(m_aStorageEncryptionKeys) )
+ if ( rKey.Name == aNameToFind )
+ rKey.Value >>= aResult;
+
+ // empty keys are not allowed here
+ // so it is not important whether there is no key, or the key is empty, it is an error
+ if ( !aResult.hasElements() )
+ throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
+ }
+ else
+ aResult = m_aEncryptionKey;
+
+ if ( !aResult.hasElements() || !m_bHaveOwnKey )
+ aResult = m_rZipPackage.GetEncryptionKey();
+
+ return aResult;
+}
+
+sal_Int32 ZipPackageStream::GetStartKeyGenID() const
+{
+ // generally should all the streams use the same Start Key
+ // but if raw copy without password takes place, we should preserve the imported algorithm
+ return m_nImportedStartKeyAlgorithm ? m_nImportedStartKeyAlgorithm : m_rZipPackage.GetStartKeyGenID();
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::TryToGetRawFromDataStream( bool bAddHeaderForEncr )
+{
+ if ( m_nStreamMode != PACKAGE_STREAM_DATA || !GetOwnSeekStream().is() || ( bAddHeaderForEncr && !m_bToBeEncrypted ) )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ Sequence< sal_Int8 > aKey;
+
+ if ( m_bToBeEncrypted )
+ {
+ aKey = GetEncryptionKey();
+ if ( !aKey.hasElements() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+ }
+
+ try
+ {
+ // create temporary file
+ uno::Reference < io::XStream > xTempStream(
+ io::TempFile::create(m_xContext),
+ uno::UNO_QUERY_THROW );
+
+ // create a package based on it
+ rtl::Reference<ZipPackage> pPackage = new ZipPackage( m_xContext );
+
+ Sequence< Any > aArgs{ Any(xTempStream) };
+ pPackage->initialize( aArgs );
+
+ // create a new package stream
+ uno::Reference< XDataSinkEncrSupport > xNewPackStream( pPackage->createInstance(), UNO_QUERY_THROW );
+ xNewPackStream->setDataStream(
+ new WrapStreamForShare(GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef()));
+
+ uno::Reference< XPropertySet > xNewPSProps( xNewPackStream, UNO_QUERY_THROW );
+
+ // copy all the properties of this stream to the new stream
+ xNewPSProps->setPropertyValue("MediaType", Any( msMediaType ) );
+ xNewPSProps->setPropertyValue("Compressed", Any( m_bToBeCompressed ) );
+ if ( m_bToBeEncrypted )
+ {
+ xNewPSProps->setPropertyValue(ENCRYPTION_KEY_PROPERTY, Any( aKey ) );
+ xNewPSProps->setPropertyValue("Encrypted", Any( true ) );
+ }
+
+ // insert a new stream in the package
+ uno::Reference< XUnoTunnel > xTunnel;
+ Any aRoot = pPackage->getByHierarchicalName("/");
+ aRoot >>= xTunnel;
+ uno::Reference< container::XNameContainer > xRootNameContainer( xTunnel, UNO_QUERY_THROW );
+
+ uno::Reference< XUnoTunnel > xNPSTunnel( xNewPackStream, UNO_QUERY );
+ xRootNameContainer->insertByName("dummy", Any( xNPSTunnel ) );
+
+ // commit the temporary package
+ pPackage->commitChanges();
+
+ // get raw stream from the temporary package
+ uno::Reference< io::XInputStream > xInRaw;
+ if ( bAddHeaderForEncr )
+ xInRaw = xNewPackStream->getRawStream();
+ else
+ xInRaw = xNewPackStream->getPlainRawStream();
+
+ // create another temporary file
+ uno::Reference < io::XOutputStream > xTempOut(
+ io::TempFile::create(m_xContext),
+ uno::UNO_QUERY_THROW );
+ uno::Reference < io::XInputStream > xTempIn( xTempOut, UNO_QUERY_THROW );
+ uno::Reference < io::XSeekable > xTempSeek( xTempOut, UNO_QUERY_THROW );
+
+ // copy the raw stream to the temporary file
+ ::comphelper::OStorageHelper::CopyInputToOutput( xInRaw, xTempOut );
+ xTempOut->closeOutput();
+ xTempSeek->seek( 0 );
+
+ // close raw stream, package stream and folder
+ xInRaw.clear();
+ xNewPSProps.clear();
+ xNPSTunnel.clear();
+ xNewPackStream.clear();
+ xTunnel.clear();
+ xRootNameContainer.clear();
+
+ // return the stream representing the first temporary file
+ return xTempIn;
+ }
+ catch ( RuntimeException& )
+ {
+ throw;
+ }
+ catch ( Exception& )
+ {
+ }
+
+ throw io::IOException(THROW_WHERE );
+}
+
+bool ZipPackageStream::ParsePackageRawStream()
+{
+ OSL_ENSURE( GetOwnSeekStream().is(), "A stream must be provided!" );
+
+ if ( !GetOwnSeekStream().is() )
+ return false;
+
+ bool bOk = false;
+
+ ::rtl::Reference< BaseEncryptionData > xTempEncrData;
+ Sequence < sal_Int8 > aHeader ( 4 );
+
+ try
+ {
+ if ( GetOwnSeekStream()->readBytes ( aHeader, 4 ) == 4 )
+ {
+ const sal_Int8 *pHeader = aHeader.getConstArray();
+ sal_uInt32 nHeader = ( pHeader [0] & 0xFF ) |
+ ( pHeader [1] & 0xFF ) << 8 |
+ ( pHeader [2] & 0xFF ) << 16 |
+ ( pHeader [3] & 0xFF ) << 24;
+ if ( nHeader == n_ConstHeader )
+ {
+ // this is one of our god-awful, but extremely devious hacks, everyone cheer
+ xTempEncrData = new BaseEncryptionData;
+
+ OUString aMediaType;
+ sal_Int32 nEncAlgorithm = 0;
+ sal_Int32 nChecksumAlgorithm = 0;
+ sal_Int32 nDerivedKeySize = 0;
+ sal_Int32 nStartKeyGenID = 0;
+ sal_Int32 nMagHackSize = 0;
+ if ( ZipFile::StaticFillData( xTempEncrData, nEncAlgorithm, nChecksumAlgorithm, nDerivedKeySize, nStartKeyGenID, nMagHackSize, aMediaType, GetOwnSeekStream() ) )
+ {
+ // We'll want to skip the data we've just read, so calculate how much we just read
+ // and remember it
+ m_nMagicalHackPos = n_ConstHeaderSize + xTempEncrData->m_aSalt.getLength()
+ + xTempEncrData->m_aInitVector.getLength()
+ + xTempEncrData->m_aDigest.getLength()
+ + aMediaType.getLength() * sizeof( sal_Unicode );
+ m_nImportedEncryptionAlgorithm = nEncAlgorithm;
+ m_nImportedChecksumAlgorithm = nChecksumAlgorithm;
+ m_nImportedDerivedKeySize = nDerivedKeySize;
+ m_nImportedStartKeyAlgorithm = nStartKeyGenID;
+ m_nMagicalHackSize = nMagHackSize;
+ msMediaType = aMediaType;
+
+ bOk = true;
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( !bOk )
+ {
+ // the provided stream is not a raw stream
+ return false;
+ }
+
+ m_xBaseEncryptionData = xTempEncrData;
+ SetIsEncrypted ( true );
+ // it's already compressed and encrypted
+ m_bToBeEncrypted = m_bToBeCompressed = false;
+
+ return true;
+}
+
+static void ImplSetStoredData( ZipEntry & rEntry, uno::Reference< io::XInputStream> const & rStream )
+{
+ // It's very annoying that we have to do this, but lots of zip packages
+ // don't allow data descriptors for STORED streams, meaning we have to
+ // know the size and CRC32 of uncompressed streams before we actually
+ // write them !
+ CRC32 aCRC32;
+ rEntry.nMethod = STORED;
+ rEntry.nCompressedSize = rEntry.nSize = aCRC32.updateStream ( rStream );
+ rEntry.nCrc = aCRC32.getValue();
+}
+
+bool ZipPackageStream::saveChild(
+ const OUString &rPath,
+ std::vector < uno::Sequence < beans::PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
+ const rtlRandomPool &rRandomPool)
+{
+ bool bSuccess = true;
+
+ static const OUStringLiteral sDigestProperty (u"Digest");
+ static const OUStringLiteral sEncryptionAlgProperty (u"EncryptionAlgorithm");
+ static const OUStringLiteral sStartKeyAlgProperty (u"StartKeyAlgorithm");
+ static const OUStringLiteral sDigestAlgProperty (u"DigestAlgorithm");
+ static const OUStringLiteral sDerivedKeySizeProperty (u"DerivedKeySize");
+
+ uno::Sequence < beans::PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
+
+ // In case the entry we are reading is also the entry we are writing, we will
+ // store the ZipEntry data in pTempEntry
+
+ // if pTempEntry is necessary, it will be released and passed to the ZipOutputStream
+ // and be deleted in the ZipOutputStream destructor
+ std::unique_ptr < ZipEntry > pAutoTempEntry ( new ZipEntry(aEntry) );
+ ZipEntry* pTempEntry = pAutoTempEntry.get();
+
+ pTempEntry->sPath = rPath;
+ pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( pTempEntry->sPath, RTL_TEXTENCODING_UTF8 ).getLength() );
+
+ const bool bToBeEncrypted = m_bToBeEncrypted && (rEncryptionKey.hasElements() || m_bHaveOwnKey);
+ const bool bToBeCompressed = bToBeEncrypted || m_bToBeCompressed;
+
+ auto pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
+ pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType( );
+ pPropSet[PKG_MNFST_VERSION].Name = "Version";
+ pPropSet[PKG_MNFST_VERSION].Value <<= OUString(); // no version is stored for streams currently
+ pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
+ pPropSet[PKG_MNFST_FULLPATH].Value <<= pTempEntry->sPath;
+
+ OSL_ENSURE( m_nStreamMode != PACKAGE_STREAM_NOTSET, "Unacceptable ZipPackageStream mode!" );
+
+ m_bRawStream = false;
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ m_bRawStream = ParsePackageRawStream();
+ else if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ m_bRawStream = true;
+
+ bool bBackgroundThreadDeflate = false;
+ bool bTransportOwnEncrStreamAsRaw = false;
+ // During the storing the original size of the stream can be changed
+ // TODO/LATER: get rid of this hack
+ m_nOwnStreamOrigSize = m_bRawStream ? m_nMagicalHackSize : aEntry.nSize;
+
+ bool bUseNonSeekableAccess = false;
+ uno::Reference < io::XInputStream > xStream;
+ if ( !IsPackageMember() && !m_bRawStream && !bToBeEncrypted && bToBeCompressed )
+ {
+ // the stream is not a package member, not a raw stream,
+ // it should not be encrypted and it should be compressed,
+ // in this case nonseekable access can be used
+
+ xStream = m_xStream;
+ uno::Reference < io::XSeekable > xSeek ( xStream, uno::UNO_QUERY );
+
+ bUseNonSeekableAccess = ( xStream.is() && !xSeek.is() );
+ }
+
+ if ( !bUseNonSeekableAccess )
+ {
+ xStream = getRawData();
+
+ if ( !xStream.is() )
+ {
+ OSL_FAIL( "ZipPackageStream didn't have a stream associated with it, skipping!" );
+ bSuccess = false;
+ return bSuccess;
+ }
+
+ uno::Reference < io::XSeekable > xSeek ( xStream, uno::UNO_QUERY );
+ try
+ {
+ if ( xSeek.is() )
+ {
+ // If the stream is a raw one, then we should be positioned
+ // at the beginning of the actual data
+ if ( !bToBeCompressed || m_bRawStream )
+ {
+ // The raw stream can neither be encrypted nor connected
+ OSL_ENSURE( !m_bRawStream || !(bToBeCompressed || bToBeEncrypted), "The stream is already encrypted!" );
+ xSeek->seek ( m_bRawStream ? m_nMagicalHackPos : 0 );
+ ImplSetStoredData ( *pTempEntry, xStream );
+
+ // TODO/LATER: Get rid of hacks related to switching of Flag Method and Size properties!
+ }
+ else if ( bToBeEncrypted )
+ {
+ // this is the correct original size
+ pTempEntry->nSize = xSeek->getLength();
+ m_nOwnStreamOrigSize = pTempEntry->nSize;
+ }
+
+ xSeek->seek ( 0 );
+ }
+ else
+ {
+ // Okay, we don't have an xSeekable stream. This is possibly bad.
+ // check if it's one of our own streams, if it is then we know that
+ // each time we ask for it we'll get a new stream that will be
+ // at position zero...otherwise, assert and skip this stream...
+ if ( IsPackageMember() )
+ {
+ // if the password has been changed then the stream should not be package member any more
+ if ( m_bIsEncrypted && m_bToBeEncrypted )
+ {
+ // Should be handled close to the raw stream handling
+ bTransportOwnEncrStreamAsRaw = true;
+ pTempEntry->nMethod = STORED;
+
+ // TODO/LATER: get rid of this situation
+ // this size should be different from the one that will be stored in manifest.xml
+ // it is used in storing algorithms and after storing the correct size will be set
+ pTempEntry->nSize = pTempEntry->nCompressedSize;
+ }
+ }
+ else
+ {
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ bSuccess = false;
+ return bSuccess;
+ }
+
+ if ( bToBeEncrypted || m_bRawStream || bTransportOwnEncrStreamAsRaw )
+ {
+ if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw )
+ {
+ uno::Sequence < sal_Int8 > aSalt( 16 ), aVector( GetBlockSize() );
+ rtl_random_getBytes ( rRandomPool, aSalt.getArray(), 16 );
+ rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() );
+ if ( !m_bHaveOwnKey )
+ {
+ m_aEncryptionKey = rEncryptionKey;
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+
+ setInitialisationVector ( aVector );
+ setSalt ( aSalt );
+ setIterationCount(nPBKDF2IterationCount);
+ }
+
+ // last property is digest, which is inserted later if we didn't have
+ // a magic header
+ aPropSet.realloc(PKG_SIZE_ENCR_MNFST);
+ pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_INIVECTOR].Name = "InitialisationVector";
+ pPropSet[PKG_MNFST_INIVECTOR].Value <<= m_xBaseEncryptionData->m_aInitVector;
+ pPropSet[PKG_MNFST_SALT].Name = "Salt";
+ pPropSet[PKG_MNFST_SALT].Value <<= m_xBaseEncryptionData->m_aSalt;
+ pPropSet[PKG_MNFST_ITERATION].Name = "IterationCount";
+ pPropSet[PKG_MNFST_ITERATION].Value <<= m_xBaseEncryptionData->m_nIterationCount;
+
+ // Need to store the uncompressed size in the manifest
+ OSL_ENSURE( m_nOwnStreamOrigSize >= 0, "The stream size was not correctly initialized!" );
+ pPropSet[PKG_MNFST_UCOMPSIZE].Name = "Size";
+ pPropSet[PKG_MNFST_UCOMPSIZE].Value <<= m_nOwnStreamOrigSize;
+
+ if ( m_bRawStream || bTransportOwnEncrStreamAsRaw )
+ {
+ ::rtl::Reference< EncryptionData > xEncData = GetEncryptionData();
+ if ( !xEncData.is() )
+ throw uno::RuntimeException();
+
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
+ pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
+ pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
+ pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
+ pPropSet[PKG_MNFST_DIGESTALG].Value <<= xEncData->m_nCheckAlg;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
+ }
+ }
+ }
+
+ // If the entry is already stored in the zip file in the format we
+ // want for this write...copy it raw
+ if ( !bUseNonSeekableAccess
+ && ( m_bRawStream || bTransportOwnEncrStreamAsRaw
+ || ( IsPackageMember() && !bToBeEncrypted
+ && ( ( aEntry.nMethod == DEFLATED && bToBeCompressed )
+ || ( aEntry.nMethod == STORED && !bToBeCompressed ) ) ) ) )
+ {
+ // If it's a PackageMember, then it's an unbuffered stream and we need
+ // to get a new version of it as we can't seek backwards.
+ if ( IsPackageMember() )
+ {
+ xStream = getRawData();
+ if ( !xStream.is() )
+ {
+ // Make sure that we actually _got_ a new one !
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+
+ try
+ {
+ if ( m_bRawStream )
+ xStream->skipBytes( m_nMagicalHackPos );
+
+ ZipOutputStream::setEntry(pTempEntry);
+ rZipOut.writeLOC(pTempEntry);
+ // the entry is provided to the ZipOutputStream that will delete it
+ pAutoTempEntry.release();
+
+ uno::Sequence < sal_Int8 > aSeq ( n_ConstBufferSize );
+ sal_Int32 nLength;
+
+ do
+ {
+ nLength = xStream->readBytes( aSeq, n_ConstBufferSize );
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ rZipOut.rawWrite(aSeq);
+ }
+ while ( nLength == n_ConstBufferSize );
+
+ rZipOut.rawCloseEntry();
+ }
+ catch ( ZipException& )
+ {
+ bSuccess = false;
+ }
+ catch ( io::IOException& )
+ {
+ bSuccess = false;
+ }
+ }
+ else
+ {
+ // This stream is definitely not a raw stream
+
+ // If nonseekable access is used the stream should be at the beginning and
+ // is useless after the storing. Thus if the storing fails the package should
+ // be thrown away ( as actually it is done currently )!
+ // To allow to reuse the package after the error, the optimization must be removed!
+
+ // If it's a PackageMember, then our previous reference held a 'raw' stream
+ // so we need to re-get it, unencrypted, uncompressed and positioned at the
+ // beginning of the stream
+ if ( IsPackageMember() )
+ {
+ xStream = getInputStream();
+ if ( !xStream.is() )
+ {
+ // Make sure that we actually _got_ a new one !
+ bSuccess = false;
+ return bSuccess;
+ }
+ }
+
+ if ( bToBeCompressed )
+ {
+ pTempEntry->nMethod = DEFLATED;
+ pTempEntry->nCrc = -1;
+ pTempEntry->nCompressedSize = pTempEntry->nSize = -1;
+ }
+
+ uno::Reference< io::XSeekable > xSeek(xStream, uno::UNO_QUERY);
+ // It's not worth to deflate jpegs to save ~1% in a slow process
+ // Unfortunately, does not work for streams protected by password
+ if (xSeek.is() && msMediaType.endsWith("/jpeg") && !m_bToBeEncrypted && !m_bToBeCompressed)
+ {
+ ImplSetStoredData(*pTempEntry, xStream);
+ xSeek->seek(0);
+ }
+
+ try
+ {
+ ZipOutputStream::setEntry(pTempEntry);
+ // the entry is provided to the ZipOutputStream that will delete it
+ pAutoTempEntry.release();
+
+ if (pTempEntry->nMethod == STORED)
+ {
+ sal_Int32 nLength;
+ uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ do
+ {
+ nLength = xStream->readBytes(aSeq, n_ConstBufferSize);
+ if (nLength != n_ConstBufferSize)
+ aSeq.realloc(nLength);
+
+ rZipOut.rawWrite(aSeq);
+ }
+ while ( nLength == n_ConstBufferSize );
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ else
+ {
+ // tdf#89236 Encrypting in a background thread does not work
+ bBackgroundThreadDeflate = !bToBeEncrypted;
+ // Do not deflate small streams using threads. XSeekable's getLength()
+ // gives the full size, XInputStream's available() may not be
+ // the full size, but it appears that at this point it usually is.
+ sal_Int64 estimatedSize = xSeek.is() ? xSeek->getLength() : xStream->available();
+
+ if (estimatedSize > 1000000)
+ {
+ // Use ThreadDeflater which will split the stream into blocks and compress
+ // them in threads, but not in background (i.e. writeStream() will block).
+ // This is suitable for large data.
+ bBackgroundThreadDeflate = false;
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ ZipOutputEntryParallel aZipEntry(rZipOut.getStream(), m_xContext, *pTempEntry, this, bToBeEncrypted);
+ aZipEntry.writeStream(xStream);
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ else if (bBackgroundThreadDeflate && estimatedSize > 100000)
+ {
+ // tdf#93553 limit to a useful amount of pending tasks. Having way too many
+ // tasks pending may use a lot of memory. Take number of available
+ // cores and allow 4-times the amount for having the queue well filled. The
+ // 2nd parameter is the time to wait between cleanups in 10th of a second.
+ // Both values may be added to the configuration settings if needed.
+ static std::size_t nAllowedTasks(comphelper::ThreadPool::getPreferredConcurrency() * 4); //TODO: overflow
+ rZipOut.reduceScheduledThreadTasksToGivenNumberOrLess(nAllowedTasks);
+
+ // Start a new thread task deflating this zip entry
+ ZipOutputEntryInThread *pZipEntry = new ZipOutputEntryInThread(
+ m_xContext, *pTempEntry, this, bToBeEncrypted);
+ rZipOut.addDeflatingThreadTask( pZipEntry,
+ pZipEntry->createTask( rZipOut.getThreadTaskTag(), xStream) );
+ }
+ else
+ {
+ bBackgroundThreadDeflate = false;
+ rZipOut.writeLOC(pTempEntry, bToBeEncrypted);
+ ZipOutputEntry aZipEntry(rZipOut.getStream(), m_xContext, *pTempEntry, this, bToBeEncrypted);
+ aZipEntry.writeStream(xStream);
+ rZipOut.rawCloseEntry(bToBeEncrypted);
+ }
+ }
+ }
+ catch ( ZipException& )
+ {
+ bSuccess = false;
+ }
+ catch ( io::IOException& )
+ {
+ bSuccess = false;
+ }
+
+ if ( bToBeEncrypted )
+ {
+ ::rtl::Reference< EncryptionData > xEncData = GetEncryptionData();
+ if ( !xEncData.is() )
+ throw uno::RuntimeException();
+
+ pPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
+ pPropSet[PKG_MNFST_DIGEST].Value <<= m_xBaseEncryptionData->m_aDigest;
+ pPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
+ pPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
+ pPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
+ pPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
+ pPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
+ pPropSet[PKG_MNFST_DIGESTALG].Value <<= xEncData->m_nCheckAlg;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
+ pPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
+
+ SetIsEncrypted ( true );
+ }
+ }
+
+ if (bSuccess && !bBackgroundThreadDeflate)
+ successfullyWritten(pTempEntry);
+
+ if ( aPropSet.hasElements()
+ && ( m_nFormat == embed::StorageFormats::PACKAGE || m_nFormat == embed::StorageFormats::OFOPXML ) )
+ rManList.push_back( aPropSet );
+
+ return bSuccess;
+}
+
+void ZipPackageStream::successfullyWritten( ZipEntry const *pEntry )
+{
+ if ( !IsPackageMember() )
+ {
+ if ( m_xStream.is() )
+ {
+ m_xStream->closeInput();
+ m_xStream.clear();
+ m_bHasSeekable = false;
+ }
+ SetPackageMember ( true );
+ }
+
+ if ( m_bRawStream )
+ {
+ // the raw stream was integrated and now behaves
+ // as usual encrypted stream
+ SetToBeEncrypted( true );
+ }
+
+ // Then copy it back afterwards...
+ aEntry = *pEntry;
+
+ // TODO/LATER: get rid of this hack ( the encrypted stream size property is changed during saving )
+ if ( m_bIsEncrypted )
+ setSize( m_nOwnStreamOrigSize );
+
+ aEntry.nOffset *= -1;
+}
+
+void ZipPackageStream::SetPackageMember( bool bNewValue )
+{
+ if ( bNewValue )
+ {
+ m_nStreamMode = PACKAGE_STREAM_PACKAGEMEMBER;
+ m_nMagicalHackPos = 0;
+ m_nMagicalHackSize = 0;
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_PACKAGEMEMBER )
+ m_nStreamMode = PACKAGE_STREAM_NOTSET; // must be reset
+}
+
+// XActiveDataSink
+void SAL_CALL ZipPackageStream::setInputStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ // if seekable access is required the wrapping will be done on demand
+ m_xStream = aStream;
+ m_nImportedEncryptionAlgorithm = 0;
+ m_bHasSeekable = false;
+ SetPackageMember ( false );
+ aEntry.nTime = -1;
+ m_nStreamMode = PACKAGE_STREAM_DETECT;
+}
+
+uno::Reference< io::XInputStream > ZipPackageStream::getRawData()
+{
+ try
+ {
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getRawData( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef(), false/*bUseBufferedStream*/ );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( ZipException & )//rException )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "" );
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "Exception is thrown during stream wrapping!" );
+ return uno::Reference < io::XInputStream > ();
+ }
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getInputStream()
+{
+ try
+ {
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getInputStream( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( ZipException & )//rException )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "" );
+ return uno::Reference < io::XInputStream > ();
+ }
+ catch ( const Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "package", "Exception is thrown during stream wrapping!");
+ return uno::Reference < io::XInputStream > ();
+ }
+}
+
+// XDataSinkEncrSupport
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getDataStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ uno::Reference< io::XInputStream > xResult;
+ try
+ {
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::None), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ if ( m_rZipPackage.GetStartKeyGenID() == xml::crypto::DigestID::SHA1 )
+ {
+ SAL_WARN("package", "ZipPackageStream::getDataStream(): SHA1 mismatch, trying fallbacks...");
+ try
+ { // tdf#114939 try with legacy StarOffice SHA1 bug
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ return xResult;
+ }
+ catch (const packages::WrongPasswordException&)
+ {
+ /* ignore and try next... */
+ }
+
+ try
+ {
+ // rhbz#1013844 / fdo#47482 workaround for the encrypted
+ // OpenOffice.org 1.0 documents generated by Libreoffice <=
+ // 3.6 with the new encryption format and using SHA256, but
+ // missing a specified startkey of SHA256
+
+ // force SHA256 and see if that works
+ m_nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA256;
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ return xResult;
+ }
+ catch (const packages::WrongPasswordException&)
+ {
+ // if that didn't work, restore to SHA1 and trundle through the *other* earlier
+ // bug fix
+ m_nImportedStartKeyAlgorithm = xml::crypto::DigestID::SHA1;
+ }
+
+ // workaround for the encrypted documents generated with the old OOo1.x bug.
+ if ( !m_bUseWinEncoding )
+ {
+ xResult = m_rZipPackage.getZipFile().getDataStream( aEntry, GetEncryptionData(Bugs::WinEncodingWrongSHA1), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ m_bUseWinEncoding = true;
+ }
+ else
+ throw;
+ }
+ else
+ throw;
+ }
+ return xResult;
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ return ZipFile::StaticGetDataFromRawStream( m_rZipPackage.GetSharedMutexRef(), m_xContext, GetOwnSeekStream(), GetEncryptionData() );
+ else if ( GetOwnSeekStream().is() )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else
+ return uno::Reference< io::XInputStream >();
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getRawStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ if ( !m_bIsEncrypted || !GetEncryptionData().is() )
+ throw packages::NoEncryptionException(THROW_WHERE );
+
+ return m_rZipPackage.getZipFile().getWrappedRawStream( aEntry, GetEncryptionData(), msMediaType, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ {
+ return new WrapStreamForShare( GetOwnSeekStream(), m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_DATA && m_bToBeEncrypted )
+ return TryToGetRawFromDataStream( true );
+ }
+
+ throw packages::NoEncryptionException(THROW_WHERE );
+}
+
+void SAL_CALL ZipPackageStream::setDataStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ setInputStream( aStream );
+ m_nStreamMode = PACKAGE_STREAM_DATA;
+}
+
+void SAL_CALL ZipPackageStream::setRawStream( const uno::Reference< io::XInputStream >& aStream )
+{
+ // wrap the stream in case it is not seekable
+ uno::Reference< io::XInputStream > xNewStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( aStream, m_xContext );
+ uno::Reference< io::XSeekable > xSeek( xNewStream, UNO_QUERY_THROW );
+ xSeek->seek( 0 );
+ uno::Reference< io::XInputStream > xOldStream = m_xStream;
+ m_xStream = xNewStream;
+ if ( !ParsePackageRawStream() )
+ {
+ m_xStream = xOldStream;
+ throw packages::NoRawFormatException(THROW_WHERE );
+ }
+
+ // the raw stream MUST have seekable access
+ m_bHasSeekable = true;
+
+ SetPackageMember ( false );
+ aEntry.nTime = -1;
+ m_nStreamMode = PACKAGE_STREAM_RAW;
+}
+
+uno::Reference< io::XInputStream > SAL_CALL ZipPackageStream::getPlainRawStream()
+{
+ // There is no stream attached to this object
+ if ( m_nStreamMode == PACKAGE_STREAM_NOTSET )
+ return uno::Reference< io::XInputStream >();
+
+ // this method can not be used together with old approach
+ if ( m_nStreamMode == PACKAGE_STREAM_DETECT )
+ throw packages::zip::ZipIOException(THROW_WHERE );
+
+ if ( IsPackageMember() )
+ {
+ return m_rZipPackage.getZipFile().getRawData( aEntry, GetEncryptionData(), m_bIsEncrypted, m_rZipPackage.GetSharedMutexRef() );
+ }
+ else if ( GetOwnSeekStream().is() )
+ {
+ if ( m_nStreamMode == PACKAGE_STREAM_RAW )
+ {
+ // the header should not be returned here
+ return GetRawEncrStreamNoHeaderCopy();
+ }
+ else if ( m_nStreamMode == PACKAGE_STREAM_DATA )
+ return TryToGetRawFromDataStream( false );
+ }
+
+ return uno::Reference< io::XInputStream >();
+}
+
+// XUnoTunnel
+
+sal_Int64 SAL_CALL ZipPackageStream::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+// XPropertySet
+void SAL_CALL ZipPackageStream::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ if ( aPropertyName == "MediaType" )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE && m_rZipPackage.getFormat() != embed::StorageFormats::OFOPXML )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ if ( !(aValue >>= msMediaType) )
+ throw IllegalArgumentException(THROW_WHERE "MediaType must be a string!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ if ( !msMediaType.isEmpty() )
+ {
+ if ( msMediaType.indexOf ( "text" ) != -1
+ || msMediaType == "application/vnd.sun.star.oleobject" )
+ m_bToBeCompressed = true;
+ else if ( !m_bCompressedIsSetFromOutside )
+ m_bToBeCompressed = false;
+ }
+ }
+ else if ( aPropertyName == "Size" )
+ {
+ if ( !( aValue >>= aEntry.nSize ) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Size property!",
+ uno::Reference< XInterface >(),
+ 2 );
+ }
+ else if ( aPropertyName == "Encrypted" )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ bool bEnc = false;
+ if ( !(aValue >>= bEnc) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Encrypted property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ if ( bEnc && m_nStreamMode == PACKAGE_STREAM_RAW )
+ throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ m_bToBeEncrypted = bEnc;
+ if ( m_bToBeEncrypted && !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ }
+ else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ uno::Sequence< sal_Int8 > aNewKey;
+
+ if ( !( aValue >>= aNewKey ) )
+ {
+ OUString sTempString;
+ if ( !(aValue >>= sTempString) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for EncryptionKey property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ sal_Int32 nPathLength = sTempString.getLength();
+ Sequence < sal_Int8 > aSequence ( nPathLength );
+ sal_Int8 *pArray = aSequence.getArray();
+ const sal_Unicode *pChar = sTempString.getStr();
+ for ( sal_Int32 i = 0; i < nPathLength; i++ )
+ pArray[i] = static_cast < sal_Int8 > ( pChar[i] );
+ aNewKey = aSequence;
+ }
+
+ if ( aNewKey.hasElements() )
+ {
+ if ( !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ m_aEncryptionKey = aNewKey;
+ // In case of new raw stream, the stream must not be encrypted on storing
+ m_bHaveOwnKey = true;
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW )
+ m_bToBeEncrypted = true;
+ }
+ else
+ {
+ m_bHaveOwnKey = false;
+ m_aEncryptionKey.realloc( 0 );
+ }
+
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+ else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ if ( m_rZipPackage.getFormat() != embed::StorageFormats::PACKAGE )
+ throw beans::PropertyVetoException(THROW_WHERE );
+
+ uno::Sequence< beans::NamedValue > aKeys;
+ if ( !( aValue >>= aKeys ) )
+ {
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for StorageEncryptionKeys property!",
+ uno::Reference< XInterface >(),
+ 2 );
+ }
+
+ if ( aKeys.hasElements() )
+ {
+ if ( !m_xBaseEncryptionData.is() )
+ m_xBaseEncryptionData = new BaseEncryptionData;
+
+ m_aStorageEncryptionKeys = aKeys;
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ m_bHaveOwnKey = true;
+ if ( m_nStreamMode != PACKAGE_STREAM_RAW )
+ m_bToBeEncrypted = true;
+ }
+ else
+ {
+ m_bHaveOwnKey = false;
+ m_aStorageEncryptionKeys.realloc( 0 );
+ }
+
+ m_aEncryptionKey.realloc( 0 );
+ }
+ else if ( aPropertyName == "Compressed" )
+ {
+ bool bCompr = false;
+
+ if ( !(aValue >>= bCompr) )
+ throw IllegalArgumentException(THROW_WHERE "Wrong type for Compressed property!",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ // In case of new raw stream, the stream must not be encrypted on storing
+ if ( bCompr && m_nStreamMode == PACKAGE_STREAM_RAW )
+ throw IllegalArgumentException(THROW_WHERE "Raw stream can not be encrypted on storing",
+ uno::Reference< XInterface >(),
+ 2 );
+
+ m_bToBeCompressed = bCompr;
+ m_bCompressedIsSetFromOutside = true;
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+Any SAL_CALL ZipPackageStream::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == "MediaType" )
+ {
+ return Any(msMediaType);
+ }
+ else if ( PropertyName == "Size" )
+ {
+ return Any(aEntry.nSize);
+ }
+ else if ( PropertyName == "Encrypted" )
+ {
+ return Any((m_nStreamMode == PACKAGE_STREAM_RAW) || m_bToBeEncrypted);
+ }
+ else if ( PropertyName == "WasEncrypted" )
+ {
+ return Any(m_bIsEncrypted);
+ }
+ else if ( PropertyName == "Compressed" )
+ {
+ return Any(m_bToBeCompressed);
+ }
+ else if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
+ {
+ return Any(m_aEncryptionKey);
+ }
+ else if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
+ {
+ return Any(m_aStorageEncryptionKeys);
+ }
+ else
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+void ZipPackageStream::setSize ( const sal_Int64 nNewSize )
+{
+ if ( aEntry.nCompressedSize != nNewSize )
+ aEntry.nMethod = DEFLATED;
+ aEntry.nSize = nNewSize;
+}
+OUString ZipPackageStream::getImplementationName()
+{
+ return "ZipPackageStream";
+}
+
+Sequence< OUString > ZipPackageStream::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.PackageStream" };
+}
+
+sal_Bool SAL_CALL ZipPackageStream::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/wrapstreamforshare.cxx b/package/source/zippackage/wrapstreamforshare.cxx
new file mode 100644
index 000000000..e5a475665
--- /dev/null
+++ b/package/source/zippackage/wrapstreamforshare.cxx
@@ -0,0 +1,151 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <osl/diagnose.h>
+
+#include "wrapstreamforshare.hxx"
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+WrapStreamForShare::WrapStreamForShare( const uno::Reference< io::XInputStream >& xInStream,
+ const rtl::Reference< comphelper::RefCountedMutex >& rMutexRef )
+: m_xMutex( rMutexRef )
+, m_xInStream( xInStream )
+, m_nCurPos( 0 )
+{
+ if ( !m_xMutex.is() || !m_xInStream.is() )
+ {
+ OSL_FAIL( "Wrong initialization of wrapping stream!" );
+ throw uno::RuntimeException(THROW_WHERE );
+ }
+ m_xSeekable.set( m_xInStream, uno::UNO_QUERY_THROW );
+}
+
+WrapStreamForShare::~WrapStreamForShare()
+{
+}
+
+// XInputStream
+sal_Int32 SAL_CALL WrapStreamForShare::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ sal_Int32 nRead = m_xInStream->readBytes( aData, nBytesToRead );
+ m_nCurPos += nRead;
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL WrapStreamForShare::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ sal_Int32 nRead = m_xInStream->readSomeBytes( aData, nMaxBytesToRead );
+ m_nCurPos += nRead;
+
+ return nRead;
+}
+
+void SAL_CALL WrapStreamForShare::skipBytes( sal_Int32 nBytesToSkip )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ m_xSeekable->seek( m_nCurPos );
+
+ m_xInStream->skipBytes( nBytesToSkip );
+ m_nCurPos = m_xSeekable->getPosition();
+}
+
+sal_Int32 SAL_CALL WrapStreamForShare::available()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_xInStream->available();
+}
+
+void SAL_CALL WrapStreamForShare::closeInput()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ // the package is the owner so it will close the stream
+ // m_xInStream->closeInput();
+ m_xInStream.clear();
+ m_xSeekable.clear();
+}
+
+// XSeekable
+void SAL_CALL WrapStreamForShare::seek( sal_Int64 location )
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ // let stream implementation do all the checking
+ m_xSeekable->seek( location );
+
+ m_nCurPos = m_xSeekable->getPosition();
+}
+
+sal_Int64 SAL_CALL WrapStreamForShare::getPosition()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_nCurPos;
+}
+
+sal_Int64 SAL_CALL WrapStreamForShare::getLength()
+{
+ ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
+
+ if ( !m_xInStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ return m_xSeekable->getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/wrapstreamforshare.hxx b/package/source/zippackage/wrapstreamforshare.hxx
new file mode 100644
index 000000000..238483b76
--- /dev/null
+++ b/package/source/zippackage/wrapstreamforshare.hxx
@@ -0,0 +1,59 @@
+/* -*- 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_ZIPPACKAGE_WRAPSTREAMFORSHARE_HXX
+#define INCLUDED_PACKAGE_SOURCE_ZIPPACKAGE_WRAPSTREAMFORSHARE_HXX
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <comphelper/refcountedmutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+class WrapStreamForShare final : public cppu::WeakImplHelper < css::io::XInputStream
+ , css::io::XSeekable >
+{
+ rtl::Reference< comphelper::RefCountedMutex > m_xMutex;
+ css::uno::Reference < css::io::XInputStream > m_xInStream;
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+ sal_Int64 m_nCurPos;
+
+public:
+ WrapStreamForShare( const css::uno::Reference< css::io::XInputStream >& xInStream,
+ const rtl::Reference< comphelper::RefCountedMutex >& rMutexRef );
+ virtual ~WrapStreamForShare() 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;
+
+ //XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/package/source/zippackage/zipfileaccess.cxx b/package/source/zippackage/zipfileaccess.cxx
new file mode 100644
index 000000000..d9b17fd80
--- /dev/null
+++ b/package/source/zippackage/zipfileaccess.cxx
@@ -0,0 +1,479 @@
+/* -*- 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/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <zipfileaccess.hxx>
+#include "ZipPackageSink.hxx"
+#include <EncryptionData.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+OZipFileAccess::OZipFileAccess( const uno::Reference< uno::XComponentContext >& rxContext )
+: m_aMutexHolder( new comphelper::RefCountedMutex )
+, m_xContext( rxContext )
+, m_bDisposed( false )
+, m_bOwnContent( false )
+{
+ if ( !rxContext.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+}
+
+OZipFileAccess::~OZipFileAccess()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+ if ( !m_bDisposed )
+ {
+ try {
+ // dispose will use refcounting so the further destruction must be avoided
+ osl_atomic_increment(&m_refCount);
+ dispose();
+ } catch( uno::Exception& )
+ {}
+ }
+}
+
+uno::Sequence< OUString > OZipFileAccess::GetPatternsFromString_Impl( const OUString& aString )
+{
+ if ( aString.isEmpty() )
+ return uno::Sequence< OUString >();
+
+ uno::Sequence< OUString > aPattern( 1 );
+ auto pPattern = aPattern.getArray();
+ sal_Int32 nInd = 0;
+
+ const sal_Unicode* pString = aString.getStr();
+ while( *pString )
+ {
+ if ( *pString == '\\' )
+ {
+ pString++;
+
+ if ( *pString == '\\' )
+ {
+ pPattern[nInd] += "\\";
+ pString++;
+ }
+ else if ( *pString == '*' )
+ {
+ pPattern[nInd] += "*";
+ pString++;
+ }
+ else
+ {
+ OSL_FAIL( "The backslash is not guarded!" );
+ pPattern[nInd] += "\\";
+ }
+ }
+ else if ( *pString == '*' )
+ {
+ aPattern.realloc( ( ++nInd ) + 1 );
+ pPattern = aPattern.getArray();
+ pString++;
+ }
+ else
+ {
+ pPattern[nInd] += OUStringChar( *pString );
+ pString++;
+ }
+ }
+
+ return aPattern;
+}
+
+bool OZipFileAccess::StringGoodForPattern_Impl( const OUString& aString,
+ const uno::Sequence< OUString >& aPattern )
+{
+ sal_Int32 nInd = aPattern.getLength() - 1;
+ if ( nInd < 0 )
+ return false;
+
+ if ( nInd == 0 )
+ {
+ if ( aPattern[0].isEmpty() )
+ return true;
+
+ return aString == aPattern[0];
+ }
+
+ sal_Int32 nBeginInd = aPattern[0].getLength();
+ sal_Int32 nEndInd = aString.getLength() - aPattern[nInd].getLength();
+ if ( nEndInd < nBeginInd
+ || ( nEndInd != aString.getLength() && aString.subView( nEndInd ) != aPattern[nInd] )
+ || ( nBeginInd != 0 && aString.subView( 0, nBeginInd ) != aPattern[0] ) )
+ return false;
+
+ for ( sal_Int32 nCurInd = aPattern.getLength() - 2; nCurInd > 0; nCurInd-- )
+ {
+ if ( aPattern[nCurInd].isEmpty() )
+ continue;
+
+ if ( nEndInd == nBeginInd )
+ return false;
+
+ // check that search does not use nEndInd position
+ sal_Int32 nLastInd = aString.lastIndexOf( aPattern[nCurInd], nEndInd - 1 );
+
+ if ( nLastInd == -1 )
+ return false;
+
+ if ( nLastInd < nBeginInd )
+ return false;
+
+ nEndInd = nLastInd;
+ }
+
+ return true;
+}
+
+// XInitialization
+void SAL_CALL OZipFileAccess::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE ); // initialization is allowed only one time
+
+ if ( !aArguments.hasElements() )
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ OSL_ENSURE( aArguments.getLength() == 1, "Too many arguments are provided, only the first one will be used!" );
+
+ OUString aParamURL;
+ uno::Reference< io::XStream > xStream;
+ uno::Reference< io::XSeekable > xSeekable;
+ uno::Sequence<beans::NamedValue> aArgs;
+
+ auto openInputStream = [&]()
+ {
+ ::ucbhelper::Content aContent(
+ aParamURL,
+ uno::Reference< css::ucb::XCommandEnvironment >(),
+ m_xContext );
+ uno::Reference < io::XActiveDataSink > xSink = new ZipPackageSink;
+ if ( aContent.openStream ( xSink ) )
+ {
+ m_xContentStream = xSink->getInputStream();
+ m_bOwnContent = true;
+ xSeekable.set( m_xContentStream, uno::UNO_QUERY );
+ }
+ };
+
+ if ( aArguments[0] >>= aParamURL )
+ {
+ openInputStream();
+ }
+ else if ( aArguments[0] >>= xStream )
+ {
+ // a writable stream can implement both XStream & XInputStream
+ m_xContentStream = xStream->getInputStream();
+ xSeekable.set( xStream, uno::UNO_QUERY );
+ }
+ else if ( aArguments[0] >>= m_xContentStream )
+ {
+ xSeekable.set( m_xContentStream, uno::UNO_QUERY );
+ }
+ else if (aArguments[0] >>= aArgs)
+ {
+ for (const beans::NamedValue& rArg : std::as_const(aArgs))
+ {
+ if (rArg.Name == "URL")
+ rArg.Value >>= aParamURL;
+ }
+
+ if (aParamURL.isEmpty())
+ throw lang::IllegalArgumentException(
+ THROW_WHERE"required argument 'URL' is not given or invalid.",
+ uno::Reference<uno::XInterface>(), 1);
+
+ openInputStream();
+ }
+ else
+ throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
+
+ if ( !m_xContentStream.is() )
+ throw io::IOException(THROW_WHERE );
+
+ if ( !xSeekable.is() )
+ {
+ // TODO: after fwkbugfix02 is integrated a helper class can be used to make the stream seekable
+ throw io::IOException(THROW_WHERE );
+ }
+
+ // TODO: in case xSeekable is implemented on separated XStream implementation a wrapper is required
+ m_pZipFile.emplace(
+ m_aMutexHolder,
+ m_xContentStream,
+ m_xContext,
+ true );
+}
+
+// XNameAccess
+uno::Any SAL_CALL OZipFileAccess::getByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
+ if ( aIter == m_pZipFile->GetEntryHash().end() )
+ throw container::NoSuchElementException(THROW_WHERE );
+
+ uno::Reference< io::XInputStream > xEntryStream;
+ try
+ {
+ xEntryStream = m_pZipFile->getDataStream((*aIter).second,
+ ::rtl::Reference< EncryptionData >(),
+ false,
+ m_aMutexHolder);
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ throw;
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException( "This package is unusable!",
+ static_cast < OWeakObject * > ( this ), anyEx);
+ }
+
+ if ( !xEntryStream.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+
+ return uno::Any ( xEntryStream );
+}
+
+uno::Sequence< OUString > SAL_CALL OZipFileAccess::getElementNames()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ uno::Sequence< OUString > aNames( m_pZipFile->GetEntryHash().size() );
+ auto pNames = aNames.getArray();
+ sal_Int32 nLen = 0;
+
+ for ( const auto& rEntry : m_pZipFile->GetEntryHash() )
+ {
+ if ( aNames.getLength() < ++nLen )
+ {
+ OSL_FAIL( "The size must be the same!" );
+ aNames.realloc( nLen );
+ pNames = aNames.getArray();
+ }
+
+ pNames[nLen-1] = rEntry.second.sPath;
+ }
+
+ if ( aNames.getLength() != nLen )
+ {
+ OSL_FAIL( "The size must be the same!" );
+ aNames.realloc( nLen );
+ }
+
+ return aNames;
+}
+
+sal_Bool SAL_CALL OZipFileAccess::hasByName( const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ EntryHash::iterator aIter = m_pZipFile->GetEntryHash().find( aName );
+
+ return ( aIter != m_pZipFile->GetEntryHash().end() );
+}
+
+uno::Type SAL_CALL OZipFileAccess::getElementType()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ return cppu::UnoType<io::XInputStream>::get();
+}
+
+sal_Bool SAL_CALL OZipFileAccess::hasElements()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw uno::RuntimeException(THROW_WHERE);
+
+ return ( !m_pZipFile->GetEntryHash().empty() );
+}
+
+// XZipFileAccess
+uno::Reference< io::XInputStream > SAL_CALL OZipFileAccess::getStreamByPattern( const OUString& aPatternString )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pZipFile )
+ throw io::NotConnectedException(THROW_WHERE );
+
+ // Code to compare strings by patterns
+ uno::Sequence< OUString > aPattern = GetPatternsFromString_Impl( aPatternString );
+
+ auto aIter = std::find_if(m_pZipFile->GetEntryHash().begin(), m_pZipFile->GetEntryHash().end(),
+ [&aPattern](const EntryHash::value_type& rEntry) { return StringGoodForPattern_Impl(rEntry.second.sPath, aPattern); });
+ if (aIter != m_pZipFile->GetEntryHash().end())
+ {
+ uno::Reference< io::XInputStream > xEntryStream( m_pZipFile->getDataStream( (*aIter).second,
+ ::rtl::Reference< EncryptionData >(),
+ false,
+ m_aMutexHolder ) );
+
+ if ( !xEntryStream.is() )
+ throw uno::RuntimeException(THROW_WHERE );
+ return xEntryStream;
+ }
+
+ throw container::NoSuchElementException(THROW_WHERE );
+}
+
+// XComponent
+void SAL_CALL OZipFileAccess::dispose()
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pListenersContainer )
+ {
+ lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
+ m_pListenersContainer->disposeAndClear( aSource );
+ m_pListenersContainer.reset();
+ }
+
+ m_pZipFile.reset();
+
+ if ( m_xContentStream.is() && m_bOwnContent )
+ try {
+ m_xContentStream->closeInput();
+ } catch( uno::Exception& )
+ {}
+
+ m_bDisposed = true;
+}
+
+void SAL_CALL OZipFileAccess::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( !m_pListenersContainer )
+ m_pListenersContainer.reset( new ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutexHolder->GetMutex() ) );
+ m_pListenersContainer->addInterface( xListener );
+}
+
+void SAL_CALL OZipFileAccess::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(THROW_WHERE );
+
+ if ( m_pListenersContainer )
+ m_pListenersContainer->removeInterface( xListener );
+}
+
+OUString SAL_CALL OZipFileAccess::getImplementationName()
+{
+ return "com.sun.star.comp.package.zip.ZipFileAccess";
+}
+
+sal_Bool SAL_CALL OZipFileAccess::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OZipFileAccess::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.zip.ZipFileAccess",
+ "com.sun.star.comp.packages.zip.ZipFileAccess" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+package_OZipFileAccess_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new OZipFileAccess(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */