Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/package/source/zippackage/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 78 kB image not shown  

Quelle  ZipPackage.cxx   Sprache: C

 
/* -*- 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 <officecfg/Office/Common.hxx>
#include <comphelper/fileurl.hxx>
#include <comphelper/processfactory.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 <com/sun/star/xml/crypto/KDFID.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 <unotools/streamwrap.hxx>
#include <unotools/tempfile.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>
#include <utility>

using namespace std::literals;

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
        {}
};

// namespace

sal_Int32 GetDefaultDerivedKeySize(sal_Int32 const nCipherID)
{
    switch (nCipherID)
    {
        case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
            return 16;
        case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
        case css::xml::crypto::CipherID::AES_GCM_W3C:
            return 32;
        default:
            O3TL_UNREACHABLE;
    }
}

ZipPackage::ZipPackage ( uno::Reference < XComponentContext > xContext )
: m_aMutexHolder( new comphelper::RefCountedMutex )
, m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
, m_oChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
, m_nKeyDerivationFunctionID(xml::crypto::KDFID::PBKDF2)
, 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(std::move( xContext ))
{
    m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
}

ZipPackage::~ZipPackage()
{
}

bool ZipPackage::isLocalFile() const
{
    return comphelper::isFileUrl(m_aURL);
}

// note: don't check for StorageFormats::ZIP, it breaks signing!
void ZipPackage::checkZipEntriesWithDD()
{
    if (!m_bForceRecovery)
    {
        ZipEnumeration entries{m_pZipFile->entries()};
        while (entries.hasMoreElements())
        {
            ZipEntry const& rEntry{*entries.nextElement()};
            if ((rEntry.nFlag & 0x08) != 0 && rEntry.nMethod == STORED)
            {
                uno::Reference<XPropertySet> xStream;
                getByHierarchicalName(rEntry.sPath) >>= xStream;
                uno::Reference<XServiceInfo> const xStreamSI{xStream, uno::UNO_QUERY_THROW};
                if (!xStreamSI->supportsService("com.sun.star.packages.PackageStream"))
                {
                    SAL_INFO("package""entry STORED with data descriptor is folder: \"" << rEntry.sPath << "\"");
                    throw ZipIOException(
                        THROW_WHERE
                        "entry STORED with data descriptor is folder");
                }
                if (!xStream->getPropertyValue("WasEncrypted").get<bool>())
                {
                    SAL_INFO("package""entry STORED with data descriptor but not encrypted: \"" << rEntry.sPath << "\"");
                    throw ZipIOException(
                        THROW_WHERE
                        "entry STORED with data descriptor but not encrypted");
                }
            }
        }
    }
}

void ZipPackage::parseManifest()
{
    if ( m_nFormat != embed::StorageFormats::PACKAGE )
        return;

    bool bManifestParsed = false;
    ::std::optional<OUString> oFirstVersion;
    static constexpr OUString sMeta (u"META-INF"_ustr);
    if ( m_xRootFolder->hasByName( sMeta ) )
    {
        try {
            static constexpr OUString sManifest (u"manifest.xml"_ustr);
            Any aAny = m_xRootFolder->getByName( sMeta );
            uno::Reference< XNameContainer > xMetaInfFolder;
            aAny >>= xMetaInfFolder;
            if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
            {
                uno::Reference < XActiveDataSink > xSink;
                aAny = xMetaInfFolder->getByName( sManifest );
                aAny >>= xSink;
                if ( xSink.is() )
                {
                    uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );

                    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;
                        uno::Any const* pKDF = nullptr;
                        uno::Any const* pArgon2Args = nullptr;
                        for ( const PropertyValue& rValue : rSequence )
                        {
                            if ( rValue.Name == u"FullPath" )
                                rValue.Value >>= sPath;
                            else if ( rValue.Name == u"Version" )
                            {
                                rValue.Value >>= sVersion;
                                if (!oFirstVersion)
                                {
                                    oFirstVersion.emplace(sVersion);
                                }
                            }
                            else if ( rValue.Name == u"MediaType" )
                                rValue.Value >>= sMediaType;
                            else if ( rValue.Name == u"Salt" )
                                pSalt = &( rValue.Value );
                            else if ( rValue.Name == u"InitialisationVector" )
                                pVector = &( rValue.Value );
                            else if ( rValue.Name == u"IterationCount" )
                                pCount = &( rValue.Value );
                            else if ( rValue.Name == u"Size" )
                                pSize = &( rValue.Value );
                            else if ( rValue.Name == u"Digest" )
                                pDigest = &( rValue.Value );
                            else if ( rValue.Name == u"DigestAlgorithm" )
                                pDigestAlg = &( rValue.Value );
                            else if ( rValue.Name == u"EncryptionAlgorithm" )
                                pEncryptionAlg = &( rValue.Value );
                            else if ( rValue.Name == u"StartKeyAlgorithm" )
                                pStartKeyAlg = &( rValue.Value );
                            else if ( rValue.Name == u"DerivedKeySize" )
                                pDerivedKeySize = &( rValue.Value );
                            else if ( rValue.Name == u"KeyInfo" )
                                pKeyInfo = &( rValue.Value );
                            else if (rValue.Name == u"KeyDerivationFunction") {
                                pKDF = &rValue.Value;
                            } else if (rValue.Name == u"Argon2Args") {
                                pArgon2Args = &rValue.Value;
                            }
                        }

                        if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
                        {
                            aAny = getByHierarchicalName( sPath );
                            uno::Reference < XInterface > xTmp;
                            aAny >>= xTmp;
                            if (auto pFolder = dynamic_cast<ZipPackageFolder*>(xTmp.get()))
                            {
                                pFolder->SetMediaType ( sMediaType );
                                pFolder->SetVersion ( sVersion );
                            }
                            else if (auto pStream = dynamic_cast<ZipPackageStream*>(xTmp.get()))
                            {
                                pStream->SetMediaType ( sMediaType );
                                pStream->SetFromManifest( true );

                                if (pKeyInfo
                                    && pVector && pSize && pEncryptionAlg
                                    && pKDF && pKDF->has<sal_Int32>() && pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
                                    && ((pEncryptionAlg->has<sal_Int32>()
                                            && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
                                        || (pDigest && pDigestAlg)))
                                {
                                    uno::Sequence < sal_Int8 > aSequence;
                                    sal_Int64 nSize = 0;
                                    ::std::optional<sal_Int32> oDigestAlg;
                                    sal_Int32 nEncryptionAlg = 0;

                                    pStream->SetToBeEncrypted ( true );

                                    *pVector >>= aSequence;
                                    pStream->setInitialisationVector ( aSequence );

                                    *pSize >>= nSize;
                                    pStream->setSize ( nSize );

                                    if (pDigest && pDigestAlg)
                                    {
                                        *pDigest >>= aSequence;
                                        pStream->setDigest(aSequence);

                                        assert(pDigestAlg->has<sal_Int32>());
                                        oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
                                    }

                                    *pEncryptionAlg >>= nEncryptionAlg;

                                    *pKeyInfo >>= m_aGpgProps;

                                    pStream->SetToBeCompressed ( true );
                                    pStream->SetToBeEncrypted ( true );
                                    pStream->SetIsEncrypted ( true );
                                    pStream->setIterationCount(::std::optional<sal_Int32>());
                                    pStream->setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>>());

                                    // 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->SetImportedAlgorithms({
                                        .nImportedStartKeyAlgorithm = nStartKeyAlg,
                                        .nImportedEncryptionAlgorithm = nEncryptionAlg,
                                        .oImportedChecksumAlgorithm = oDigestAlg,
                                        // note m_nCommonEncryptionID is not inited yet here
                                        .nImportedDerivedKeySize = ::GetDefaultDerivedKeySize(nEncryptionAlg),
                                        });

                                    if (!m_bHasEncryptedEntries
                                        && (pStream->getName() == "content.xml"
                                            || pStream->getName() == "encrypted-package"))
                                    {
                                        m_bHasEncryptedEntries = true;
                                        m_oChecksumDigestID = oDigestAlg;
                                        m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
                                        m_nCommonEncryptionID = nEncryptionAlg;
                                        m_nStartKeyGenerationID = nStartKeyAlg;
                                    }
                                }
                                else if (pSalt
                                    && pVector && pSize && pEncryptionAlg
                                    && pKDF && pKDF->has<sal_Int32>()
                                    && ((pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2 && pCount)
                                        || (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id && pArgon2Args))
                                    && ((pEncryptionAlg->has<sal_Int32>()
                                            && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
                                        || (pDigest && pDigestAlg)))

                                {
                                    uno::Sequence < sal_Int8 > aSequence;
                                    sal_Int64 nSize = 0;
                                    ::std::optional<sal_Int32> oDigestAlg;
                                    sal_Int32 nKDF = 0;
                                    sal_Int32 nEncryptionAlg = 0;
                                    sal_Int32 nCount = 0;
                                    sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;

                                    pStream->SetToBeEncrypted ( true );

                                    *pSalt >>= aSequence;
                                    pStream->setSalt ( aSequence );

                                    *pVector >>= aSequence;
                                    pStream->setInitialisationVector ( aSequence );

                                    *pKDF >>= nKDF;

                                    if (pCount)
                                    {
                                        *pCount >>= nCount;
                                        pStream->setIterationCount(::std::optional<sal_Int32>(nCount));
                                    }

                                    if (pArgon2Args)
                                    {
                                        uno::Sequence<sal_Int32> args;
                                        *pArgon2Args >>= args;
                                        assert(args.getLength() == 3);
                                        ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgs;
                                        oArgs.emplace(args[0], args[1], args[2]);
                                        pStream->setArgon2Args(oArgs);
                                    }

                                    *pSize >>= nSize;
                                    pStream->setSize ( nSize );

                                    if (pDigest && pDigestAlg)
                                    {
                                        *pDigest >>= aSequence;
                                        pStream->setDigest(aSequence);

                                        assert(pDigestAlg->has<sal_Int32>());
                                        oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
                                    }

                                    *pEncryptionAlg >>= nEncryptionAlg;

                                    if ( pDerivedKeySize )
                                        *pDerivedKeySize >>= nDerivedKeySize;

                                    if ( pStartKeyAlg )
                                        *pStartKeyAlg >>= nStartKeyAlg;

                                    pStream->SetImportedAlgorithms({
                                        .nImportedStartKeyAlgorithm = nStartKeyAlg,
                                        .nImportedEncryptionAlgorithm = nEncryptionAlg,
                                        .oImportedChecksumAlgorithm = oDigestAlg,
                                        .nImportedDerivedKeySize = nDerivedKeySize,
                                        });

                                    pStream->SetToBeCompressed ( true );
                                    pStream->SetToBeEncrypted ( true );
                                    pStream->SetIsEncrypted ( true );
                                    if (!m_bHasEncryptedEntries
                                        && (pStream->getName() == "content.xml"
                                            || pStream->getName() == "encrypted-package"))
                                    {
                                        m_bHasEncryptedEntries = true;
                                        m_nStartKeyGenerationID = nStartKeyAlg;
                                        m_nKeyDerivationFunctionID = nKDF;
                                        m_oChecksumDigestID = oDigestAlg;
                                        m_nCommonEncryptionID = nEncryptionAlg;
                                    }
                                }
                                else
                                    m_bHasNonEncryptedEntries = true;
                            }
                            else
                                throw ZipIOException(THROW_WHERE "Wrong content");
                        }
                    }

                    bManifestParsed = true;
                }

                checkZipEntriesWithDD(); // check before removing entries!

                // 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 constexpr OUString sMimetype (u"mimetype"_ustr);
    if ( m_xRootFolder->hasByName( sMimetype ) )
    {
        // get mediatype from the "mimetype" stream
        OUString aPackageMediatype;
        uno::Reference < io::XActiveDataSink > xMimeSink;
        m_xRootFolder->getByName( sMimetype ) >>= xMimeSink;
        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 || m_xRootFolder->GetMediaType().isEmpty())
        {
            // 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 );
                // also set version explicitly
                if (oFirstVersion && m_xRootFolder->GetVersion().isEmpty())
                {
                    m_xRootFolder->SetVersion(*oFirstVersion);
                }
                // if there is an encrypted inner package, there is no root
                // document, because instead there is a package, and it is not
                // an error
                if (!m_xRootFolder->hasByName(u"encrypted-package"sv))
                {
                    m_bMediaTypeFallbackUsed = true;
                }
            }
        }
        else if ( !m_bForceRecovery )
        {
            // the mimetype stream should contain the same information as manifest.xml
            OUString const mediaTypeXML(m_xRootFolder->hasByName(u"encrypted-package"sv)
                ? m_xRootFolder->doGetByName(u"encrypted-package").xPackageEntry->GetMediaType()
                : m_xRootFolder->GetMediaType());
            if (mediaTypeXML != aPackageMediatype)
            {
                throw ZipIOException(
                    THROW_WHERE
                    "mimetype conflicts with manifest.xml, \""
                    + mediaTypeXML + "\" vs. \""
                    + aPackageMediatype + "\"" );
            }
        }

        m_xRootFolder->removeByName( sMimetype );
    }

    m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams(
        std::u16string_view(), m_xRootFolder->hasByName(u"encrypted-package"sv));

    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 constexpr std::u16string_view 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 < io::XActiveDataSink > xSink;
        uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
        aAny >>= xSink;
        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 < XInterface > xIterTmp;
                        aIterAny >>= xIterTmp;
                        if (auto pStream = dynamic_cast<ZipPackageStream*>(xIterTmp.get()))
                        {
                            // 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 sDirName;

    while (aEnum.hasMoreElements())
    {
        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( '\\''/' );
        }

        sal_Int32 nStreamIndex = rName.lastIndexOf ( '/' );
        if ( nStreamIndex != -1 )
        {
            sDirName = rName.copy ( 0, nStreamIndex );
            FolderHash::iterator aIter = m_aRecent.find ( sDirName );
            if ( aIter != m_aRecent.end() )
                pCurrent = ( *aIter ).second;
        }

        if ( pCurrent == m_xRootFolder.get() )
        {
            sal_Int32 nIndex;
            sal_Int32 nOldIndex = 0;
            while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
            {
                std::u16string_view sTemp = rName.subView( nOldIndex, nIndex - nOldIndex );
                if ( nIndex == nOldIndex )
                    break;
                if ( !pCurrent->hasByName( sTemp ) )
                {
                    rtl::Reference<ZipPackageFolder> pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
                    try {
                        pPkgFolder->setName( OUString(sTemp) );
                    } catch (uno::RuntimeException const& e) {
                        throw css::packages::zip::ZipIOException(e.Message);
                    }
                    pPkgFolder->doSetParent( pCurrent );
                    pCurrent = pPkgFolder.get();
                }
                else
                {
                    ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
                    if (!rInfo.bFolder)
                        throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
                    pCurrent = rInfo.pFolder;
                }
                nOldIndex = nIndex+1;
            }
            if ( nStreamIndex != -1 && !sDirName.isEmpty() )
                m_aRecent [ sDirName ] = pCurrent;
        }
        if ( rName.getLength() -1 != nStreamIndex )
        {
            nStreamIndex++;
            std::u16string_view sTemp = rName.subView( 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(OUString(sTemp));
                pPkgStream->doSetParent(pCurrent);
            }
        }
    }

    if ( m_nFormat == embed::StorageFormats::PACKAGE )
        parseManifest();
    else if ( m_nFormat == embed::StorageFormats::OFOPXML )
    {
        parseContentType();
        checkZipEntriesWithDD();
    }
}

void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
{
    beans::NamedValue aNamedValue;

    if ( !aArguments.hasElements() )
        return;

    bool bHaveZipFile = true;

    forconst 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(u"Size"_ustr);
                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.",
                getXWeak() );
        }
    }

    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,
            m_nFormat == embed::StorageFormats::ZIP
                ? ZipFile::Checks::Default
                : m_nFormat == embed::StorageFormats::OFOPXML
                    ? ZipFile::Checks::CheckInsensitive
                    : ZipFile::Checks::TryCheckInsensitive);
        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,
            getXWeak() );
    }
}

Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
{
    OUString sDirName;
    sal_Int32 nOldIndex, nStreamIndex;

    if (aName == "/")
        // root directory.
        return Any ( uno::Reference( cppu::getXWeak(m_xRootFolder.get()) ) );

    sal_Int32 nIndex = aName.getLength();

    nStreamIndex = aName.lastIndexOf ( '/' );
    bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.

    if ( nStreamIndex != -1 )
    {
        // The name contains '/'.
        sDirName = aName.copy ( 0, nStreamIndex );
        FolderHash::iterator 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 );
                std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );

                if (pFolder && sTemp == pFolder->getName())
                    return Any(uno::Reference(cppu::getXWeak(pFolder)));
            }
            else
            {
                // Determine the file name.
                std::u16string_view sTemp = aName.subView( 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 )
    {
        std::u16string_view sTemp = aName.subView ( 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(u"Bad Zip File, stream as folder"_ustr);
        pCurrent = rInfo.pFolder;
        nOldIndex = nIndex+1;
    }

    if ( bFolder )
    {
        if ( nStreamIndex != -1 )
            m_aRecent[sDirName] = pPrevious; // cache it.
        return Any ( uno::Reference( cppu::getXWeak(pCurrent) ) );
    }

    std::u16string_view sTemp = aName.subView( 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 )
{
    if (aName == "/")
        // root directory
        return true;

    try
    {
        OUString sDirName;
        sal_Int32 nStreamIndex;
        nStreamIndex = aName.lastIndexOf ( '/' );
        bool bFolder = nStreamIndex == aName.getLength()-1;
        if ( nStreamIndex != -1 )
        {
            sDirName = aName.copy ( 0, nStreamIndex );
            FolderHash::iterator aIter = m_aRecent.find ( sDirName );
            if ( aIter != m_aRecent.end() )
            {
                if ( bFolder )
                {
                    sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
                    std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
                    if ( sTemp == ( *aIter ).second->getName() )
                        return true;
                    else
                        m_aRecent.erase ( aIter );
                }
                else
                {
                    std::u16string_view sTemp = aName.subView( 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;
        sal_Int32 nOldIndex = 0;
        sal_Int32 nIndex;
        while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
        {
            if ( nIndex == nOldIndex )
                break;

            std::u16string_view sTemp = aName.subView ( nOldIndex, nIndex - nOldIndex );

            if ( pCurrent->hasByName( sTemp ) )
            {
                pPrevious = pCurrent;
                ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
                if (!rInfo.bFolder)
                    throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
                pCurrent = rInfo.pFolder;
            }
            else
                return false;
            nOldIndex = nIndex+1;
        }
        if ( bFolder )
        {
            m_aRecent[sDirName] = pPrevious;
            return true;
        }
        else
        {
            std::u16string_view sTemp = aName.subView( 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(u"ZipPackage::hasByHierarchicalName"_ustr, 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 constexpr std::u16string_view sMime (u"mimetype" );
    if ( m_xRootFolder->hasByName( sMime ) )
        m_xRootFolder->removeByName( sMime );

    auto pEntry = std::make_unique<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(std::move(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!",
                getXWeak(),
                anyEx );
    }
}

void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
{
    // Write the manifest
    uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
    auto pEntry = std::make_unique<ZipEntry>();
    rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;

    pEntry->sPath = "META-INF/manifest.xml";
    pEntry->nMethod = DEFLATED;
    pEntry->nCrc = -1;
    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 );
    pEntry->nSize = nBufferLength;

    // the manifest.xml is never encrypted - so pass an empty reference
    ZipOutputStream::setEntry(*pEntry);
    auto p = pEntry.get();
    aZipOut.writeLOC(std::move(pEntry));
    ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
    aZipEntry.write(pBuffer->getSequence());
    aZipEntry.closeEntry();
    aZipOut.rawCloseEntry();
}

void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
{
    auto pEntry = std::make_unique<ZipEntry>();
    rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;

    pEntry->sPath = "[Content_Types].xml";
    pEntry->nMethod = DEFLATED;
    pEntry->nCrc = -1;
    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
    {
        { u"xml"_ustr, u"application/xml"_ustr },
        { u"rels"_ustr, u"application/vnd.openxmlformats-package.relationships+xml"_ustr },
        { u"png"_ustr, u"image/png"_ustr },
        { u"jpeg"_ustr, u"image/jpeg"_ustr },
        { u"fntdata"_ustr, u"application/x-fontdata"_ustr }
    };

    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 );
    pEntry->nSize = nBufferLength;

    // there is no encryption in this format currently
    ZipOutputStream::setEntry(*pEntry);
    auto p = pEntry.get();
    aZipOut.writeLOC(std::move(pEntry));
    ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
    aZipEntry.write(pBuffer->getSequence());
    aZipEntry.closeEntry();
    aZipOut.rawCloseEntry();
}

void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
{
    assert(dynamic_cast<comphelper::ByteReader*>(xInStream.get()));
    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,
            false,
            m_nFormat == embed::StorageFormats::ZIP
                ? ZipFile::Checks::Default
                : m_nFormat == embed::StorageFormats::OFOPXML
                    ? ZipFile::Checks::CheckInsensitive
                    : ZipFile::Checks::TryCheckInsensitive);
}

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
        rtl::Reference < utl::TempFileFastService > xTempFile( new utl::TempFileFastService );
        xTempOut.set( xTempFile );
        xTempIn.set( xTempFile );
    }

    // 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 std::u16string_view sMeta (u"META-INF");

            if ( m_xRootFolder->hasByName( sMeta ) )
            {
                static constexpr OUString sManifest (u"manifest.xml"_ustr);

                uno::Reference< XNameContainer > xMetaInfFolder;
                Any aAny = m_xRootFolder->getByName( sMeta );
                aAny >>= xMetaInfFolder;
                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 std::u16string_view 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();

        // note: this is always created here (needed for GPG), possibly
        // filtered out later in ManifestExport
        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 <<= u"/"_ustr;

            if( bIsGpgEncrypt )
            {
                pPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
                pPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
            }
            aManList.push_back( aPropSeq );
        }

        {
            ::std::optional<sal_Int32> oPBKDF2IterationCount;
            ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args;

            if (!bIsGpgEncrypt)
            {
                if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
                {   // if there is only one KDF invocation, increase the safety margin
                    oPBKDF2IterationCount.emplace(m_xRootFolder->hasByName(u"encrypted-package"sv) ? 600000 : 100000);
                }
                else
                {
                    assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
                    oArgon2Args.emplace(3, (1<<16), 4);
                }
            }

            // call saveContents - it will recursively save sub-directories
            m_xRootFolder->saveContents(u""_ustr, aManList, aZipOut, GetEncryptionKey(),
                oPBKDF2IterationCount, oArgon2Args);
        }

        if( m_nFormat == embed::StorageFormats::PACKAGE )
        {
            WriteManifest( aZipOut, aManList );
        }
        else if( m_nFormat == embed::StorageFormats::OFOPXML )
        {
            WriteContentTypes( aZipOut, aManList );
        }

        aZipOut.finish();

        if( bUseTemp )
            xResult = std::move(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!",
                    getXWeak(),
                    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,
                                            getXWeak(),
                                            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(u"Size"_ustr, 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(u"open"_ustr, 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!",
                getXWeak(), 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!",
                    getXWeak(), anyEx );
    }
    if ( xTempInStream.is() )
    {
        uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );

        try
        {
            xTempSeek->seek( 0 );
        }
        catchconst uno::Exception& )
        {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw WrappedTargetException(THROW_WHERE "Temporary file should be seekable!",
                    getXWeak(), anyEx );
        }

        try
        {
            // connect to the temporary stream
            ConnectTo( xTempInStream );
        }
        catchconst io::IOException& )
        {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
                    getXWeak(), 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();
            }
            catchconst uno::Exception& )
            {
                css::uno::Any anyEx = cppu::getCaughtException();
                throw WrappedTargetException(THROW_WHERE "This package is read only!",
                        getXWeak(), 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;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.