# Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/

require 'rails_helper'

RSpec.describe SecureMailing::SMIME do
  before do
    Setting.set('smime_integration', true)
  end

  let(:raw_body) { 'Some text' }

  let(:system_email_address)      { 'smime1@example.com' }
  let(:customer_email_address)    { 'smime2@example.com' }
  let(:cc_customer_email_address) { 'smime3@example.com' }

  let(:sender_certificate_subject)       { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{sender_email_address}" }
  let(:recipient_certificate_subject)    { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{recipient_email_address}" }
  let(:cc_recipient_certificate_subject) { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{cc_recipient_email_address}" }

  let(:expired_email_address) { 'expiredsmime1@example.com' }

  let(:ca_certificate_subject) { '/emailAddress=RootCA@example.com/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com' }

  let(:intermediate_ca_certificate_subject) { '/C=DE/ST=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=IntermediateCA@example.com' }

  let(:content_type) { 'text/plain' }

  def build_mail
    Channel::EmailBuild.build(
      from:         sender_email_address,
      to:           recipient_email_address,
      cc:           cc_recipient_email_address,
      body:         raw_body,
      content_type: content_type,
      security:     security_preferences
    )
  end

  describe '.outgoing' do

    shared_examples 'HttpLog writer' do |status|

      it "logs #{status}" do
        expect do
          build_mail
        rescue
          # allow failures
        end.to change(HttpLog, :count).by(1)
        expect(HttpLog.last.attributes).to include('direction' => 'out', 'status' => status)
      end
    end

    let(:sender_email_address)       { system_email_address }
    let(:recipient_email_address)    { customer_email_address }
    let(:cc_recipient_email_address) { cc_customer_email_address }

    context 'without security' do
      let(:security_preferences) do
        nil
      end

      it 'builds mail' do
        expect(build_mail.body).not_to match(SecureMailing::SMIME::Incoming::EXPRESSION_SIGNATURE)
        expect(build_mail.body).not_to match(SecureMailing::SMIME::Incoming::EXPRESSION_MIME)
        expect(build_mail.body).to eq(raw_body)
      end
    end

    context 'signing' do

      let(:security_preferences) do
        {
          type:       'S/MIME',
          sign:       {
            success: true,
          },
          encryption: {
            success: false,
          },
        }
      end

      context 'private key present' do

        let!(:sender_certificate) do
          create(:smime_certificate, :with_private, fixture: system_email_address)
        end

        it 'builds mail' do
          expect(build_mail.body).to match(SecureMailing::SMIME::Incoming::EXPRESSION_SIGNATURE)
        end

        it_behaves_like 'HttpLog writer', 'success'

        context 'expired certificate' do
          let(:system_email_address) { expired_email_address }

          it 'raises exception' do
            expect { build_mail }.to raise_error RuntimeError
          end

          it_behaves_like 'HttpLog writer', 'failed'
        end

        context 'when message is 7bit or 8bit encoded' do

          let(:mail) do
            smime_mail = build_mail

            mail = Channel::EmailParser.new.parse(smime_mail.to_s)
            SecureMailing.incoming(mail)

            mail
          end

          context 'when Content-Type is text/plain' do
            let(:raw_body) { "\r\n\r\n@john.doe, now known as John Dóe has accepted your invitation to join the Administrator / htmltest project.\r\n\r\nhttp://169.254.169.254:3000/root/htmltest\r\n\r\n-- \r\nYou're receiving this email because of your account on 169.254.169.254.\r\n\r\n\r\n\r\n" }

            it 'verifies' do
              expect(mail['x-zammad-article-preferences']['security']['sign']['success']).to be true
            end
          end

          context 'when Content-Type is text/html' do
            let(:content_type) { 'text/html' }
            let(:raw_body) { "<div><ul><li><p>an \nexample „Text“ with ümläütß. </p></li></ul></div>" }

            it 'verifies' do
              expect(mail['x-zammad-article-preferences']['security']['sign']['success']).to be true
            end
          end
        end

        context 'when certificate chain is present' do
          let(:system_email_address) { 'chain@example.com' }

          let!(:chain) do
            [
              sender_certificate,
              create(:smime_certificate, fixture: 'IntermediateCA'),
              create(:smime_certificate, fixture: 'RootCA'),
            ]
          end

          let(:p7enc) do
            mail = Channel::EmailParser.new.parse(build_mail.to_s)
            OpenSSL::PKCS7.read_smime(mail[:raw])
          end

          it 'is included in the generated mail' do
            expect(p7enc.certificates.map(&:subject).sort).to eq(chain.map { |x| x.parsed.subject }.sort)
          end
        end
      end

      context 'no private key present' do
        before do
          create(:smime_certificate, fixture: system_email_address)
        end

        it 'raises exception' do
          expect { build_mail }.to raise_error RuntimeError
        end

        it_behaves_like 'HttpLog writer', 'failed'
      end
    end

    context 'encryption' do

      let(:security_preferences) do
        {
          type:       'S/MIME',
          sign:       {
            success: false,
          },
          encryption: {
            success: true,
          },
        }
      end

      context 'public key present' do
        before do
          create(:smime_certificate, fixture: recipient_email_address)
          create(:smime_certificate, fixture: cc_recipient_email_address)
        end

        it 'builds mail' do
          mail = build_mail

          expect(mail['Content-Type'].value).to match(SecureMailing::SMIME::Incoming::EXPRESSION_MIME)
          expect(mail.body).not_to include(raw_body)
        end

        it_behaves_like 'HttpLog writer', 'success'

        context 'expired certificate' do

          let(:recipient_email_address) { expired_email_address }

          it 'raises exception' do
            expect { build_mail }.to raise_error RuntimeError
          end

          it_behaves_like 'HttpLog writer', 'failed'
        end
      end

      context 'no public key present' do

        it 'raises exception' do
          expect { build_mail }.to raise_error ActiveRecord::RecordNotFound
        end

        it_behaves_like 'HttpLog writer', 'failed'
      end
    end
  end

  describe '.incoming' do

    shared_examples 'HttpLog writer' do |status|

      it "logs #{status}" do
        expect do
          mail
        rescue
          # allow failures
        end.to change(HttpLog, :count).by(2)
        expect(HttpLog.last.attributes).to include('direction' => 'in', 'status' => status)
      end
    end

    let(:sender_email_address)       { customer_email_address }
    let(:recipient_email_address)    { system_email_address }
    let(:cc_recipient_email_address) { cc_customer_email_address }

    context 'signature verification' do

      let(:allow_expired) { false }

      let(:security_preferences) do
        {
          type:       'S/MIME',
          sign:       {
            success:       true,
            allow_expired: allow_expired,
          },
          encryption: {
            success: false,
          },
        }
      end

      context 'sender certificate present' do

        before do
          create(:smime_certificate, :with_private, fixture: sender_email_address)
        end

        let(:mail) do
          smime_mail = build_mail
          mail = Channel::EmailParser.new.parse(smime_mail.to_s)
          SecureMailing.incoming(mail)

          mail
        end

        it 'verifies' do
          expect(mail[:body]).to include(raw_body)
          expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
          expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
        end

        it_behaves_like 'HttpLog writer', 'success'

        context 'expired' do

          # required to build mail with expired certificate
          let(:allow_expired)        { true }
          let(:sender_email_address) { expired_email_address }

          it 'verifies with comment' do
            expect(mail[:body]).to include(raw_body)
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to include(expired_email_address).and include('expired')
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end

          it_behaves_like 'HttpLog writer', 'success'
        end

        context 'with wrapped mime-type S/MIME signature (e.g. for Microsoft Outlook)' do
          before do
            # We need to disable the open ssl detached flag, to force the smime-type with 'signed-data'.
            stub_const('OpenSSL::PKCS7::DETACHED', nil)
          end

          it 'check that mail was verified' do
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
          end

          it 'check that signe comment exists' do
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
          end

          it 'check that body was verified' do
            expect(mail[:body]).to include(raw_body)
          end
        end

        context 'sender is signer' do
          let(:sender_email_address) { system_email_address }
          let(:mail) do
            smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer.eml').read
            mail = Channel::EmailParser.new.parse(smime_mail.to_s)
            SecureMailing.incoming(mail)

            mail
          end

          it 'verifies' do
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end
        end

        context 'sender is signer, with upcased sender address' do
          let(:sender_email_address) { system_email_address }
          let(:mail) do
            smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer.eml').read.sub('smime1@example.com', 'SMIME1@example.com')
            mail = Channel::EmailParser.new.parse(smime_mail.to_s)
            SecureMailing.incoming(mail)

            mail
          end

          it 'verifies' do
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end
        end

        context 'sender is not signer' do
          before do
            create(:smime_certificate, :with_private, fixture: system_email_address)
          end

          let(:mail) do
            smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_not_signer.eml').read
            mail = Channel::EmailParser.new.parse(smime_mail.to_s)
            SecureMailing.incoming(mail)

            mail
          end

          it 'verifies with comment' do
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('This message was not signed by its sender.')
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end
        end

        context 'sender certificate with another sender name signed by trusted CA (#4457)' do
          before do
            create(:smime_certificate, fixture: 'SenderCA')
          end

          let(:mail) do
            smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer_with_ca.eml').read
            mail = Channel::EmailParser.new.parse(smime_mail.to_s)
            SecureMailing.incoming(mail)

            mail
          end

          it 'verifies' do
            expect(mail[:from]).to include('Zammad Helpdesk <smime-sender-ca@example.com>')
            expect(mail[:body]).to include(raw_body)
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('/emailAddress=SenderCA@example.com/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com')
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end
        end
      end

      context 'no sender certificate' do

        let!(:sender_certificate) { create(:smime_certificate, :with_private, fixture: sender_email_address) }

        let(:mail) do
          smime_mail = build_mail
          mail       = Channel::EmailParser.new.parse(smime_mail.to_s)

          sender_certificate.destroy!

          SecureMailing.incoming(mail)

          mail
        end

        it 'fails' do
          expect(mail[:body]).to include(raw_body)
          expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
        end

        context 'when the mail is not containing the certificate chain but Intermediate CA is present on incoming mails (#5357)' do
          let(:ca_fixture) { 'IntermediateCA' }

          context 'when present in the SMIME store' do
            let(:mail) do
              smime_mail = build_mail
              mail       = Channel::EmailParser.new.parse(smime_mail.to_s)

              sender_certificate.destroy!
              create(:smime_certificate, fixture: ca_fixture)

              SecureMailing.incoming(mail)

              mail
            end

            it 'does verify' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end
          end

          context 'when present in the SSL store' do
            let(:mail) do
              smime_mail = build_mail
              mail       = Channel::EmailParser.new.parse(smime_mail.to_s)

              sender_certificate.destroy!
              create(:ssl_certificate, fixture: ca_fixture)

              SecureMailing.incoming(mail)

              mail
            end

            it 'does verify' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end
          end
        end

        context 'public key present in signature' do

          let(:not_related_fixture)      { 'smime3@example.com' }
          let!(:not_related_certificate) { create(:smime_certificate, fixture: not_related_fixture) }

          context 'not related certificate present' do

            it 'fails' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end

            it_behaves_like 'HttpLog writer', 'failed'

            context 'CA' do

              let(:not_related_fixture) { 'ExpiredCA' }

              it 'fails' do
                expect(mail[:body]).to include(raw_body)
                expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
                expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
                expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
                expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
              end

              it_behaves_like 'HttpLog writer', 'failed'
            end
          end

          context 'usage not prevented' do

            before do
              # remove OpenSSL::PKCS7::NOINTERN
              stub_const('SecureMailing::SMIME::Incoming::OPENSSL_PKCS7_VERIFY_FLAGS', OpenSSL::PKCS7::NOVERIFY)
            end

            it 'does not perform verification' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end
          end
        end

        context 'intermediate CA present' do

          before do
            create(:smime_certificate, fixture: ca_fixture)
          end

          let(:ca_fixture) { 'IntermediateCA' }

          it 'verifies' do
            expect(mail[:body]).to include(raw_body)
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
          end

          it_behaves_like 'HttpLog writer', 'success'

          context 'expired' do
            let(:ca_fixture) { 'ExpiredIntermediateCA' }

            it 'fails' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end

            it_behaves_like 'HttpLog writer', 'failed'

            context 'allowed' do

              let(:allow_expired) { true }

              # ATTENTION: expired CA is a special case where `allow_expired` does not count
              it 'fails' do
                expect(mail[:body]).to include(raw_body)
                expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
                expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
                expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
                expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
              end

              it_behaves_like 'HttpLog writer', 'failed'
            end
          end
        end

        context 'certificate chain' do
          let(:sender_email_address) { 'chain@example.com' }
          let(:ca_subject_chain) { ca_chain.reverse.map { |cert| cert.parsed.subject }.join(', ') }

          context 'incomplete certificate chain present' do

            before do
              create(:smime_certificate, fixture: 'RootCA')
            end

            it 'fails' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end
          end

          context 'certificate chain only partly present' do
            let(:ca_certificate_subject) { subject_chain }

            let!(:ca_chain) do
              create_list(:smime_certificate, 1, fixture: 'IntermediateCA')
            end

            it 'verifies' do
              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(ca_subject_chain)
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
            end
          end

          context 'complete certificate chain present' do
            let!(:ca_chain) do
              create_list(:smime_certificate, 1, fixture: 'ChainCA')
            end

            it 'verifies' do
              allow(Rails.logger).to receive(:warn)

              expect(mail[:body]).to include(raw_body)
              expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
              expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(ca_subject_chain)
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
              expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil

              expect(Rails.logger).not_to have_received(:warn).with(%r{#{Regexp.escape(ca_certificate_subject)}})
            end
          end
        end
      end
    end

    context 'decryption' do

      let(:allow_expired) { false }
      let(:security_preferences) do
        {
          type:       'S/MIME',
          sign:       {
            success: false,
          },
          encryption: {
            success:       true,
            allow_expired: allow_expired,
          },
        }
      end

      let!(:sender_certificate)       { create(:smime_certificate, :with_private, fixture: sender_email_address) }
      let!(:recipient_certificate)    { create(:smime_certificate, :with_private, fixture: recipient_email_address) }
      let!(:cc_recipient_certificate) { create(:smime_certificate, :with_private, fixture: cc_recipient_email_address) }

      context 'private key present' do

        let(:mail) do
          smime_mail = build_mail

          mail = Channel::EmailParser.new.parse(smime_mail.to_s)
          SecureMailing.incoming(mail)

          mail
        end

        it 'decrypts' do
          expect(mail[:body]).to include(raw_body)
          expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq(recipient_certificate_subject)
        end

        it_behaves_like 'HttpLog writer', 'success'

        context 'expired allowed' do
          let(:allow_expired) { true }
          let(:system_email_address) { expired_email_address }

          it 'decrypts with comment' do
            expect(mail[:body]).to include(raw_body)
            expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
            expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
            expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to include(expired_email_address).and include('expired')
          end

          it_behaves_like 'HttpLog writer', 'success'
        end
      end

      context 'no private key present' do

        let(:mail) do
          smime_mail = build_mail

          mail = Channel::EmailParser.new.parse(smime_mail.to_s)

          sender_certificate.destroy!
          recipient_certificate.destroy!
          cc_recipient_certificate.destroy!

          SecureMailing.incoming(mail)

          mail
        end

        it 'fails' do
          expect(mail[:body]).to include('no visible content')
          expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq('The private key for decryption could not be found.')
        end

        it_behaves_like 'HttpLog writer', 'failed'
      end
    end

    context 'with signature verification and decryption' do
      let!(:sender_certificate)       { create(:smime_certificate, :with_private, fixture: sender_email_address) }
      let!(:recipient_certificate)    { create(:smime_certificate, :with_private, fixture: recipient_email_address) }
      let!(:cc_recipient_certificate) { create(:smime_certificate, :with_private, fixture: cc_recipient_email_address) }

      let(:security_preferences) do
        {
          type:       'S/MIME',
          sign:       {
            success: true,
          },
          encryption: {
            success: true,
          },
        }
      end

      let(:mail) do
        smime_mail = build_mail

        mail = Channel::EmailParser.new.parse(smime_mail.to_s)
        SecureMailing.incoming(mail)

        mail
      end

      context 'with wrapped mime-type S/MIME signature (e.g. for Microsoft Outlook)' do
        before do
          # We need to disable the open ssl detached flag, to force the smime-type with 'signed-data'.
          stub_const('OpenSSL::PKCS7::DETACHED', nil)
        end

        it 'check that mail was decrypted' do
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
        end

        it 'check that encryption comment exists' do
          expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq(recipient_certificate_subject)
        end

        it 'check that mail was verified' do
          expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
        end

        it 'check that signe comment exists' do
          expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
        end

        it 'check that body was encrypted and verified' do
          expect(mail[:body]).to include(raw_body)
        end
      end
    end
  end

  describe '.retry' do
    let(:sender_email_address)       { customer_email_address }
    let(:recipient_email_address)    { system_email_address }
    let(:cc_recipient_email_address) { system_email_address }

    let(:security_preferences) do
      {
        type:       'S/MIME',
        sign:       {
          success: true,
        },
        encryption: {
          success: true,
        },
      }
    end

    let(:mail) do
      sender_certificate    = create(:smime_certificate, :with_private, fixture: sender_email_address)
      recipient_certificate = create(:smime_certificate, :with_private, fixture: system_email_address)

      smime_mail = Channel::EmailBuild.build(
        from:         sender_email_address,
        to:           recipient_email_address,
        body:         raw_body,
        content_type: 'text/plain',
        security:     security_preferences,
        attachments:  [
          {
            content_type: 'text/plain',
            content:      'blub',
            filename:     'test-file1.txt',
          },
        ],
      )
      mail = Channel::EmailParser.new.parse(smime_mail.to_s)

      sender_certificate.destroy
      recipient_certificate.destroy

      mail
    end

    let!(:article) do
      _ticket, article, _user, _mail = Channel::EmailParser.new.process({}, mail['raw'])
      article
    end

    context 'private key added' do
      before do
        create(:smime_certificate, :with_private, fixture: recipient_email_address)
        create(:smime_certificate, fixture: sender_email_address)
      end

      it 'succeeds' do
        SecureMailing.retry(article)

        expect(article.preferences[:security][:sign][:success]).to be true
        expect(article.preferences[:security][:sign][:comment]).to eq(sender_certificate_subject)
        expect(article.preferences[:security][:encryption][:success]).to be true
        expect(article.preferences[:security][:encryption][:comment]).to eq(recipient_certificate_subject)
        expect(article.body).to include(raw_body)
        expect(article.attachments.count).to eq(1)
        expect(article.attachments.first.filename).to eq('test-file1.txt')
      end

      context 'S/MIME activated' do

        before do
          Setting.set('smime_integration', false)
        end

        it 'succeeds' do
          Setting.set('smime_integration', true)

          SecureMailing.retry(article)

          expect(article.preferences[:security][:sign][:success]).to be true
          expect(article.preferences[:security][:sign][:comment]).to eq(sender_certificate_subject)
          expect(article.preferences[:security][:encryption][:success]).to be true
          expect(article.preferences[:security][:encryption][:comment]).to eq(recipient_certificate_subject)
          expect(article.body).to include(raw_body)
          expect(article.attachments.count).to eq(1)
          expect(article.attachments.first.filename).to eq('test-file1.txt')
        end
      end
    end
  end
end