/* -*- 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 <digitalsignaturesdialog.hxx>
#include <certificatechooser.hxx>
#include <certificateviewer.hxx>
#include <biginteger.hxx>
#include <sax/tools/converter.hxx>
 
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/StorageFormats.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/security/NoPasswordException.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/security/CertificateValidity.hpp>
#include <com/sun/star/packages/WrongPasswordException.hpp>
#include <com/sun/star/security/CertificateKind.hpp>
#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
#include <com/sun/star/packages/manifest/ManifestReader.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/system/SystemShellExecuteException.hpp>
 
#include <osl/file.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/uri.hxx>
#include <sal/log.hxx>
 
#include <tools/date.hxx>
#include <tools/time.hxx>
#include <svtools/treelistentry.hxx>
 
#include <strings.hrc>
#include <resourcemanager.hxx>
 
#include <vcl/weld.hxx>
#include <unotools/configitem.hxx>
 
using namespace css::security;
using namespace css::uno;
using namespace css;
 
namespace
{
    class SaveODFItem: public utl::ConfigItem
    {
    private:
        sal_Int16 m_nODF;
 
        virtual void ImplCommit() override;
 
    public:
    virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
        SaveODFItem();
        //See group ODF in Common.xcs
        bool isLessODF1_2()
        {
            return m_nODF < 3;
        }
    };
 
    void SaveODFItem::ImplCommit() {}
    void SaveODFItem::Notify( const css::uno::Sequence< OUString >& ) {}
 
    SaveODFItem::SaveODFItem(): utl::ConfigItem("Office.Common/Save"), m_nODF(0)
    {
        OUString sDef("ODF/DefaultVersion");
        Sequence< css::uno::Any > aValues = GetProperties( Sequence<OUString>(&sDef,1) );
        if ( aValues.getLength() != 1)
            throw uno::RuntimeException(
                "[xmlsecurity] Could not open property Office.Common/Save/ODF/DefaultVersion",
                nullptr);
 
        sal_Int16 nTmp = 0;
        if ( !(aValues[0] >>= nTmp) )
            throw uno::RuntimeException(
                "[xmlsecurity]SaveODFItem::SaveODFItem(): Wrong Type!",
                nullptr );
 
        m_nODF = nTmp;
    }
}
 
DigitalSignaturesDialog::DigitalSignaturesDialog(
    vcl::Window* pParent,
    uno::Reference< uno::XComponentContext >& rxCtx, DocumentSignatureMode eMode,
    bool bReadOnly, const OUString& sODFVersion, bool bHasDocumentSignature)
    : ModalDialog(pParent, "DigitalSignaturesDialog", "xmlsec/ui/digitalsignaturesdialog.ui")
    , mxCtx(rxCtx)
    , maSignatureManager(rxCtx, eMode)
    , m_sODFVersion (sODFVersion)
    , m_bHasDocumentSignature(bHasDocumentSignature)
    , m_bWarningShowSignMacro(false)
{
    get(m_pHintDocFT, "dochint");
    get(m_pHintBasicFT, "macrohint");
    get(m_pHintPackageFT, "packagehint");
    get(m_pAdESCompliantCB, "adescompliant");
    get(m_pViewBtn, "view");
    get(m_pAddBtn, "sign");
    get(m_pRemoveBtn, "remove");
    get(m_pCloseBtn, "close");
    get(m_pStartCertMgrBtn, "start_certmanager");
    get(m_pSigsValidImg, "validimg");
    get(m_pSigsValidFI, "validft");
    get(m_pSigsInvalidImg, "invalidimg");
    get(m_pSigsInvalidFI, "invalidft");
    get(m_pSigsNotvalidatedImg, "notvalidatedimg");
    get(m_pSigsNotvalidatedFI, "notvalidatedft");
    get(m_pSigsOldSignatureImg, "oldsignatureimg");
    get(m_pSigsOldSignatureFI, "oldsignatureft");
 
    m_bAdESCompliant = !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
 
    Size aControlSize(375, 109);
    const long nControlWidth = aControlSize.Width();
    aControlSize = LogicToPixel(aControlSize, MapMode(MapUnit::MapAppFont));
    SvSimpleTableContainer *pSignatures = get<SvSimpleTableContainer>("signatures");
    pSignatures->set_width_request(aControlSize.Width());
    pSignatures->set_height_request(aControlSize.Height());
 
    m_pSignaturesLB = VclPtr<SvSimpleTable>::Create(*pSignatures);
    // Give the first column 6 percent, try to distribute the rest equally.
    static long aTabs[] = { 0, 6*nControlWidth/100, 25*nControlWidth/100, 44*nControlWidth/100, 62*nControlWidth/100, 81*nControlWidth/100 };
    m_pSignaturesLB->SetTabs(SAL_N_ELEMENTS(aTabs), aTabs);
 
    m_pSignaturesLB->InsertHeaderEntry("\t" + get<FixedText>("signed")->GetText() + "\t"
               + get<FixedText>("issued")->GetText() + "\t" + get<FixedText>("date")->GetText() + "\t"
               + get<FixedText>("description")->GetText() + "\t" + get<FixedText>("type")->GetText());
 
    mbVerifySignatures = true;
    mbSignaturesChanged = false;
 
    m_pSignaturesLB->SetSelectHdl( LINK( this, DigitalSignaturesDialog, SignatureHighlightHdl ) );
    m_pSignaturesLB->SetDoubleClickHdl( LINK( this, DigitalSignaturesDialog, SignatureSelectHdl ) );
 
    m_pAdESCompliantCB->SetToggleHdl( LINK( this, DigitalSignaturesDialog, AdESCompliantCheckBoxHdl ) );
    m_pAdESCompliantCB->Check(m_bAdESCompliant);
 
    m_pViewBtn->SetClickHdl( LINK( this, DigitalSignaturesDialog, ViewButtonHdl ) );
    m_pViewBtn->Disable();
 
    m_pAddBtn->SetClickHdl( LINK( this, DigitalSignaturesDialog, AddButtonHdl ) );
    if ( bReadOnly  )
        m_pAddBtn->Disable();
 
    m_pRemoveBtn->SetClickHdl( LINK( this, DigitalSignaturesDialog, RemoveButtonHdl ) );
    m_pRemoveBtn->Disable();
 
    m_pStartCertMgrBtn->SetClickHdl( LINK( this, DigitalSignaturesDialog, CertMgrButtonHdl ) );
 
    m_pCloseBtn->SetClickHdl( LINK( this, DigitalSignaturesDialog, OKButtonHdl) );
 
    switch( maSignatureManager.meSignatureMode )
    {
        case DocumentSignatureMode::Content:
            m_pHintDocFT->Show();
            break;
        case DocumentSignatureMode::Macros:
            m_pHintBasicFT->Show();
            break;
        case DocumentSignatureMode::Package:
            m_pHintPackageFT->Show();
            break;
    }
}
 
DigitalSignaturesDialog::~DigitalSignaturesDialog()
{
    disposeOnce();
}
 
void DigitalSignaturesDialog::dispose()
{
    m_pSignaturesLB.disposeAndClear();
    m_pHintDocFT.clear();
    m_pHintBasicFT.clear();
    m_pHintPackageFT.clear();
    m_pSigsValidImg.clear();
    m_pSigsValidFI.clear();
    m_pSigsInvalidImg.clear();
    m_pSigsInvalidFI.clear();
    m_pSigsNotvalidatedImg.clear();
    m_pSigsNotvalidatedFI.clear();
    m_pSigsOldSignatureImg.clear();
    m_pSigsOldSignatureFI.clear();
    m_pAdESCompliantCB.clear();
    m_pViewBtn.clear();
    m_pAddBtn.clear();
    m_pRemoveBtn.clear();
    m_pCloseBtn.clear();
    m_pStartCertMgrBtn.clear();
    ModalDialog::dispose();
}
 
bool DigitalSignaturesDialog::Init()
{
    bool bInit = maSignatureManager.init();
 
    SAL_WARN_IF( !bInit, "xmlsecurity.dialogs", "Error initializing security context!" );
 
    if ( bInit )
    {
        maSignatureManager.maSignatureHelper.SetStartVerifySignatureHdl( LINK( this, DigitalSignaturesDialog, StartVerifySignatureHdl ) );
    }
 
    return bInit;
}
 
void DigitalSignaturesDialog::SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStore )
{
    if (!rxStore.is())
    {
        // PDF supports AdES.
        m_bAdESCompliant = true;
        m_pAdESCompliantCB->Check(m_bAdESCompliant);
        return;
    }
 
    maSignatureManager.mxStore = rxStore;
    maSignatureManager.maSignatureHelper.SetStorage( maSignatureManager.mxStore, m_sODFVersion);
 
    Reference < css::packages::manifest::XManifestReader > xReader =
        css::packages::manifest::ManifestReader::create(mxCtx);
 
    uno::Reference<container::XNameAccess> xNameAccess(rxStore, uno::UNO_QUERY);
    if (!xNameAccess.is())
        return;
 
    if (xNameAccess->hasByName("META-INF"))
    {
        //Get the manifest.xml
        Reference < css::embed::XStorage > xSubStore(rxStore->openStorageElement(
                    "META-INF", css::embed::ElementModes::READ), UNO_QUERY_THROW);
 
        Reference< css::io::XInputStream > xStream(
            xSubStore->openStreamElement("manifest.xml", css::embed::ElementModes::READ),
            UNO_QUERY_THROW);
 
        maSignatureManager.m_manifest = xReader->readManifestSequence(xStream);
    }
}
 
void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream )
{
    maSignatureManager.mxSignatureStream = rxStream;
}
 
bool DigitalSignaturesDialog::canAddRemove()
{
    //FIXME: this func needs some cleanup, such as real split between
    //'canAdd' and 'canRemove' case
    bool ret = true;
 
    uno::Reference<container::XNameAccess> xNameAccess(maSignatureManager.mxStore, uno::UNO_QUERY);
    if (xNameAccess.is() && xNameAccess->hasByName("[Content_Types].xml"))
        // It's always possible to append an OOXML signature.
        return ret;
 
    if (!maSignatureManager.mxStore.is())
        // It's always possible to append a PDF signature.
        return ret;
 
    OSL_ASSERT(maSignatureManager.mxStore.is());
    bool bDoc1_1 = DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
    SaveODFItem item;
    bool bSave1_1 = item.isLessODF1_2();
 
    // see specification
    //cvs: specs/www/appwide/security/Electronic_Signatures_and_Security.sxw
    //Paragraph 'Behavior with regard to ODF 1.2'
    //For both, macro and document
    if ( (!bSave1_1  && bDoc1_1) || (bSave1_1 && bDoc1_1) )
    {
        //#4
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                  VclMessageType::Warning, VclButtonsType::Ok,
                                                  XsResId(STR_XMLSECDLG_OLD_ODF_FORMAT)));
        xBox->run();
        ret = false;
    }
 
    //As of OOo 3.2 the document signature includes in macrosignatures.xml. That is
    //adding a macro signature will break an existing document signature.
    //The sfx2 will remove the documentsignature when the user adds a macro signature
    if (maSignatureManager.meSignatureMode == DocumentSignatureMode::Macros
        && ret)
    {
        if (m_bHasDocumentSignature && !m_bWarningShowSignMacro)
        {
            //The warning says that the document signatures will be removed if the user
            //continues. He can then either press 'OK' or 'NO'
            //It the user presses 'Add' or 'Remove' several times then, then the warning
            //is shown every time until the user presses 'OK'. From then on, the warning
            //is not displayed anymore as long as the signatures dialog is alive.
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                      VclMessageType::Question, VclButtonsType::YesNo,
                                                      XsResId(STR_XMLSECDLG_QUERY_REMOVEDOCSIGNBEFORESIGN)));
            if (xBox->run() == RET_NO)
                ret = false;
            else
                m_bWarningShowSignMacro = true;
 
        }
    }
    return ret;
}
 
bool DigitalSignaturesDialog::canAdd()
{
    return canAddRemove();
}
 
bool DigitalSignaturesDialog::canRemove()
{
    bool bRet = true;
 
    if ( maSignatureManager.meSignatureMode == DocumentSignatureMode::Content )
    {
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                  VclMessageType::Question, VclButtonsType::YesNo,
                                                  XsResId(STR_XMLSECDLG_QUERY_REALLYREMOVE)));
        short nDlgRet = xBox->run();
        bRet = ( nDlgRet == RET_YES );
    }
 
    return (bRet && canAddRemove());
}
 
short DigitalSignaturesDialog::Execute()
{
    // Verify Signatures and add certificates to ListBox...
    mbVerifySignatures = true;
    ImplGetSignatureInformations(/*bUseTempStream=*/false, /*bCacheLastSignature=*/true);
    ImplFillSignaturesBox();
 
    // FIXME: Disable the "Use XAdES compliant signatures" checkbox if it is irrelevant. If it is
    // enabled, set its initial state based on existing signatures, if any.
 
    // If it is OOXML, the checkbox is irrelevant.
 
    // How to find out here whether it is OOXML? I don't want to create a SignatureStreamHelper and
    // check its nStorageFormat as that seems overly complicated and seems to have weird indirect
    // consequences, as I noticed when I tried to use DocumentSignatureManager::IsXAdESRelevant()
    // (which now is in #if 0).
 
    if (maSignatureManager.maCurrentSignatureInformations.size() > 0)
    {
        // If the document has only SHA-1 signatures we probably want it to stay that way?
    }
 
    // Only verify once, content will not change.
    // But for refreshing signature information, StartVerifySignatureHdl will be called after each add/remove
    mbVerifySignatures = false;
 
    return Dialog::Execute();
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureHighlightHdl, SvTreeListBox*, void)
{
    bool bSel = m_pSignaturesLB->FirstSelected();
    m_pViewBtn->Enable( bSel );
    if ( m_pAddBtn->IsEnabled() ) // not read only
        m_pRemoveBtn->Enable( bSel );
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, OKButtonHdl, Button*, void)
{
    if (mbSignaturesChanged)
        maSignatureManager.write(m_bAdESCompliant);
 
    EndDialog(RET_OK);
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureSelectHdl, SvTreeListBox*, bool)
{
    ImplShowSignaturesDetails();
    return false;
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, AdESCompliantCheckBoxHdl, CheckBox&, void)
{
    m_bAdESCompliant = m_pAdESCompliantCB->IsChecked();
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, ViewButtonHdl, Button*, void)
{
    ImplShowSignaturesDetails();
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, Button*, void)
{
    if( ! canAdd())
        return;
    try
    {
        std::vector<uno::Reference<xml::crypto::XXMLSecurityContext>> xSecContexts;
        xSecContexts.push_back(maSignatureManager.getSecurityContext());
        // Gpg signing is only possible with ODF >= 1.2 documents
        if (DocumentSignatureHelper::CanSignWithGPG(maSignatureManager.mxStore, m_sODFVersion))
            xSecContexts.push_back(maSignatureManager.getGpgSecurityContext());
 
        ScopedVclPtrInstance< CertificateChooser > aChooser( this, mxCtx, xSecContexts, UserAction::Sign );
        if ( aChooser->Execute() == RET_OK )
        {
            sal_Int32 nSecurityId;
            if (!maSignatureManager.add(aChooser->GetSelectedCertificates()[0], aChooser->GetSelectedSecurityContext(),
                                        aChooser->GetDescription(), nSecurityId, m_bAdESCompliant))
                return;
            mbSignaturesChanged = true;
 
            xml::crypto::SecurityOperationStatus nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
 
            if (maSignatureManager.mxStore.is())
                // In the PDF case the signature information is only available after parsing.
                nStatus = maSignatureManager.maSignatureHelper.GetSignatureInformation( nSecurityId ).nStatus;
 
            if ( nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED )
            {
                mbSignaturesChanged = true;
 
                // Can't simply remember current information, need parsing for getting full information :(
                // We need to verify the signatures again, otherwise the status in the signature information
                // will not contain
                // SecurityOperationStatus_OPERATION_SUCCEEDED
                mbVerifySignatures = true;
                ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
                ImplFillSignaturesBox();
            }
        }
    }
    catch ( uno::Exception& )
    {
        OSL_FAIL( "Exception while adding a signature!" );
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                  VclMessageType::Error, VclButtonsType::Ok,
                                                  XsResId(STR_XMLSECDLG_SIGNING_FAILED)));
        xBox->run();
        // Don't keep invalid entries...
        ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
        ImplFillSignaturesBox();
    }
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, RemoveButtonHdl, Button*, void)
{
    if (!canRemove())
        return;
    if( m_pSignaturesLB->FirstSelected() )
    {
        try
        {
            sal_uInt16 nSelected = static_cast<sal_uInt16>(reinterpret_cast<sal_uIntPtr>( m_pSignaturesLB->FirstSelected()->GetUserData() ));
            maSignatureManager.remove(nSelected);
 
            mbSignaturesChanged = true;
 
            ImplFillSignaturesBox();
        }
        catch ( uno::Exception& )
        {
            OSL_FAIL( "Exception while removing a signature!" );
            // Don't keep invalid entries...
            ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/true);
            ImplFillSignaturesBox();
        }
    }
}
 
