require 'test_helper'
require 'tmpdir'

class GpgMeTester < Test::Unit::TestCase
  KEY_NAME = "My Test"
  KEY_PASSPHRASE = "stuff"

  def setup
    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_classic")
    @mail = TMail::Mail.parse(fixture)
  end

  def work_with_key
    @gpgdir = Dir.mktmpdir
    ENV['GNUPGHOME'] = @gpgdir

    @ctx = GPGME::Ctx.new
    @ctx.genkey(<<-EOF, nil, nil)
      <GnupgKeyParms format="internal">
      Key-Type: DSA
      Key-Length: 1024
      Subkey-Type: ELG-E
      Subkey-Length: 1024
      Name-Real: #{KEY_NAME}
      Name-Comment: TMail test key
      Name-Email: taiste@mydom.net
      Expire-Date: 0
      Passphrase: #{KEY_PASSPHRASE}
      </GnupgKeyParms>
      EOF
    key = @ctx.keys.first
    @key_id = key.subkeys.first.fingerprint

    yield

    FileUtils.remove_entry_secure @gpgdir
  end

  def test_available
    assert TMail::Mail.include?(TMail::GpgExtension), "GGPME extension could be loaded"
  end

  def test_copy_headers
    other_mail = TMail::Mail.new
    other_mail.copy_headers_from(@mail)

    assert other_mail.header.keys.sort == @mail.header.keys.sort, "not all headers copied, or imaginery headers were created"
    other_mail.header.keys.each do |h|
      assert other_mail[h].is_a?(Array) == @mail[h].is_a?(Array)
      if other_mail[h].is_a?(Array)
        list1 = other_mail[h].collect {|d| d.to_s}
        list2 = @mail[h].collect {|d| d.to_s}
        assert list1 == list2, "header '#{h}' not properly copied"
      else
        assert other_mail[h].to_s == @mail[h].to_s, "header '#{h}' not properly copied"
      end
    end
  end

  def test_classic_mail
    fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_classic")
    mail = TMail::Mail.parse(fixture)

    assert !mail.is_pgp_signed?
    assert mail.pgp_signature.nil?
    assert mail.verify_pgp_signature.nil?
    assert !mail.is_pgp_encrypted?
    assert mail.pgp_crypt_info.nil?
    assert mail.pgp_encrypted_part.nil?
    assert mail.pgp_decrypt.nil?
  end

  def test_signing
    work_with_key do
      assert_raise GPGME::Error::BadPassphrase do
        @mail.create_signed(@key_id) do |uid_hint, passphrase_info, prev_was_bad|
          "wrongpassphrase"
        end
      end
      signed_mail = nil
      assert_nothing_raised do
        signed_mail = @mail.create_signed(@key_id) do |uid_hint, passphrase_info, prev_was_bad|
          KEY_PASSPHRASE
        end
      end

      # simulate sending/receiving mail
      recv_mail = TMail::Mail.parse(signed_mail.to_s)

      assert recv_mail.is_pgp_signed?
      sign_vrf = nil
      assert_nothing_raised do
        sign_vrf = recv_mail.verify_pgp_signature
      end

      assert !sign_vrf.nil?
      assert sign_vrf.size == 1
      fpr = sign_vrf.first.fingerprint
      assert fpr == @key_id[-fpr.size .. -1]
      assert sign_vrf.first.status == 0
      assert !recv_mail.is_pgp_encrypted?
      assert recv_mail.pgp_crypt_info.nil?
      assert recv_mail.pgp_encrypted_part.nil?
      assert recv_mail.pgp_decrypt.nil?

      # alter message
      signed_mail.parts[0].body = "modified message"

      # simulate sending/receiving mail
      recv_mail = TMail::Mail.parse(signed_mail.to_s)

      assert_nothing_raised do
        sign_vrf = recv_mail.verify_pgp_signature
      end

      assert sign_vrf.first.status != 0
    end
  end

  def test_encryption
    work_with_key do
      encrypted_mail = nil
      assert_nothing_raised do
        encrypted_mail = @mail.create_encrypted(@key_id)
      end

      # simulate sending/receiving mail
      recv_mail = TMail::Mail.parse(encrypted_mail.to_s)

      assert !recv_mail.is_pgp_signed?
      assert recv_mail.pgp_signature.nil?
      assert recv_mail.verify_pgp_signature.nil?
      assert recv_mail.is_pgp_encrypted?
      assert !recv_mail.pgp_crypt_info.nil?
      assert !recv_mail.pgp_encrypted_part.nil?

      assert_raise GPGME::Error::BadPassphrase do
        recv_mail.pgp_decrypt do |uid_hint, passphrase_info, prev_was_bad|
          "wrongpassphrase"
        end
      end
      clear_message = nil
      assert_nothing_raised do
        clear_message = recv_mail.pgp_decrypt do |uid_hint, passphrase_info, prev_was_bad|
          KEY_PASSPHRASE
        end
      end

      # mail reconstitution
      clear_mail = TMail::Mail.parse(clear_message)

      assert clear_mail.body == @mail.body

      # alter message
      encrypted_mail.parts[1].body = "crap"

      # simulate sending/receiving mail
      recv_mail = TMail::Mail.parse(encrypted_mail.to_s)
      assert_raise GPGME::Error::NoData do
        clear_message = recv_mail.pgp_decrypt do |uid_hint, passphrase_info, prev_was_bad|
          KEY_PASSPHRASE
        end
      end
    end
  end

  def test_signing_encryption_chain
    work_with_key do
      signed_encrypted_mail = nil
      assert_nothing_raised do
        signed_encrypted_mail = @mail.create_signed_and_encrypted(@key_id, @key_id) do
          KEY_PASSPHRASE
        end
      end

      # simulate sending/receiving mail
      recv_mail = TMail::Mail.parse(signed_encrypted_mail.to_s)

      assert !recv_mail.is_pgp_signed?
      assert recv_mail.is_pgp_encrypted?

      clear_message = nil
      assert_nothing_raised do
        clear_message = recv_mail.pgp_decrypt do |uid_hint, passphrase_info, prev_was_bad|
          KEY_PASSPHRASE
        end
      end

      clear_mail = TMail::Mail.parse(clear_message)

      assert clear_mail.is_pgp_signed?
      sign_vrf = nil
      assert_nothing_raised do
        sign_vrf = clear_mail.verify_pgp_signature
      end
      assert sign_vrf.first.status == 0
    end
  end
end
