/* -*- 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/xml/sax/XExtendedDocumentHandler.hpp>
#include <com/sun/star/xml/crypto/DigestID.hpp>
#include <com/sun/star/xml/crypto/CipherID.hpp>
#include <com/sun/star/xml/crypto/KDFID.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/uno/RuntimeException.hpp>
 
#include "ManifestDefines.hxx"
#include "ManifestExport.hxx"
 
#include <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ref.hxx>
#include <sal/log.hxx>
#include <comphelper/base64.hxx>
#include <comphelper/documentconstants.hxx>
#include <comphelper/attributelist.hxx>
 
using namespace ::com::sun::star;
 
#if OSL_DEBUG_LEVEL > 0
#define THROW_WHERE SAL_WHERE
#else
#define THROW_WHERE ""
#endif
 
ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > const & xHandler,  const uno::Sequence< uno::Sequence < beans::PropertyValue > >& rManList )
{
    static constexpr OUString sKeyInfo                      ( u"KeyInfo"_ustr );
    static constexpr OUString sPgpKeyIDProperty             ( u"KeyId"_ustr );
    static constexpr OUString sPgpKeyPacketProperty         ( u"KeyPacket"_ustr );
    static constexpr OUString sCipherValueProperty          ( u"CipherValue"_ustr );
    static constexpr OUString sFullPathProperty             ( u"FullPath"_ustr );
    static constexpr OUString sVersionProperty              ( u"Version"_ustr );
    static constexpr OUString sMediaTypeProperty            ( u"MediaType"_ustr );
    static constexpr OUString sIterationCountProperty       ( u"IterationCount"_ustr );
    static constexpr OUString sDerivedKeySizeProperty       ( u"DerivedKeySize"_ustr );
    static constexpr OUString sSaltProperty                 ( u"Salt"_ustr );
    static constexpr OUString sInitialisationVectorProperty ( u"InitialisationVector"_ustr );
    static constexpr OUString sSizeProperty                 ( u"Size"_ustr );
    static constexpr OUString sDigestProperty               ( u"Digest"_ustr );
    static constexpr OUString sEncryptionAlgProperty        ( u"EncryptionAlgorithm"_ustr );
    static constexpr OUString sStartKeyAlgProperty          ( u"StartKeyAlgorithm"_ustr );
    static constexpr OUString sDigestAlgProperty            ( u"DigestAlgorithm"_ustr );
 
    static constexpr OUString sWhiteSpace                   ( u" "_ustr );
 
    rtl::Reference<::comphelper::AttributeList> pRootAttrList = new ::comphelper::AttributeList;
 
    // find the mediatype of the document if any
    OUString aDocMediaType;
    OUString aDocVersion;
    bool isWholesomeEncryption(false);
    const uno::Sequence<beans::PropertyValue>* pRootFolderPropSeq = nullptr;
    for (const uno::Sequence < beans::PropertyValue >& rSequence : rManList)
    {
        OUString aMediaType;
        OUString aPath;
        OUString aVersion;
 
        for (const beans::PropertyValue& rValue : rSequence)
        {
            if (rValue.Name == sMediaTypeProperty )
            {
                rValue.Value >>= aMediaType;
            }
            else if (rValue.Name == sFullPathProperty )
            {
                rValue.Value >>= aPath;
            }
            else if (rValue.Name == sVersionProperty )
            {
                rValue.Value >>= aVersion;
            }
 
            if ( !aPath.isEmpty() && !aMediaType.isEmpty() && !aVersion.isEmpty() )
                break;
        }
 
        if ( aPath == "/" )
        {
            assert(aDocMediaType.isEmpty());
            // unfortunately no aMediaType in some cases where non-documents
            // are stored as StorageFormats::PACKAGE instead of sensible
            // StorageFormats::ZIP, such as SvxXMLXTableExportComponent and
            // SwXMLTextBlocks, which results in an empty "mimetype" etc but
            // can't be easily fixed; try to exclude these cases by checking
            // for aVersion, but of course then forgetting to set both version
            // and type on an actual document can't be found :(
            assert(!aMediaType.isEmpty() || aVersion.isEmpty());
            aDocMediaType = aMediaType;
            aDocVersion = aVersion;
            pRootFolderPropSeq = &rSequence;
        }
 
        if (aPath == "encrypted-package")
        {
            isWholesomeEncryption = true;
            assert(aDocMediaType.isEmpty() || aDocMediaType == aMediaType);
        }
    }
    assert(pRootFolderPropSeq);
 
    bool bProvideDTD = false;
    bool bAcceptNonemptyVersion = false;
    bool bStoreStartKeyGeneration = false;
    if ( !aDocMediaType.isEmpty() )
    {
        if ( aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE
          || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE )
 
        {
            // oasis format
            pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
                                        MANIFEST_OASIS_NAMESPACE );
            bAcceptNonemptyVersion = true;
            if ( aDocVersion.compareTo( ODFVER_012_TEXT ) >= 0 )
            {
                // this is ODF12 or later generation, let encrypted
                // streams contain start-key-generation entry
                bStoreStartKeyGeneration = true;
                pRootAttrList->AddAttribute ( ATTRIBUTE_VERSION, aDocVersion );
                // plus gpg4libre extensions - loext NS for that
                pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS_LOEXT,
                                              MANIFEST_LOEXT_NAMESPACE );
            }
        }
        else
        {
            // even if it is no SO6 format the namespace must be specified
            // thus SO6 format is used as default one
            pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
                                        MANIFEST_NAMESPACE );
 
            bProvideDTD = true;
        }
    }
 
    xHandler->startDocument();
    uno::Reference < xml::sax::XExtendedDocumentHandler > xExtHandler ( xHandler, uno::UNO_QUERY );
    if ( xExtHandler.is() && bProvideDTD )
    {
        xExtHandler->unknown ( MANIFEST_DOCTYPE );
        xHandler->ignorableWhitespace ( sWhiteSpace );
    }
    xHandler->startElement( ELEMENT_MANIFEST, pRootAttrList );
 
    const uno::Any *pKeyInfoProperty = nullptr;
    if ( pRootFolderPropSeq )
    {
        // do we have package-wide encryption info?
        for (const beans::PropertyValue& rValue : *pRootFolderPropSeq)
        {
            if (rValue.Name == sKeyInfo )
                pKeyInfoProperty = &rValue.Value;
        }
 
        if ( pKeyInfoProperty )
        {
            // no start-key-generation needed, our session key has
            // max size already
            bStoreStartKeyGeneration = false;
 
            // yeah, so that goes directly below the manifest:manifest
            // element
            OUStringBuffer aBuffer;
 
            xHandler->ignorableWhitespace ( sWhiteSpace );
 
            // ==== manifest:keyinfo & children
            bool const isODF13(aDocVersion.compareTo(ODFVER_013_TEXT) >= 0);
            if (!isODF13)
            {
                xHandler->startElement(ELEMENT_MANIFEST_KEYINFO, nullptr);
            }
            xHandler->ignorableWhitespace ( sWhiteSpace );
 
            uno::Sequence< uno::Sequence < beans::NamedValue > > aKeyInfoSequence;
            *pKeyInfoProperty >>= aKeyInfoSequence;
            for (const uno::Sequence<beans::NamedValue>& rKeyInfoSequence : aKeyInfoSequence)
            {
                uno::Sequence < sal_Int8 > aPgpKeyID;
                uno::Sequence < sal_Int8 > aPgpKeyPacket;
                uno::Sequence < sal_Int8 > aCipherValue;
                for (const beans::NamedValue& rNValue : rKeyInfoSequence)
                {
                    if (rNValue.Name == sPgpKeyIDProperty )
                        rNValue.Value >>= aPgpKeyID;
                    else if (rNValue.Name == sPgpKeyPacketProperty )
                        rNValue.Value >>= aPgpKeyPacket;
                    else if (rNValue.Name == sCipherValueProperty )
                        rNValue.Value >>= aCipherValue;
                }
 
                if (aPgpKeyID.hasElements() && aCipherValue.hasElements() )
                {
                    // ==== manifest:encrypted-key & children - one for each recipient
                    xHandler->startElement(isODF13 ? ELEMENT_ENCRYPTEDKEY13 : ELEMENT_ENCRYPTEDKEY, nullptr);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    rtl::Reference<::comphelper::AttributeList> pNewAttrList = new ::comphelper::AttributeList;
                    // TODO: the algorithm should rather be configurable
                    pNewAttrList->AddAttribute(
                        isODF13 ? ATTRIBUTE_ALGORITHM13 : ATTRIBUTE_ALGORITHM,
                                                 u"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"_ustr );
                    xHandler->startElement(isODF13 ? ELEMENT_ENCRYPTIONMETHOD13 : ELEMENT_ENCRYPTIONMETHOD, pNewAttrList);
                    xHandler->endElement(isODF13 ? ELEMENT_ENCRYPTIONMETHOD13 : ELEMENT_ENCRYPTIONMETHOD);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    // note: the mismatch here corresponds to ODF 1.3 cs01 schema
                    xHandler->startElement(isODF13 ? ELEMENT_MANIFEST13_KEYINFO : ELEMENT_MANIFEST_KEYINFO, nullptr);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->startElement(isODF13 ? ELEMENT_PGPDATA13 : ELEMENT_PGPDATA, nullptr);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->startElement(isODF13 ? ELEMENT_PGPKEYID13 : ELEMENT_PGPKEYID, nullptr);
                    ::comphelper::Base64::encode(aBuffer, aPgpKeyID);
                    xHandler->characters( aBuffer.makeStringAndClear() );
                    xHandler->endElement(isODF13 ? ELEMENT_PGPKEYID13 : ELEMENT_PGPKEYID);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    // key packet is optional
                    if (aPgpKeyPacket.hasElements())
                    {
                        xHandler->startElement(isODF13 ? ELEMENT_PGPKEYPACKET13 : ELEMENT_PGPKEYPACKET, nullptr);
                        ::comphelper::Base64::encode(aBuffer, aPgpKeyPacket);
                        xHandler->characters( aBuffer.makeStringAndClear() );
                        xHandler->endElement(isODF13 ? ELEMENT_PGPKEYPACKET13 : ELEMENT_PGPKEYPACKET);
                        xHandler->ignorableWhitespace ( sWhiteSpace );
                    }
 
                    xHandler->endElement(isODF13 ? ELEMENT_PGPDATA13 : ELEMENT_PGPDATA);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->endElement(isODF13 ? ELEMENT_MANIFEST13_KEYINFO : ELEMENT_MANIFEST_KEYINFO);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->startElement(isODF13 ? ELEMENT_CIPHERDATA13 : ELEMENT_CIPHERDATA, nullptr);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->startElement(isODF13 ? ELEMENT_CIPHERVALUE13 : ELEMENT_CIPHERVALUE, nullptr);
                    ::comphelper::Base64::encode(aBuffer, aCipherValue);
                    xHandler->characters( aBuffer.makeStringAndClear() );
                    xHandler->endElement(isODF13 ? ELEMENT_CIPHERVALUE13 : ELEMENT_CIPHERVALUE);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->endElement(isODF13 ? ELEMENT_CIPHERDATA13 : ELEMENT_CIPHERDATA);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
 
                    xHandler->endElement(isODF13 ? ELEMENT_ENCRYPTEDKEY13 : ELEMENT_ENCRYPTEDKEY);
                    xHandler->ignorableWhitespace ( sWhiteSpace );
                }
            }
 
            if (!isODF13)
            {
                xHandler->endElement(ELEMENT_MANIFEST_KEYINFO);
            }
            xHandler->ignorableWhitespace ( sWhiteSpace );
        }
    }
 
    // now write individual file entries
    for (const uno::Sequence<beans::PropertyValue>& rSequence : rManList)
    {
        if (&rSequence == pRootFolderPropSeq && isWholesomeEncryption)
        {
            continue; // no root document, but embedded package => omit
        }
        rtl::Reference<::comphelper::AttributeList> pAttrList = new ::comphelper::AttributeList;
        OUString fullPath;
        OUString aString;
        const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
        uno::Any const* pKDF = nullptr;
        uno::Any const* pArgon2Args = nullptr;
        for (const beans::PropertyValue& rValue : rSequence)
        {
            if (rValue.Name == sMediaTypeProperty )
            {
                rValue.Value >>= aString;
                pAttrList->AddAttribute ( ATTRIBUTE_MEDIA_TYPE, aString );
            }
            else if (rValue.Name == sVersionProperty )
            {
                rValue.Value >>= aString;
                // the version is stored only if it is not empty
                if ( bAcceptNonemptyVersion && !aString.isEmpty() )
                    pAttrList->AddAttribute ( ATTRIBUTE_VERSION, aString );
            }
            else if (rValue.Name == sFullPathProperty )
            {
                rValue.Value >>= fullPath;
                pAttrList->AddAttribute(ATTRIBUTE_FULL_PATH, fullPath);
            }
            else if (rValue.Name == sSizeProperty )
            {
                sal_Int64 nSize = 0;
                rValue.Value >>= nSize;
                pAttrList->AddAttribute ( ATTRIBUTE_SIZE, OUString::number( nSize ) );
            }
            else if (rValue.Name == sInitialisationVectorProperty )
                pVector = &rValue.Value;
            else if (rValue.Name == sSaltProperty )
                pSalt = &rValue.Value;
            else if (rValue.Name == sIterationCountProperty )
                pIterationCount = &rValue.Value;
            else if (rValue.Name == sDigestProperty )
                pDigest = &rValue.Value;
            else if (rValue.Name == sDigestAlgProperty )
                pDigestAlg = &rValue.Value;
            else if (rValue.Name == sEncryptionAlgProperty )
                pEncryptAlg = &rValue.Value;
            else if (rValue.Name == sStartKeyAlgProperty )
                pStartKeyAlg = &rValue.Value;
            else if (rValue.Name == sDerivedKeySizeProperty )
                pDerivedKeySize = &rValue.Value;
            else if (rValue.Name == "KeyDerivationFunction") {
                pKDF = &rValue.Value;
            } else if (rValue.Name == "Argon2Args") {
                pArgon2Args = &rValue.Value;
            }
        }
        assert(!fullPath.isEmpty());
        if (isWholesomeEncryption)
        {   // there may be signatures in META-INF too
            assert(fullPath == "encrypted-package" || fullPath.startsWith("META-INF/"));
        }
 
        xHandler->ignorableWhitespace ( sWhiteSpace );
        xHandler->startElement( ELEMENT_FILE_ENTRY , pAttrList);
        if (pVector && pEncryptAlg && pDerivedKeySize && pKDF
            && ((pSalt && pStartKeyAlg && (pIterationCount || pArgon2Args))
                || pKeyInfoProperty))
        {
            // ==== Encryption Data
            rtl::Reference<::comphelper::AttributeList> pNewAttrList = new ::comphelper::AttributeList;
            OUStringBuffer aBuffer;
            uno::Sequence < sal_Int8 > aSequence;
 
            xHandler->ignorableWhitespace ( sWhiteSpace );
 
            // ==== Digest
            if (pDigest && pDigestAlg && pDigestAlg->hasValue())
            {
                OUString sChecksumType;
                sal_Int32 nDigestAlgID = 0;
                *pDigestAlg >>= nDigestAlgID;
                if ( nDigestAlgID == xml::crypto::DigestID::SHA256_1K )
                    sChecksumType = SHA256_1K_URL;
                else if ( nDigestAlgID == xml::crypto::DigestID::SHA1_1K )
                    sChecksumType = SHA1_1K_NAME;
                else
                    throw uno::RuntimeException( THROW_WHERE "Unexpected digest algorithm is provided!" );
 
                pNewAttrList->AddAttribute(ATTRIBUTE_CHECKSUM_TYPE, sChecksumType);
                *pDigest >>= aSequence;
                ::comphelper::Base64::encode(aBuffer, aSequence);
                pNewAttrList->AddAttribute(ATTRIBUTE_CHECKSUM, aBuffer.makeStringAndClear());
            }
 
            xHandler->startElement( ELEMENT_ENCRYPTION_DATA , pNewAttrList);
 
            // ==== Algorithm
            pNewAttrList = new ::comphelper::AttributeList;
 
            sal_Int32 nEncAlgID = 0;
            sal_Int32 nDerivedKeySize = 0;
            *pEncryptAlg >>= nEncAlgID;
            *pDerivedKeySize >>= nDerivedKeySize;
 
            OUString sEncAlgName;
            if ( nEncAlgID == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
            {
                OSL_ENSURE( nDerivedKeySize, "Unexpected key size is provided!" );
                if ( nDerivedKeySize != 32 )
                    throw uno::RuntimeException( THROW_WHERE "Unexpected key size is provided!" );
 
                sEncAlgName = AES256_URL;
            }
            else if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
            {
                assert(bStoreStartKeyGeneration || pKeyInfoProperty);
                SAL_WARN_IF(nDerivedKeySize != 32, "package.manifest", "Unexpected key size is provided!");
                if (nDerivedKeySize != 32)
                {
                    throw uno::RuntimeException(THROW_WHERE "Unexpected key size is provided!");
                }
                sEncAlgName = AESGCM256_URL;
            }
            else if ( nEncAlgID == xml::crypto::CipherID::BLOWFISH_CFB_8 )
            {
                sEncAlgName = BLOWFISH_NAME;
            }
            else
                throw uno::RuntimeException( THROW_WHERE "Unexpected encryption algorithm is provided!" );
 
            pNewAttrList->AddAttribute ( ATTRIBUTE_ALGORITHM_NAME, sEncAlgName );
 
            *pVector >>= aSequence;
            ::comphelper::Base64::encode(aBuffer, aSequence);
            pNewAttrList->AddAttribute ( ATTRIBUTE_INITIALISATION_VECTOR, aBuffer.makeStringAndClear() );
 
            xHandler->ignorableWhitespace ( sWhiteSpace );
            xHandler->startElement( ELEMENT_ALGORITHM , pNewAttrList);
            xHandler->ignorableWhitespace ( sWhiteSpace );
            xHandler->endElement( ELEMENT_ALGORITHM );
 
            if ( bStoreStartKeyGeneration )
            {
                // ==== Start Key Generation
                pNewAttrList = new ::comphelper::AttributeList;
 
                OUString sStartKeyAlg;
                OUString sStartKeySize;
                sal_Int32 nStartKeyAlgID = 0;
                *pStartKeyAlg >>= nStartKeyAlgID;
                if ( nStartKeyAlgID == xml::crypto::DigestID::SHA256 )
                {
                    if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
                    {   // new encryption is incompatible anyway, use W3C URL
                        sStartKeyAlg = SHA256_URL;
                    }
                    else // to interop with ODF <= 1.4 consumers use bad ODF URL
                    {
                        sStartKeyAlg = SHA256_URL_ODF12;
                    }
                    aBuffer.append( sal_Int32(32) );
                    sStartKeySize = aBuffer.makeStringAndClear();
                }
                else if ( nStartKeyAlgID == xml::crypto::DigestID::SHA1 )
                {
                    sStartKeyAlg = SHA1_NAME;
                    aBuffer.append( sal_Int32(20) );
                    sStartKeySize = aBuffer.makeStringAndClear();
                }
                else
                    throw uno::RuntimeException( THROW_WHERE "Unexpected start key algorithm is provided!" );
 
                pNewAttrList->AddAttribute ( ATTRIBUTE_START_KEY_GENERATION_NAME, sStartKeyAlg );
                pNewAttrList->AddAttribute ( ATTRIBUTE_KEY_SIZE, sStartKeySize );
 
                xHandler->ignorableWhitespace ( sWhiteSpace );
                xHandler->startElement( ELEMENT_START_KEY_GENERATION , pNewAttrList);
                xHandler->ignorableWhitespace ( sWhiteSpace );
                xHandler->endElement( ELEMENT_START_KEY_GENERATION );
            }
 
            // ==== Key Derivation
            pNewAttrList = new ::comphelper::AttributeList;
 
            if (pKeyInfoProperty)
            {
                assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P);
                pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
                                           PGP_NAME);
            }
            else
            {
                if (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id)
                {
                    pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
                                               ARGON2ID_URL_LO);
 
                    uno::Sequence<sal_Int32> args;
                    *pArgon2Args >>= args;
                    assert(args.getLength() == 3);
                    pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_T_LO, OUString::number(args[0]));
                    pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_M_LO, OUString::number(args[1]));
                    pNewAttrList->AddAttribute(ATTRIBUTE_ARGON2_P_LO, OUString::number(args[2]));
                }
                else
                {
                    assert(pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2);
                    pNewAttrList->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME,
                                               PBKDF2_NAME);
 
                    sal_Int32 nCount = 0;
                    *pIterationCount >>= nCount;
                    aBuffer.append(nCount);
                    pNewAttrList->AddAttribute(ATTRIBUTE_ITERATION_COUNT, aBuffer.makeStringAndClear());
                }
 
                *pSalt >>= aSequence;
                ::comphelper::Base64::encode(aBuffer, aSequence);
                pNewAttrList->AddAttribute ( ATTRIBUTE_SALT, aBuffer.makeStringAndClear() );
            }
 
            // ODF 1.3 specifies the default as 16 so have to write it for PGP
            if (bStoreStartKeyGeneration || pKeyInfoProperty)
            {
                aBuffer.append(nDerivedKeySize);
                pNewAttrList->AddAttribute(ATTRIBUTE_KEY_SIZE, aBuffer.makeStringAndClear());
            }
 
            xHandler->ignorableWhitespace(sWhiteSpace);
            xHandler->startElement(ELEMENT_KEY_DERIVATION, pNewAttrList);
            xHandler->ignorableWhitespace(sWhiteSpace);
            xHandler->endElement(ELEMENT_KEY_DERIVATION);
 
            xHandler->ignorableWhitespace ( sWhiteSpace );
            xHandler->endElement( ELEMENT_ENCRYPTION_DATA );
        }
        xHandler->ignorableWhitespace ( sWhiteSpace );
        xHandler->endElement( ELEMENT_FILE_ENTRY );
    }
    xHandler->ignorableWhitespace ( sWhiteSpace );
    xHandler->endElement( ELEMENT_MANIFEST );
    xHandler->endDocument();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V530 The return value of function 'append' is required to be utilized.

↑ V530 The return value of function 'append' is required to be utilized.

↑ V530 The return value of function 'append' is required to be utilized.

↑ V530 The return value of function 'append' is required to be utilized.