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

class SecureMailing::PGP::Outgoing < SecureMailing::Backend::Handler
  def initialize(mail, security)
    super()

    @mail     = mail
    @security = security
  end

  def process
    return unless process?

    if @security[:sign][:success]
      sign
      log('sign', 'success')
    end
    if @security[:encryption][:success]
      encrypt
      log('encryption', 'success')
    end
  end

  def process?
    return false if @security.blank?
    return false if @security[:type] != 'PGP'

    @security[:sign][:success] || @security[:encryption][:success]
  end

  def cleanup(mail)
    part = Mail::Part.new
    if mail.multipart?
      if mail.content_type =~ /^(multipart[^;]+)/
        part.content_type Regexp.last_match(1)
      else
        part.content_type 'multipart/mixed'
      end
      mail.body.parts.each do |p|
        part.add_part cleanup(p)
      end
    else
      # retain important headers if present
      part.content_type mail.content_type
      part.content_id mail.header['Content-ID'] if mail.header['Content-ID']
      part.content_disposition mail.content_disposition if mail.content_disposition

      # force base64 encoding
      part.body Mail::Encodings::Base64.encode(mail.body.to_s)
      part.body.encoding = 'base64'
    end
    part
  end

  def sign
    from = @mail.from.first
    cert = PGPKeypair.for_sender_email_address(from)
    raise "Unable to find PGP private key for '#{from}'" unless cert

    signature = Sequoia.sign_detached_with(plaintext: @mail.body.encoded, sender: cert.private_key,
                                           password: cert.private_key_secret)

    signature_part = Mail::Part.new do
      content_type 'application/pgp-signature; name="signature.asc"'
      content_disposition 'attachment; filename="signature.asc"'
      content_description 'OpenPGP signature'
      body signature
    end
    @mail.add_part signature_part
    @mail.content_type "multipart/signed; protocol=\"application/pgp-signature\"; micalg=\"pgp-sha512\"; boundary=\"#{@mail.boundary}\""
  rescue StandardError => e
    log('sign', 'failed', e.message)
    raise
  end

  def encrypt
    recipients = []
    recipients += @mail.to if @mail.to
    recipients += @mail.cc if @mail.cc
    recipients += @mail.bcc if @mail.bcc

    certificates = PGPKeypair.for_recipient_email_addresses!(recipients)

    encrypted_control = Mail::Part.new do
      content_type 'application/pgp-encrypted'
      content_description 'OpenPGP version'
      body 'Version: 1'
    end

    plaintext = @mail.encoded
    encrypted_part = Mail::Part.new do
      content_type 'application/octet-stream; name="encrypted.asc"'
      content_disposition 'inline; filename="encrypted.asc"'
      content_description 'OpenPGP encrypted message'
      body Sequoia.encrypt_for(plaintext: plaintext, recipients: certificates.map(&:public_key))
    end
    @mail.body = nil
    @mail.add_part encrypted_control
    @mail.add_part encrypted_part
    @mail.content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"#{@mail.boundary}\""
  rescue StandardError => e
    log('encryption', 'failed', e.message)
    raise
  end

  def log(action, status, error = nil)
    HttpLog.create(
      direction: 'out',
      facility: 'PGP',
      url: "#{@mail[:from_email]} -> #{@mail[:to]}",
      status: status,
      ip: nil,
      request: @security,
      response: { error: error },
      method: action,
      created_by_id: 1,
      updated_by_id: 1
    )
  end
end
