tdf#155665 Adding option to remember signatures for each save

Added the option in digital signatures dialog to remember used
signature.

Implemented ResignDocument function in objserv.cxx to resign after
every save in case the option to remember signature is on.
Added a new dialog box that checks whether there is a need to
remember the signature.

Change-Id: Ia7dbcc952044e9542e3fe6cd84b5d6633fcd1461
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152687
Reviewed-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
Tested-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index b29fb43..15533c2 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -190,6 +190,7 @@ private:
                                                  // sal_False := new object
    bool                        bIsInGenerateThumbnail; //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
    bool                        mbAvoidRecentDocs; ///< Avoid adding to the recent documents list, if not necessary.
    bool                        bRememberSignature; // Do we want to remember the signature.

    enum TriState               {undefined, yes, no};
    TriState                    mbContinueImportOnFilterExceptions = undefined; // try to import as much as possible
@@ -199,6 +200,8 @@ private:
    SAL_DLLPRIVATE void UpdateTime_Impl(const css::uno::Reference<
        css::document::XDocumentProperties> & i_xDocProps);

    css::uno::Sequence< css::security::DocumentSignatureInformation > rSignatureInfosRemembered;

    SAL_DLLPRIVATE bool SaveTo_Impl(SfxMedium &rMedium, const SfxItemSet* pSet );

protected:
@@ -350,13 +353,14 @@ public:
    void AfterSigning(bool bSignSuccess, bool bSignScriptingContent);
    bool HasValidSignatures() const;
    SignatureState              GetDocumentSignatureState();
    void                        SignDocumentContent(weld::Window* pDialogParent);
    bool                        SignDocumentContent(weld::Window* pDialogParent);
    css::uno::Sequence<css::security::DocumentSignatureInformation> GetDocumentSignatureInformation(
        bool bScriptingContent,
        const css::uno::Reference<css::security::XDocumentDigitalSignatures>& xSigner
        = css::uno::Reference<css::security::XDocumentDigitalSignatures>());

    bool SignDocumentContentUsingCertificate(const css::uno::Reference<css::security::XCertificate>& xCertificate);
    bool ResignDocument(css::uno::Sequence< css::security::DocumentSignatureInformation >& rSignaturesInfo);

    void SignSignatureLine(weld::Window* pDialogParent, const OUString& aSignatureLineId,
                           const css::uno::Reference<css::security::XCertificate>& xCert,
@@ -364,7 +368,7 @@ public:
                           const css::uno::Reference<css::graphic::XGraphic>& xInvalidGraphic,
                           const OUString& aComment);
    SignatureState              GetScriptingSignatureState();
    void                        SignScriptingContent(weld::Window* pDialogParent);
    bool                        SignScriptingContent(weld::Window* pDialogParent);
    DECL_DLLPRIVATE_LINK(SignDocumentHandler, weld::Button&, void);

    virtual std::shared_ptr<SfxDocumentInfoDialog> CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet& rItemSet);
@@ -458,6 +462,8 @@ public:
    /// Don't add to the recent documents - it's an expensive operation, sometimes it is not wanted.
    bool                        IsAvoidRecentDocs() const { return mbAvoidRecentDocs; }

    bool                        IsRememberingSignature() const { return bRememberSignature; }

    /// Don't add to the recent documents - it's an expensive operation, sometimes it is not wanted.
    void                        AvoidRecentDocs(bool bAvoid) { mbAvoidRecentDocs = bAvoid; }

diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc
index bd2c0b5..3ccc50c 100644
--- a/include/sfx2/strings.hrc
+++ b/include/sfx2/strings.hrc
@@ -149,6 +149,7 @@
#define RID_SVXSTR_XMLSEC_QUERY_LOSINGSIGNATURE NC_("RID_SVXSTR_XMLSEC_QUERY_LOSINGSIGNATURE", "Saving will remove all existing signatures.\nDo you want to continue saving the document?")
#define RID_SVXSTR_XMLSEC_QUERY_SAVEBEFORESIGN  NC_("RID_SVXSTR_XMLSEC_QUERY_SAVEBEFORESIGN", "The document has to be saved before it can be signed.\nDo you want to save the document?")
#define STR_QUERY_CANCELCHECKOUT                NC_("STR_QUERY_CANCELCHECKOUT", "This will discard all changes on the server since check-out.\nDo you want to proceed?")
#define STR_QUERY_REMEMBERSIGNATURE             NC_("STR_QUERY_REMEMBERSIGNATURE", "Do you want to remember that signature for each save?")
#define STR_INFO_WRONGDOCFORMAT                 NC_("STR_INFO_WRONGDOCFORMAT", "This document must be saved in OpenDocument file format before it can be digitally signed.")
#define RID_XMLSEC_DOCUMENTSIGNED               NC_("RID_XMLSEC_DOCUMENTSIGNED", " (Signed)")
#define STR_EMBEDDED_TITLE                      NC_("STR_EMBEDDED_TITLE", " (Embedded document)")
diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx
index b6d4ce6..272d68f 100644
--- a/sfx2/source/doc/guisaveas.cxx
+++ b/sfx2/source/doc/guisaveas.cxx
@@ -1487,11 +1487,14 @@ bool SfxStoringHelper::GUIStoreModel( const uno::Reference< frame::XModel >& xMo

    if (!comphelper::LibreOfficeKit::isActive() && !( m_nStoreMode & EXPORT_REQUESTED ) )
    {
        SfxObjectShell* pDocShell = SfxViewShell::Current()->GetObjectShell();

        // if it is no export, warn user that the signature will be removed
        if (  SignatureState::OK == nDocumentSignatureState
        if (  !pDocShell->IsRememberingSignature()
           && (SignatureState::OK == nDocumentSignatureState
           || SignatureState::INVALID == nDocumentSignatureState
           || SignatureState::NOTVALIDATED == nDocumentSignatureState
           || SignatureState::PARTIAL_OK == nDocumentSignatureState)
           || SignatureState::PARTIAL_OK == nDocumentSignatureState) )
        {
            std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(SfxStoringHelper::GetModelWindow(xModel),
                                                             VclMessageType::Question, VclButtonsType::YesNo, SfxResId(RID_SVXSTR_XMLSEC_QUERY_LOSINGSIGNATURE)));
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index 78c4fd0..f321cbfc 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -532,6 +532,8 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)

    sal_uInt16 nId = rReq.GetSlot();

    bool bHaveWeSigned = false;

    if( SID_SIGNATURE == nId || SID_MACRO_SIGNATURE == nId )
    {
        if ( QueryHiddenInformation( HiddenWarningFact::WhenSigning, nullptr ) == RET_YES )
@@ -541,7 +543,8 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
                uno::Reference<security::XCertificate> xCertificate = GetSignPDFCertificate();
                if (xCertificate.is())
                {
                    SignDocumentContentUsingCertificate(xCertificate);

                    bHaveWeSigned |= SignDocumentContentUsingCertificate(xCertificate);

                    // Reload to show how the PDF actually looks like after signing. This also
                    // changes "finish signing" on the infobar back to "sign document" as a side
@@ -568,14 +571,31 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
                }
                else
                {
                    SignDocumentContent(pDialogParent);
                    bHaveWeSigned |= SignDocumentContent(pDialogParent);
                }
            }
            else
            {
                SignScriptingContent(pDialogParent);
                bHaveWeSigned |= SignScriptingContent(pDialogParent);
            }
        }

        if ( bHaveWeSigned && HasValidSignatures() )
        {
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( pDialogParent,
                                                      VclMessageType::Question, VclButtonsType::YesNo, SfxResId(STR_QUERY_REMEMBERSIGNATURE)));
            if (xBox->run() == RET_YES)
            {
                rSignatureInfosRemembered = GetDocumentSignatureInformation(false);
                bRememberSignature = true;
            }
            else
            {
                rSignatureInfosRemembered = uno::Sequence< security::DocumentSignatureInformation >();
                bRememberSignature = false;
            }
        }

        return;
    }

@@ -1207,6 +1227,9 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
                }
            }

            if (nId == SID_SAVEDOC && bRememberSignature && rSignatureInfosRemembered.hasElements())
                ResignDocument(rSignatureInfosRemembered);

            rReq.SetReturnValue( SfxBoolItem(0, nErrorCode == ERRCODE_NONE ) );

            ResetError();
@@ -1527,9 +1550,10 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
                        aInfobarType = InfobarType::DANGER;
                        break;
                    case SignatureState::INVALID:
                        sMessage = SfxResId(STR_SIGNATURE_INVALID);
                        // If we are remembering the certificates, it should be kept as valid
                        sMessage = SfxResId(bRememberSignature ? STR_SIGNATURE_OK : STR_SIGNATURE_INVALID);
                        // Warning only, I've tried Danger and it looked too scary
                        aInfobarType = InfobarType::WARNING;
                        aInfobarType = ( bRememberSignature ? InfobarType::INFO : InfobarType::WARNING );
                        break;
                    case SignatureState::NOTVALIDATED:
                        sMessage = SfxResId(STR_SIGNATURE_NOTVALIDATED);