IMPL_STATIC_LINK(DigitalSignaturesDialog, CertMgrButtonHdl, Button*, pButton, void)
{
#ifdef _WIN32
    // FIXME: call GpgME::dirInfo("bindir") somewhere in
    // SecurityEnvironmentGpg or whatnot
    // FIXME: perhaps poke GpgME for uiserver, and hope it returns something useful?
    const OUString aGUIServers[] = { OUString("kleopatra.exe"), OUString("launch-gpa.exe"), OUString("gpa.exe"),
                                     OUString("bin\\kleopatra.exe"), OUString("bin\\launch-gpa.exe"), OUString("bin\\gpa.exe") };
    const char* const cPath = "C:\\Program Files (x86)\\GNU\\GnuPG";
#else
    const OUString aGUIServers[] = { OUString("kleopatra"), OUString("seahorse"),  OUString("gpa"), OUString("kgpg") };
    const char* cPath = getenv("PATH");
#endif
 
    if (cPath)
    {
       OUString aPath(cPath, strlen(cPath), osl_getThreadTextEncoding());
       OUString sFoundGUIServer, sExecutable;
 
       for ( auto const &rServer : aGUIServers )
       {
           osl::FileBase::RC searchError = osl::File::searchFileURL(rServer, aPath, sFoundGUIServer );
           if (searchError == osl::FileBase::E_None)
           {
               osl::File::getSystemPathFromFileURL( sFoundGUIServer, sExecutable );
               break;
           }
 
       }
 
       if ( !sExecutable.isEmpty() )
       {
           uno::Reference< uno::XComponentContext > xContext =
               ::comphelper::getProcessComponentContext();
           uno::Reference< css::system::XSystemShellExecute > xSystemShell(
                    css::system::SystemShellExecute::create(xContext) );
 
           xSystemShell->execute( sExecutable, OUString(),
               css::system::SystemShellExecuteFlags::DEFAULTS );
       }
       else
       {
           std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pButton->GetFrameWeld(),
                                                         VclMessageType::Info, VclButtonsType::Ok,
                                                         XsResId(STR_XMLSECDLG_NO_CERT_MANAGER)));
           xInfoBox->run();
       }
    }
}
 
