tdf#89383 DOCX import: fix permission for editing

w:writeProtection passwords in DOCX documents created
with Word weren't asked and verified to permit editing.

Change-Id: I53d73e3acaf0c0fd398ded2de52e1d8ef00cfd56
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122384
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/compilerplugins/clang/unusedfields.writeonly.results b/compilerplugins/clang/unusedfields.writeonly.results
index 7bf4af9..c97ce0c 100644
--- a/compilerplugins/clang/unusedfields.writeonly.results
+++ b/compilerplugins/clang/unusedfields.writeonly.results
@@ -1060,8 +1060,8 @@ writerfilter/source/dmapper/DomainMapperTableHandler.cxx:240
    writerfilter::dmapper::TableInfo aTablePropertyIds std::vector<PropertyIds>
writerfilter/source/dmapper/PropertyMap.hxx:219
    writerfilter::dmapper::SectionPropertyMap m_nDebugSectionNumber sal_Int32
writerfilter/source/dmapper/SettingsTable.cxx:264
    writerfilter::dmapper::SettingsTable_Impl m_sRedlineProtectionKey class rtl::OUString
writerfilter/source/dmapper/DocumentProtection.hxx:50
    writerfilter::dmapper::DocumentProtection m_sRedlineProtectionKey class rtl::OUString
xmlhelp/source/cxxhelp/provider/databases.hxx:249
    chelp::Databases m_aDatabases chelp::Databases::DatabasesTable
xmlhelp/source/cxxhelp/provider/databases.hxx:255
diff --git a/sw/qa/uitest/data/writeprotection.docx b/sw/qa/uitest/data/writeprotection.docx
new file mode 100644
index 0000000..f0fab46
--- /dev/null
+++ b/sw/qa/uitest/data/writeprotection.docx
Binary files differ
diff --git a/sw/qa/uitest/writer_tests6/tdf89383.py b/sw/qa/uitest/writer_tests6/tdf89383.py
new file mode 100644
index 0000000..26f5594
--- /dev/null
+++ b/sw/qa/uitest/writer_tests6/tdf89383.py
@@ -0,0 +1,28 @@
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# 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/.
#
from uitest.framework import UITestCase
from uitest.uihelper.common import get_url_for_data_file
from libreoffice.uno.propertyvalue import mkPropertyValues

#Bug 89383 - Read-only passwords on OOXML files are not working

class tdf89383(UITestCase):
   def test_tdf89383_DOCX(self):
        with self.ui_test.load_file(get_url_for_data_file("writeprotection.docx")):
            document = self.ui_test.get_component()

            # Without the fix in place, this test would have failed with
            # AssertionError: False is not true
            self.assertTrue(document.isReadonly())

            with self.ui_test.execute_dialog_through_command(".uno:EditDoc") as xDialog:
                xPassword = xDialog.getChild("newpassEntry")
                xPassword.executeAction("TYPE", mkPropertyValues({"TEXT": "a"}))

            self.assertFalse(document.isReadonly())

# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/writerfilter/Library_writerfilter.mk b/writerfilter/Library_writerfilter.mk
index add7c20..2b974536 100644
--- a/writerfilter/Library_writerfilter.mk
+++ b/writerfilter/Library_writerfilter.mk
@@ -78,6 +78,7 @@ $(eval $(call gb_Library_add_exception_objects,writerfilter,\
    writerfilter/source/dmapper/CellColorHandler \
    writerfilter/source/dmapper/CellMarginHandler \
    writerfilter/source/dmapper/ConversionHelper \
	writerfilter/source/dmapper/DocumentProtection \
    writerfilter/source/dmapper/DomainMapper \
    writerfilter/source/dmapper/DomainMapperTableHandler \
    writerfilter/source/dmapper/DomainMapperTableManager \
@@ -113,6 +114,7 @@ $(eval $(call gb_Library_add_exception_objects,writerfilter,\
    writerfilter/source/dmapper/TblStylePrHandler \
    writerfilter/source/dmapper/ThemeTable \
    writerfilter/source/dmapper/WrapPolygonHandler \
	writerfilter/source/dmapper/WriteProtection \
    writerfilter/source/dmapper/util \
    writerfilter/source/filter/RtfFilter \
    writerfilter/source/filter/WriterFilter \
diff --git a/writerfilter/source/dmapper/DocumentProtection.cxx b/writerfilter/source/dmapper/DocumentProtection.cxx
new file mode 100644
index 0000000..dddf964
--- /dev/null
+++ b/writerfilter/source/dmapper/DocumentProtection.cxx
@@ -0,0 +1,239 @@
/* -*- 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 "DocumentProtection.hxx"
#include "TagLogger.hxx"
#include <vector>
#include <comphelper/sequence.hxx>

using namespace com::sun::star;

namespace writerfilter::dmapper
{
DocumentProtection::DocumentProtection()
    : LoggedProperties("DocumentProtection")
    , m_nEdit(
          NS_ooxml::
              LN_Value_doc_ST_DocProtect_none) // Specifies that no editing restrictions have been applied to the document
    , m_bProtectForm(false)
    , m_bRedlineProtection(false)
    , m_bReadOnly(false)
    , m_bEnforcement(false)
    , m_bFormatting(false)
    , m_nCryptProviderType(NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
    , m_sCryptAlgorithmClass("hash")
    , m_sCryptAlgorithmType("typeAny")
    , m_CryptSpinCount(0)
{
}

DocumentProtection::~DocumentProtection() {}

void DocumentProtection::lcl_attribute(Id nName, Value& val)
{
    int nIntValue = val.getInt();
    OUString sStringValue = val.getString();

    switch (nName)
    {
        case NS_ooxml::LN_CT_DocProtect_edit: // 92037
            m_nEdit = nIntValue;
            // multiple DocProtect_edits should not exist. If they do, last one wins
            m_bRedlineProtection = false;
            m_bProtectForm = false;
            m_bReadOnly = false;
            switch (nIntValue)
            {
                case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:
                {
                    m_bRedlineProtection = true;
                    m_sRedlineProtectionKey = m_sHash;
                    break;
                }
                case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:
                    m_bProtectForm = true;
                    break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:
                    m_bReadOnly = true;
                    break;
            }
            break;
        case NS_ooxml::LN_CT_DocProtect_enforcement: // 92039
            m_bEnforcement = (nIntValue != 0);
            break;
        case NS_ooxml::LN_CT_DocProtect_formatting: // 92038
            m_bFormatting = (nIntValue != 0);
            break;
        case NS_ooxml::LN_AG_Password_cryptProviderType: // 92025
            m_nCryptProviderType = nIntValue;
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmClass: // 92026
            if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgClass_hash) // 92023
                m_sCryptAlgorithmClass = "hash";
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmType: // 92027
            if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgType_typeAny) // 92024
                m_sCryptAlgorithmType = "typeAny";
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmSid: // 92028
            m_sCryptAlgorithmSid = sStringValue;
            break;
        case NS_ooxml::LN_AG_Password_cryptSpinCount: // 92029
            m_CryptSpinCount = nIntValue;
            break;
        case NS_ooxml::LN_AG_Password_hash: // 92035
            m_sHash = sStringValue;
            break;
        case NS_ooxml::LN_AG_Password_salt: // 92036
            m_sSalt = sStringValue;
            break;
        default:
        {
#ifdef DBG_UTIL
            TagLogger::getInstance().element("unhandled");
#endif
        }
    }
}

void DocumentProtection::lcl_sprm(Sprm& /*rSprm*/) {}

uno::Sequence<beans::PropertyValue> DocumentProtection::toSequence() const
{
    std::vector<beans::PropertyValue> documentProtection;

    if (enabled())
    {
        // w:edit
        {
            beans::PropertyValue aValue;
            aValue.Name = "edit";

            switch (m_nEdit)
            {
                case NS_ooxml::LN_Value_doc_ST_DocProtect_none:
                    aValue.Value <<= OUString("none");
                    break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:
                    aValue.Value <<= OUString("readOnly");
                    break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_comments:
                    aValue.Value <<= OUString("comments");
                    break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:
                    aValue.Value <<= OUString("trackedChanges");
                    break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:
                    aValue.Value <<= OUString("forms");
                    break;
                default:
                {
#ifdef DBG_UTIL
                    TagLogger::getInstance().element("unhandled");
#endif
                }
            }

            documentProtection.push_back(aValue);
        }

        // w:enforcement
        if (m_bEnforcement)
        {
            beans::PropertyValue aValue;
            aValue.Name = "enforcement";
            aValue.Value <<= OUString("1");
            documentProtection.push_back(aValue);
        }

        // w:formatting
        if (m_bFormatting)
        {
            beans::PropertyValue aValue;
            aValue.Name = "formatting";
            aValue.Value <<= OUString("1");
            documentProtection.push_back(aValue);
        }

        // w:cryptProviderType
        {
            beans::PropertyValue aValue;
            aValue.Name = "cryptProviderType";
            if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
                aValue.Value <<= OUString("rsaAES");
            else if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull)
                aValue.Value <<= OUString("rsaFull");
            documentProtection.push_back(aValue);
        }

        // w:cryptAlgorithmClass
        {
            beans::PropertyValue aValue;
            aValue.Name = "cryptAlgorithmClass";
            aValue.Value <<= m_sCryptAlgorithmClass;
            documentProtection.push_back(aValue);
        }

        // w:cryptAlgorithmType
        {
            beans::PropertyValue aValue;
            aValue.Name = "cryptAlgorithmType";
            aValue.Value <<= m_sCryptAlgorithmType;
            documentProtection.push_back(aValue);
        }

        // w:cryptAlgorithmSid
        {
            beans::PropertyValue aValue;
            aValue.Name = "cryptAlgorithmSid";
            aValue.Value <<= m_sCryptAlgorithmSid;
            documentProtection.push_back(aValue);
        }

        // w:cryptSpinCount
        {
            beans::PropertyValue aValue;
            aValue.Name = "cryptSpinCount";
            aValue.Value <<= OUString::number(m_CryptSpinCount);
            documentProtection.push_back(aValue);
        }

        // w:hash
        {
            beans::PropertyValue aValue;
            aValue.Name = "hash";
            aValue.Value <<= m_sHash;
            documentProtection.push_back(aValue);
        }

        // w:salt
        {
            beans::PropertyValue aValue;
            aValue.Name = "salt";
            aValue.Value <<= m_sSalt;
            documentProtection.push_back(aValue);
        }
    }

    return comphelper::containerToSequence(documentProtection);
}

} //namespace writerfilter::dmapper

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DocumentProtection.hxx b/writerfilter/source/dmapper/DocumentProtection.hxx
new file mode 100644
index 0000000..2ec0f3f
--- /dev/null
+++ b/writerfilter/source/dmapper/DocumentProtection.hxx
@@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#include "LoggedResources.hxx"
#include <com/sun/star/beans/PropertyValue.hpp>
#include <ooxml/resourceids.hxx>

namespace writerfilter::dmapper
{
/** Document protection restrictions
     *
     * This element specifies the set of document protection restrictions which have been applied to the contents of a
     * WordprocessingML document.These restrictions should be enforced by applications editing this document
     * when the enforcement attribute is turned on, and ignored(but persisted) otherwise.Document protection is a
     * set of restrictions used to prevent unintentional changes to all or part of a WordprocessingML document.
     */
class DocumentProtection : public LoggedProperties
{
private:
    /** Document Editing Restrictions
         *
         * Possible values:
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_none
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_comments
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_forms
         */
    sal_Int32 m_nEdit;
    bool m_bProtectForm;
    bool m_bRedlineProtection;
    OUString m_sRedlineProtectionKey;
    bool m_bReadOnly;
    bool m_bEnforcement;
    bool m_bFormatting;

    /** Provider type
         *
         * Possible values:
         *  "rsaAES"  - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES
         *  "rsaFull" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull
         */
    sal_Int32 m_nCryptProviderType;
    OUString m_sCryptAlgorithmClass;
    OUString m_sCryptAlgorithmType;
    OUString m_sCryptAlgorithmSid;
    sal_Int32 m_CryptSpinCount;
    OUString m_sHash;
    OUString m_sSalt;

    virtual void lcl_attribute(Id Name, Value& val) override;
    virtual void lcl_sprm(Sprm& sprm) override;

    bool enabled() const { return !isNone(); }
    bool isNone() const { return m_nEdit == NS_ooxml::LN_Value_doc_ST_DocProtect_none; };

public:
    DocumentProtection();
    virtual ~DocumentProtection() override;

    css::uno::Sequence<css::beans::PropertyValue> toSequence() const;

    bool getProtectForm() const { return m_bProtectForm; }
    bool getRedlineProtection() const { return m_bRedlineProtection; }
    bool getReadOnly() const { return m_bReadOnly; }
    bool getEnforcement() const { return m_bEnforcement; }
};
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 616b097..f11add3 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -7710,6 +7710,10 @@ void DomainMapper_Impl::ApplySettingsTable()
        {
            xSettings->setPropertyValue("GutterAtTop", uno::makeAny(true));
        }
        uno::Sequence<beans::PropertyValue> aWriteProtection
                = m_pSettingsTable->GetWriteProtectionSettings();
        if (aWriteProtection.hasElements())
                xSettings->setPropertyValue("ModifyPasswordInfo", uno::makeAny(aWriteProtection));
    }
    catch(const uno::Exception&)
    {
diff --git a/writerfilter/source/dmapper/SettingsTable.cxx b/writerfilter/source/dmapper/SettingsTable.cxx
index a780635..f97d0a0 100644
--- a/writerfilter/source/dmapper/SettingsTable.cxx
+++ b/writerfilter/source/dmapper/SettingsTable.cxx
@@ -18,7 +18,9 @@
 */

#include "SettingsTable.hxx"
#include "DocumentProtection.hxx"
#include "TagLogger.hxx"
#include "WriteProtection.hxx"

#include <vector>

@@ -32,7 +34,6 @@
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <comphelper/propertysequence.hxx>
#include <comphelper/sequence.hxx>
#include <ooxml/resourceids.hxx>
#include "ConversionHelper.hxx"
#include "DomainMapper.hxx"
#include "util.hxx"
@@ -61,176 +62,6 @@ sal_Int16 lcl_GetZoomType(Id nType)

namespace dmapper
{
    namespace {

    /** Document protection restrictions
     *
     * This element specifies the set of document protection restrictions which have been applied to the contents of a
     * WordprocessingML document.These restrictions should be enforced by applications editing this document
     * when the enforcement attribute is turned on, and ignored(but persisted) otherwise.Document protection is a
     * set of restrictions used to prevent unintentional changes to all or part of a WordprocessingML document.
     */
    struct DocumentProtection_Impl
    {
        /** Document Editing Restrictions
         *
         * Possible values:
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_none
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_comments
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges
         *  - NS_ooxml::LN_Value_doc_ST_DocProtect_forms
         */
        sal_Int32       m_nEdit;
        bool            m_bEnforcement;
        bool            m_bFormatting;

        /** Provider type
         *
         * Possible values:
         *  "rsaAES"  - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES
         *  "rsaFull" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull
         */
        sal_Int32       m_nCryptProviderType;
        OUString        m_sCryptAlgorithmClass;
        OUString        m_sCryptAlgorithmType;
        OUString        m_sCryptAlgorithmSid;
        sal_Int32       m_CryptSpinCount;
        OUString        m_sHash;
        OUString        m_sSalt;

        DocumentProtection_Impl()
            : m_nEdit(NS_ooxml::LN_Value_doc_ST_DocProtect_none) // Specifies that no editing restrictions have been applied to the document
            , m_bEnforcement(false)
            , m_bFormatting(false)
            , m_nCryptProviderType(NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
            , m_sCryptAlgorithmClass("hash")
            , m_sCryptAlgorithmType("typeAny")
            , m_CryptSpinCount(0)
        {
        }

        css::uno::Sequence<css::beans::PropertyValue> toSequence() const;

        bool enabled() const
        {
            return ! isNone();
        }

        bool isNone()           const { return m_nEdit == NS_ooxml::LN_Value_doc_ST_DocProtect_none; };
    };

    }

    css::uno::Sequence<css::beans::PropertyValue> DocumentProtection_Impl::toSequence() const
    {
        std::vector<beans::PropertyValue> documentProtection;

        if (enabled())
        {
            // w:edit
            {
                beans::PropertyValue aValue;
                aValue.Name = "edit";

                switch (m_nEdit)
                {
                case NS_ooxml::LN_Value_doc_ST_DocProtect_none:             aValue.Value <<= OUString("none"); break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:         aValue.Value <<= OUString("readOnly"); break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_comments:         aValue.Value <<= OUString("comments"); break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:   aValue.Value <<= OUString("trackedChanges"); break;
                case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:            aValue.Value <<= OUString("forms"); break;
                default:
                {
#ifdef DBG_UTIL
                    TagLogger::getInstance().element("unhandled");
#endif
                }
                }

                documentProtection.push_back(aValue);
            }

            // w:enforcement
            if (m_bEnforcement)
            {
                beans::PropertyValue aValue;
                aValue.Name = "enforcement";
                aValue.Value <<= OUString("1");
                documentProtection.push_back(aValue);
            }

            // w:formatting
            if (m_bFormatting)
            {
                beans::PropertyValue aValue;
                aValue.Name = "formatting";
                aValue.Value <<= OUString("1");
                documentProtection.push_back(aValue);
            }

            // w:cryptProviderType
            {
                beans::PropertyValue aValue;
                aValue.Name = "cryptProviderType";
                if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
                    aValue.Value <<= OUString("rsaAES");
                else if (m_nCryptProviderType == NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull)
                    aValue.Value <<= OUString("rsaFull");
                documentProtection.push_back(aValue);
            }

            // w:cryptAlgorithmClass
            {
                beans::PropertyValue aValue;
                aValue.Name = "cryptAlgorithmClass";
                aValue.Value <<= m_sCryptAlgorithmClass;
                documentProtection.push_back(aValue);
            }

            // w:cryptAlgorithmType
            {
                beans::PropertyValue aValue;
                aValue.Name = "cryptAlgorithmType";
                aValue.Value <<= m_sCryptAlgorithmType;
                documentProtection.push_back(aValue);
            }

            // w:cryptAlgorithmSid
            {
                beans::PropertyValue aValue;
                aValue.Name = "cryptAlgorithmSid";
                aValue.Value <<= m_sCryptAlgorithmSid;
                documentProtection.push_back(aValue);
            }

            // w:cryptSpinCount
            {
                beans::PropertyValue aValue;
                aValue.Name = "cryptSpinCount";
                aValue.Value <<= OUString::number(m_CryptSpinCount);
                documentProtection.push_back(aValue);
            }

            // w:hash
            {
                beans::PropertyValue aValue;
                aValue.Name = "hash";
                aValue.Value <<= m_sHash;
                documentProtection.push_back(aValue);
            }

            // w:salt
            {
                beans::PropertyValue aValue;
                aValue.Name = "salt";
                aValue.Value <<= m_sSalt;
                documentProtection.push_back(aValue);
            }
        }

        return comphelper::containerToSequence(documentProtection);
    }

struct SettingsTable_Impl
{
@@ -259,10 +90,6 @@ struct SettingsTable_Impl
    bool                m_bSplitPgBreakAndParaMark;
    bool                m_bMirrorMargin;
    bool                m_bDoNotExpandShiftReturn;
    bool                m_bProtectForm;
    bool                m_bRedlineProtection;
    OUString            m_sRedlineProtectionKey;
    bool                m_bReadOnly;
    bool                m_bDisplayBackgroundShape;
    bool                m_bNoLeading = false;
    OUString            m_sDecimalSymbol;
@@ -274,7 +101,8 @@ struct SettingsTable_Impl
    uno::Sequence<beans::PropertyValue> m_pCurrentCompatSetting;
    OUString            m_sCurrentDatabaseDataSource;

    DocumentProtection_Impl m_DocumentProtection;
    std::shared_ptr<DocumentProtection> m_pDocumentProtection;
    std::shared_ptr<WriteProtection> m_pWriteProtection;
    bool m_bGutterAtTop = false;

    SettingsTable_Impl() :
@@ -301,9 +129,6 @@ struct SettingsTable_Impl
    , m_bSplitPgBreakAndParaMark(false)
    , m_bMirrorMargin(false)
    , m_bDoNotExpandShiftReturn(false)
    , m_bProtectForm(false)
    , m_bRedlineProtection(false)
    , m_bReadOnly(false)
    , m_bDisplayBackgroundShape(false)
    , m_sDecimalSymbol(".")
    , m_sListSeparator(",")
@@ -325,6 +150,8 @@ SettingsTable::SettingsTable(const DomainMapper& rDomainMapper)
        // Longer space sequence is opt-in for RTF, and not in OOXML.
        m_pImpl->m_bLongerSpaceSequence = true;
    }
    m_pImpl->m_pDocumentProtection = std::make_shared<DocumentProtection>();
    m_pImpl->m_pWriteProtection = std::make_shared<WriteProtection>();
}

SettingsTable::~SettingsTable()
@@ -371,57 +198,6 @@ void SettingsTable::lcl_attribute(Id nName, Value & val)
        m_pImpl->m_pCurrentCompatSetting[2].Name = "val";
        m_pImpl->m_pCurrentCompatSetting[2].Value <<= sStringValue;
        break;
    case NS_ooxml::LN_CT_DocProtect_edit: // 92037
        m_pImpl->m_DocumentProtection.m_nEdit = nIntValue;
        // multiple DocProtect_edits should not exist. If they do, last one wins
        m_pImpl->m_bRedlineProtection = false;
        m_pImpl->m_bProtectForm = false;
        m_pImpl->m_bReadOnly = false;
        switch (nIntValue)
        {
        case NS_ooxml::LN_Value_doc_ST_DocProtect_trackedChanges:
        {
            m_pImpl->m_bRedlineProtection = true;
            m_pImpl->m_sRedlineProtectionKey = m_pImpl->m_DocumentProtection.m_sHash;
            break;
        }
        case NS_ooxml::LN_Value_doc_ST_DocProtect_forms:
            m_pImpl->m_bProtectForm = true;
            break;
        case NS_ooxml::LN_Value_doc_ST_DocProtect_readOnly:
            m_pImpl->m_bReadOnly = true;
            break;
        }
        break;
    case NS_ooxml::LN_CT_DocProtect_enforcement: // 92039
        m_pImpl->m_DocumentProtection.m_bEnforcement = (nIntValue != 0);
        break;
    case NS_ooxml::LN_CT_DocProtect_formatting: // 92038
        m_pImpl->m_DocumentProtection.m_bFormatting = (nIntValue != 0);
        break;
    case NS_ooxml::LN_AG_Password_cryptProviderType: // 92025
        m_pImpl->m_DocumentProtection.m_nCryptProviderType = nIntValue;
        break;
    case NS_ooxml::LN_AG_Password_cryptAlgorithmClass: // 92026
        if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgClass_hash) // 92023
            m_pImpl->m_DocumentProtection.m_sCryptAlgorithmClass = "hash";
        break;
    case NS_ooxml::LN_AG_Password_cryptAlgorithmType: // 92027
        if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgType_typeAny) // 92024
            m_pImpl->m_DocumentProtection.m_sCryptAlgorithmType = "typeAny";
        break;
    case NS_ooxml::LN_AG_Password_cryptAlgorithmSid: // 92028
        m_pImpl->m_DocumentProtection.m_sCryptAlgorithmSid = sStringValue;
        break;
    case NS_ooxml::LN_AG_Password_cryptSpinCount: // 92029
        m_pImpl->m_DocumentProtection.m_CryptSpinCount = nIntValue;
        break;
    case NS_ooxml::LN_AG_Password_hash: // 92035
        m_pImpl->m_DocumentProtection.m_sHash = sStringValue;
        break;
    case NS_ooxml::LN_AG_Password_salt: // 92036
        m_pImpl->m_DocumentProtection.m_sSalt = sStringValue;
        break;
    case NS_ooxml::LN_CT_TrackChangesView_insDel:
        m_pImpl->m_bShowInsDelChanges = (nIntValue != 0);
        break;
@@ -505,7 +281,10 @@ void SettingsTable::lcl_sprm(Sprm& rSprm)
        resolveSprmProps(*this, rSprm);
        break;
    case NS_ooxml::LN_CT_Settings_documentProtection:
        resolveSprmProps(*this, rSprm);
        resolveSprmProps(*(m_pImpl->m_pDocumentProtection), rSprm);
        break;
    case NS_ooxml::LN_CT_Settings_writeProtection:
        resolveSprmProps(*(m_pImpl->m_pWriteProtection), rSprm);
        break;
    case NS_ooxml::LN_CT_Compat_usePrinterMetrics:
        m_pImpl->m_bUsePrinterMetrics = nIntValue;
@@ -678,12 +457,15 @@ bool SettingsTable::GetDoNotExpandShiftReturn() const

bool SettingsTable::GetProtectForm() const
{
    return m_pImpl->m_bProtectForm && m_pImpl->m_DocumentProtection.m_bEnforcement;
    return m_pImpl->m_pDocumentProtection->getProtectForm()
           && m_pImpl->m_pDocumentProtection->getEnforcement();
}

bool SettingsTable::GetReadOnly() const
{
    return m_pImpl->m_bReadOnly && m_pImpl->m_DocumentProtection.m_bEnforcement;
    return m_pImpl->m_pWriteProtection->getRecommended()
           || (m_pImpl->m_pDocumentProtection->getReadOnly()
               && m_pImpl->m_pDocumentProtection->getEnforcement());
}

bool SettingsTable::GetNoHyphenateCaps() const
@@ -733,9 +515,14 @@ uno::Sequence<beans::PropertyValue> SettingsTable::GetCompatSettings() const
    return comphelper::containerToSequence(m_pImpl->m_aCompatSettings);
}