@@ -1880,11 +1904,15 @@ bool SfxObjectShell::PrepareForSigning(weld::Window* pDialogParent)
        if (nVersion >= SvtSaveOptions::ODFSVER_012)
        {
            OUString sQuestion(bHasSign ? SfxResId(STR_XMLSEC_QUERY_SAVESIGNEDBEFORESIGN) : SfxResId(RID_SVXSTR_XMLSEC_QUERY_SAVEBEFORESIGN));
            std::unique_ptr<weld::MessageDialog> xQuestion(Application::CreateMessageDialog(pDialogParent,
            std::unique_ptr<weld::MessageDialog> xQuestion;

            if (!bRememberSignature)
            {
                xQuestion = std::unique_ptr<weld::MessageDialog>(Application::CreateMessageDialog(pDialogParent,
                                                           VclMessageType::Question, VclButtonsType::YesNo, sQuestion));
            }


            if (xQuestion->run() == RET_YES)
            if ( bRememberSignature || ( xQuestion != nullptr && xQuestion->run() == RET_YES ) )
            {
                sal_uInt16 nId = SID_SAVEDOC;
                if ( !GetMedium() || GetMedium()->GetName().isEmpty() )
@@ -1932,7 +1960,7 @@ bool SfxObjectShell::PrepareForSigning(weld::Window* pDialogParent)

    // the document is not modified currently, so it can not become modified after signing
    pImpl->m_bAllowModifiedBackAfterSigning = false;
    if ( IsEnableSetModified() )
    if ( IsEnableSetModified() || /*bRememberSignature == */true )
    {
        EnableSetModified( false );
        pImpl->m_bAllowModifiedBackAfterSigning = true;
@@ -1968,7 +1996,7 @@ void SfxObjectShell::AfterSigning(bool bSignSuccess, bool bSignScriptingContent)
    if ( bSignSuccess )
        RecheckSignature(bSignScriptingContent);

    if ( pImpl->m_bAllowModifiedBackAfterSigning )
    if ( pImpl->m_bAllowModifiedBackAfterSigning || /* bRememberSignature ==*/ true )
        EnableSetModified();
}

@@ -2041,17 +2069,36 @@ SignatureState SfxObjectShell::GetDocumentSignatureState()
    return ImplGetSignatureState();
}

void SfxObjectShell::SignDocumentContent(weld::Window* pDialogParent)
bool SfxObjectShell::SignDocumentContent(weld::Window* pDialogParent)
{
    if (!PrepareForSigning(pDialogParent))
        return;
        return false;

    if (CheckIsReadonly(false, pDialogParent))
        return;
        return false;

    bool bSignSuccess = GetMedium()->SignContents_Impl(pDialogParent, false, HasValidSignatures());

    AfterSigning(bSignSuccess, false);

    return bSignSuccess;
}

bool SfxObjectShell::ResignDocument(uno::Sequence< security::DocumentSignatureInformation >& rSignaturesInfo)
{
    bool bSignSuccess = true;

    // This should be at most one element, automatic iteration to avoid pointing issues in case no signs
    for (auto & rInfo : rSignaturesInfo)
    {
        auto xCert = rInfo.Signer;
        if (xCert.is())
        {
            bSignSuccess &= SignDocumentContentUsingCertificate(xCert);
        }
    }

    return bSignSuccess;
}

bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertificate>& xCertificate)
@@ -2162,17 +2209,19 @@ SignatureState SfxObjectShell::GetScriptingSignatureState()
    return ImplGetSignatureState( true );
}

void SfxObjectShell::SignScriptingContent(weld::Window* pDialogParent)
bool SfxObjectShell::SignScriptingContent(weld::Window* pDialogParent)
{
    if (!PrepareForSigning(pDialogParent))
        return;
        return false;

    if (CheckIsReadonly(true, pDialogParent))
        return;
        return false;

    bool bSignSuccess = GetMedium()->SignContents_Impl(pDialogParent, true, HasValidSignatures());

    AfterSigning(bSignSuccess, true);

    return bSignSuccess;
}

const uno::Sequence<sal_Int8>& SfxObjectShell::getUnoTunnelId()
diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx
index 990fa5d..0fd029f 100644
--- a/xmlsecurity/source/component/documentdigitalsignatures.cxx
+++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx
@@ -869,7 +869,13 @@ bool DocumentDigitalSignatures::signWithCertificateImpl(
    aSignatureManager.setSignatureStream(xStream);
    aSignatureManager.setModel(xModel);

    Reference<XXMLSecurityContext> xSecurityContext = aSignatureManager.getSecurityContext();
    Reference<XXMLSecurityContext> xSecurityContext;
    Reference<XServiceInfo> xServiceInfo(xCertificate, UNO_QUERY);
    if (xServiceInfo->getImplementationName()
        == "com.sun.star.xml.security.gpg.XCertificate_GpgImpl")
        xSecurityContext = aSignatureManager.getGpgSecurityContext();
    else
        xSecurityContext = aSignatureManager.getSecurityContext();

    sal_Int32 nSecurityId;