IMPL_LINK_NOARG(DigitalSignaturesDialog, StartVerifySignatureHdl, LinkParamNone*, bool)
{
    return mbVerifySignatures;
}
 
void DigitalSignaturesDialog::ImplFillSignaturesBox()
{
    m_pSignaturesLB->Clear();
 
    size_t nInfos = maSignatureManager.maCurrentSignatureInformations.size();
    size_t nValidSigs = 0, nValidCerts = 0;
    bool bAllNewSignatures = true;
 
    if( nInfos )
    {
        for( size_t n = 0; n < nInfos; ++n )
        {
            DocumentSignatureAlgorithm mode = DocumentSignatureHelper::getDocumentAlgorithm(
                m_sODFVersion, maSignatureManager.maCurrentSignatureInformations[n]);
            std::vector< OUString > aElementsToBeVerified;
            if (maSignatureManager.mxStore.is())
                aElementsToBeVerified = DocumentSignatureHelper::CreateElementList(maSignatureManager.mxStore, maSignatureManager.meSignatureMode, mode);
 
            const SignatureInformation& rInfo = maSignatureManager.maCurrentSignatureInformations[n];
            uno::Reference< css::security::XCertificate > xCert = getCertificate(rInfo);
 
            OUString aSubject;
            OUString aIssuer;
            OUString aDateTimeStr;
            OUString aDescription;
            OUString aType;
 
            bool bSigValid = false;
            bool bCertValid = false;
            if( xCert.is() )
            {
                //check the validity of the cert
                try {
                    sal_Int32 certResult = getSecurityEnvironmentForCertificate(xCert)->verifyCertificate(xCert,
                                                                                                          Sequence<uno::Reference<security::XCertificate> >());
 
                    bCertValid = certResult == css::security::CertificateValidity::VALID;
                    if ( bCertValid )
                        nValidCerts++;
 
                } catch (css::uno::SecurityException& ) {
                    OSL_FAIL("Verification of certificate failed");
                    bCertValid = false;
                }
 
                aSubject = XmlSec::GetContentPart( xCert->getSubjectName() );
                aIssuer = XmlSec::GetContentPart( xCert->getIssuerName() );
            }
            else if (!rInfo.ouGpgCertificate.isEmpty())
            {
                // In case we don't have the gpg key locally, get some data from the document
                aIssuer = rInfo.ouGpgOwner;
            }
 
            aDateTimeStr = XmlSec::GetDateTimeString( rInfo.stDateTime );
            aDescription = rInfo.ouDescription;
 
            // Decide type string.
            if (maSignatureManager.mxStore.is())
            {
                // OpenPGP
                if (!rInfo.ouGpgCertificate.isEmpty())
                    aType = "OpenPGP";
                // XML based: XAdES or not.
                else if (!rInfo.ouCertDigest.isEmpty())
                    aType = "XAdES";
                else
                    aType = "XML-DSig";
            }
            else
            {
                // Assume PDF: PAdES or not.
                if (rInfo.bHasSigningCertificate)
                    aType = "PAdES";
                else
                    aType = "PDF";
            }
 
            bSigValid = ( rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED );
 
            if ( bSigValid )
            {
                 bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
                      aElementsToBeVerified, rInfo, mode);
 
                if( bSigValid )
                    nValidSigs++;
            }
 
            Image aImage;
            if (!bSigValid)
            {
                aImage = m_pSigsInvalidImg->GetImage();
            }
            else if (bSigValid && !bCertValid)
            {
                aImage = m_pSigsNotvalidatedImg->GetImage();
            }
            //Check if the signature is a "old" document signature, that is, which was created
            //by an version of OOo previous to 3.2
            // If there is no storage, then it's pointless to check storage
            // stream references.
            else if (maSignatureManager.meSignatureMode == DocumentSignatureMode::Content
                && bSigValid && bCertValid && (maSignatureManager.mxStore.is() && !DocumentSignatureHelper::isOOo3_2_Signature(
                maSignatureManager.maCurrentSignatureInformations[n])))
            {
                aImage = m_pSigsNotvalidatedImg->GetImage();
                bAllNewSignatures = false;
            }
            else if (maSignatureManager.meSignatureMode == DocumentSignatureMode::Content
                && bSigValid && bCertValid && DocumentSignatureHelper::isOOo3_2_Signature(
                maSignatureManager.maCurrentSignatureInformations[n]))
            {
                aImage = m_pSigsValidImg->GetImage();
            }
            else if (maSignatureManager.meSignatureMode == DocumentSignatureMode::Macros
                && bSigValid && bCertValid)
            {
                aImage = m_pSigsValidImg->GetImage();
            }
 
            SvTreeListEntry* pEntry = m_pSignaturesLB->InsertEntry( OUString(), aImage, aImage );
            m_pSignaturesLB->SetEntryText( aSubject, pEntry, 1 );
            m_pSignaturesLB->SetEntryText( aIssuer, pEntry, 2 );
            m_pSignaturesLB->SetEntryText( aDateTimeStr, pEntry, 3 );
            m_pSignaturesLB->SetEntryText(aDescription, pEntry, 4);
            m_pSignaturesLB->SetEntryText(aType, pEntry, 5);
            pEntry->SetUserData( reinterpret_cast<void*>(n) );     // misuse user data as index
        }
    }
 
    bool bAllSigsValid = (nValidSigs == nInfos);
    bool bAllCertsValid = (nValidCerts == nInfos);
    bool bShowValidState = nInfos && (bAllSigsValid && bAllCertsValid && bAllNewSignatures);
 
    m_pSigsValidImg->Show( bShowValidState);
    m_pSigsValidFI->Show( bShowValidState );
 
    bool bShowInvalidState = nInfos && !bAllSigsValid;
 
    m_pSigsInvalidImg->Show( bShowInvalidState );
    m_pSigsInvalidFI->Show( bShowInvalidState );
 
    bool bShowNotValidatedState = nInfos && bAllSigsValid && !bAllCertsValid;
 
    m_pSigsNotvalidatedImg->Show(bShowNotValidatedState);
    m_pSigsNotvalidatedFI->Show(bShowNotValidatedState);
 
    //bAllNewSignatures is always true if we are not in document mode
    bool bShowOldSignature = nInfos && bAllSigsValid && bAllCertsValid && !bAllNewSignatures;
    m_pSigsOldSignatureImg->Show(bShowOldSignature);
    m_pSigsOldSignatureFI->Show(bShowOldSignature);
 
    SignatureHighlightHdl( nullptr );
}
 
uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(const SignatureInformation& rInfo)
{
    uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = maSignatureManager.getSecurityEnvironment();
    uno::Reference<xml::crypto::XSecurityEnvironment> xGpgSecEnv = maSignatureManager.getGpgSecurityEnvironment();
    uno::Reference<security::XCertificate> xCert;
 
    //First we try to get the certificate which is embedded in the XML Signature
    if (xSecEnv.is() && !rInfo.ouX509Certificate.isEmpty())
        xCert = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate);
    else {
        //There must be an embedded certificate because we use it to get the
        //issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName
        //because it could be modified by an attacker. The issuer is displayed
        //in the digital signature dialog.
        //Comparing the X509IssuerName with the one from the X509Certificate in order
        //to find out if the X509IssuerName was modified does not work. See #i62684
        SAL_WARN( "xmlsecurity.dialogs", "Could not find embedded certificate!");
    }
 
    //In case there is no embedded certificate we try to get it from a local store
    if (!xCert.is() && xSecEnv.is())
        xCert = xSecEnv->getCertificate( rInfo.ouX509IssuerName, xmlsecurity::numericStringToBigInteger( rInfo.ouX509SerialNumber ) );
    if (!xCert.is() && xGpgSecEnv.is())
        xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("") );
 
    SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" );
 
    return xCert;
}
 
uno::Reference<xml::crypto::XSecurityEnvironment> DigitalSignaturesDialog::getSecurityEnvironmentForCertificate(const uno::Reference<security::XCertificate>& xCert)
{
    if (xCert->getCertificateKind() == CertificateKind_OPENPGP)
        return maSignatureManager.getGpgSecurityEnvironment();
    else if (xCert->getCertificateKind() == CertificateKind_X509)
        return maSignatureManager.getSecurityEnvironment();
 
    throw RuntimeException("Unknown certificate kind");
}
 
//If bUseTempStream is true then the temporary signature stream is used.
//Otherwise the real signature stream is used.
void DigitalSignaturesDialog::ImplGetSignatureInformations(bool bUseTempStream, bool bCacheLastSignature)
{
    maSignatureManager.read(bUseTempStream, bCacheLastSignature);
    mbVerifySignatures = false;
}
 
void DigitalSignaturesDialog::ImplShowSignaturesDetails()
{
    if( m_pSignaturesLB->FirstSelected() )
    {
        sal_uInt16 nSelected = static_cast<sal_uInt16>(reinterpret_cast<sal_uIntPtr>( m_pSignaturesLB->FirstSelected()->GetUserData() ));
        const SignatureInformation& rInfo = maSignatureManager.maCurrentSignatureInformations[ nSelected ];
        uno::Reference<security::XCertificate> xCert = getCertificate(rInfo);
 
        if ( xCert.is() )
        {
            uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = getSecurityEnvironmentForCertificate(xCert);
            ScopedVclPtrInstance<CertificateViewer> aViewer(this, xSecEnv, xCert, false);
            aViewer->Execute();
        }
        else
        {
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          XsResId(STR_XMLSECDLG_NO_CERT_FOUND)));
            xInfoBox->run();
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression 'cPath' is always true.

V560 A part of conditional expression is always true: bSigValid.

V560 A part of conditional expression is always true: bSigValid.

V560 A part of conditional expression is always true: bCertValid.

V560 A part of conditional expression is always true: bSigValid.

V560 A part of conditional expression is always true: bCertValid.

V560 A part of conditional expression is always true: bSigValid.

V560 A part of conditional expression is always true: bCertValid.