css::uno::Sequence<css::beans::PropertyValue> SettingsTable::GetDocumentProtectionSettings() const
uno::Sequence<beans::PropertyValue> SettingsTable::GetDocumentProtectionSettings() const
{
    return m_pImpl->m_DocumentProtection.toSequence();
    return m_pImpl->m_pDocumentProtection->toSequence();
}

uno::Sequence<beans::PropertyValue> SettingsTable::GetWriteProtectionSettings() const
{
    return m_pImpl->m_pWriteProtection->toSequence();
}

const OUString & SettingsTable::GetCurrentDatabaseDataSource() const
@@ -772,7 +559,8 @@ void SettingsTable::ApplyProperties(uno::Reference<text::XTextDocument> const& x
    {
        xDocProps->setPropertyValue("RecordChanges", uno::makeAny( m_pImpl->m_bRecordChanges ) );
        // Password protected Record changes
        if ( m_pImpl->m_bRecordChanges && m_pImpl->m_bRedlineProtection && m_pImpl->m_DocumentProtection.m_bEnforcement )
        if (m_pImpl->m_bRecordChanges && m_pImpl->m_pDocumentProtection->getRedlineProtection()
            && m_pImpl->m_pDocumentProtection->getEnforcement())
        {
            // use dummy protection key to forbid disabling of Record changes without a notice
            // (extending the recent GrabBag support)    TODO support password verification...
diff --git a/writerfilter/source/dmapper/SettingsTable.hxx b/writerfilter/source/dmapper/SettingsTable.hxx
index 431bb5d..a0af31b 100644
--- a/writerfilter/source/dmapper/SettingsTable.hxx
+++ b/writerfilter/source/dmapper/SettingsTable.hxx
@@ -87,6 +87,8 @@ public:

    css::uno::Sequence<css::beans::PropertyValue> GetDocumentProtectionSettings() const;

    css::uno::Sequence<css::beans::PropertyValue> GetWriteProtectionSettings() const;

    void ApplyProperties(css::uno::Reference<css::text::XTextDocument> const& xDoc);

    bool GetCompatSettingValue(std::u16string_view sCompatName) const;
diff --git a/writerfilter/source/dmapper/WriteProtection.cxx b/writerfilter/source/dmapper/WriteProtection.cxx
new file mode 100644
index 0000000..724dbb7
--- /dev/null
+++ b/writerfilter/source/dmapper/WriteProtection.cxx
@@ -0,0 +1,143 @@
/* -*- 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 "WriteProtection.hxx"
#include "TagLogger.hxx"
#include <ooxml/resourceids.hxx>

using namespace com::sun::star;

namespace writerfilter::dmapper
{
WriteProtection::WriteProtection()
    : LoggedProperties("WriteProtection")
    , m_nCryptProviderType(NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES)
    , m_CryptSpinCount(0)
    , m_bRecommended(false)
{
}

WriteProtection::~WriteProtection() {}

void WriteProtection::lcl_attribute(Id nName, Value& val)
{
    int nIntValue = val.getInt();
    OUString sStringValue = val.getString();

    switch (nName)
    {
        case NS_ooxml::LN_AG_Password_cryptProviderType: // 92025
            m_nCryptProviderType = nIntValue;
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmClass: // 92026
            if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgClass_hash) // 92023
                m_sCryptAlgorithmClass = "hash";
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmType: // 92027
            if (nIntValue == NS_ooxml::LN_Value_doc_ST_AlgType_typeAny) // 92024
                m_sCryptAlgorithmType = "typeAny";
            break;
        case NS_ooxml::LN_AG_Password_cryptAlgorithmSid: // 92028
        {
            sal_Int32 nCryptAlgorithmSid = sStringValue.toInt32();
            switch (nCryptAlgorithmSid)
            {
                case 1:
                    m_sAlgorithmName = "MD2";
                    break;
                case 2:
                    m_sAlgorithmName = "MD4";
                    break;
                case 3:
                    m_sAlgorithmName = "MD5";
                    break;
                case 4:
                    m_sAlgorithmName = "SHA-1";
                    break;
                case 5:
                    m_sAlgorithmName = "MAC";
                    break;
                case 6:
                    m_sAlgorithmName = "RIPEMD";
                    break;
                case 7:
                    m_sAlgorithmName = "RIPEMD-160";
                    break;
                case 9:
                    m_sAlgorithmName = "HMAC";
                    break;
                case 12:
                    m_sAlgorithmName = "SHA-256";
                    break;
                case 13:
                    m_sAlgorithmName = "SHA-384";
                    break;
                case 14:
                    m_sAlgorithmName = "SHA-512";
                    break;
                default:; // 8, 10, 11, any other value: Undefined.
            }
        }
        break;
        case NS_ooxml::LN_AG_Password_cryptSpinCount: // 92029
            m_CryptSpinCount = nIntValue;
            break;
        case NS_ooxml::LN_AG_Password_hash: // 92035
            m_sHash = sStringValue;
            break;
        case NS_ooxml::LN_AG_Password_salt: // 92036
            m_sSalt = sStringValue;
            break;
        case NS_ooxml::LN_CT_WriteProtection_recommended:
            m_bRecommended = nIntValue;
            break;
        default:
        {
#ifdef DBG_UTIL
            TagLogger::getInstance().element("unhandled");
#endif
        }
    }
}

void WriteProtection::lcl_sprm(Sprm& /*rSprm*/) {}

uno::Sequence<beans::PropertyValue> WriteProtection::toSequence() const
{
    uno::Sequence<beans::PropertyValue> aResult;
    if (!m_sAlgorithmName.isEmpty() && !m_sSalt.isEmpty() && !m_sHash.isEmpty()
        && m_sCryptAlgorithmClass == "hash" && m_sCryptAlgorithmType == "typeAny")
    {
        aResult.realloc(4);
        aResult[0].Name = "algorithm-name";
        aResult[0].Value <<= m_sAlgorithmName;
        aResult[1].Name = "salt";
        aResult[1].Value <<= m_sSalt;
        aResult[2].Name = "iteration-count";
        aResult[2].Value <<= m_CryptSpinCount;
        aResult[3].Name = "hash";
        aResult[3].Value <<= m_sHash;
    }

    return aResult;
}

} //namespace writerfilter::dmapper

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/WriteProtection.hxx b/writerfilter/source/dmapper/WriteProtection.hxx
new file mode 100644
index 0000000..21b420b
--- /dev/null
+++ b/writerfilter/source/dmapper/WriteProtection.hxx
@@ -0,0 +1,58 @@
/* -*- 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 .
 */

#pragma once

#include "LoggedResources.hxx"
#include <com/sun/star/beans/PropertyValue.hpp>

namespace writerfilter::dmapper
{
class WriteProtection : public LoggedProperties
{
private:
    /** Provider type
         *
         * Possible values:
         *  "rsaAES"  - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaAES
         *  "rsaFull" - NS_ooxml::LN_Value_doc_ST_CryptProv_rsaFull
         */
    sal_Int32 m_nCryptProviderType;
    OUString m_sCryptAlgorithmClass;
    OUString m_sCryptAlgorithmType;
    sal_Int32 m_CryptSpinCount;
    OUString m_sAlgorithmName;
    OUString m_sHash;
    OUString m_sSalt;
    bool m_bRecommended;

    virtual void lcl_attribute(Id Name, Value& val) override;
    virtual void lcl_sprm(Sprm& sprm) override;

public:
    WriteProtection();
    virtual ~WriteProtection() override;

    css::uno::Sequence<css::beans::PropertyValue> toSequence() const;

    bool getRecommended() const { return m_bRecommended; }
};
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */