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

require 'test_helper'

class EmailHelperTest < ActiveSupport::TestCase

  test 'a mx_records' do

    domain = 'zammad.com'
    mx_domains = EmailHelper.mx_records(domain)
    assert_equal('mx2.zammad.com', mx_domains[0])

  end

  test 'a email parser test' do

    user, domain = EmailHelper.parse_email('somebody@example.com')
    assert_equal('somebody', user)
    assert_equal('example.com', domain)

    user, domain = EmailHelper.parse_email('somebody+test@example.com')
    assert_equal('somebody+test', user)
    assert_equal('example.com', domain)

    user, domain = EmailHelper.parse_email('somebody+testexample.com')
    assert_not(user)
    assert_not(domain)

  end

  test 'provider test' do
    email = 'linus@kernel.org'
    password = 'some_pw'
    map = EmailHelper.provider(email, password)

    assert_equal('imap', map[:google_imap][:inbound][:adapter])
    assert_equal('imap.gmail.com', map[:google_imap][:inbound][:options][:host])
    assert_equal(993, map[:google_imap][:inbound][:options][:port])
    assert_equal(email, map[:google_imap][:inbound][:options][:user])
    assert_equal(password, map[:google_imap][:inbound][:options][:password])

    assert_equal('smtp', map[:google_imap][:outbound][:adapter])
    assert_equal('smtp.gmail.com', map[:google_imap][:outbound][:options][:host])
    assert_equal(587, map[:google_imap][:outbound][:options][:port])
    assert_equal(true, map[:google_imap][:outbound][:options][:start_tls])
    assert_equal(email, map[:google_imap][:outbound][:options][:user])
    assert_equal(password, map[:google_imap][:outbound][:options][:password])

  end

  test 'provider_inbound_mx' do

    email = 'linus@zammad.com'
    password = 'some_pw'
    user, domain = EmailHelper.parse_email(email)
    mx_domains = EmailHelper.mx_records(domain)
    map = EmailHelper.provider_inbound_mx(user, email, password, mx_domains)

    assert_equal('imap', map[0][:adapter])
    assert_equal('mx2.zammad.com', map[0][:options][:host])
    assert_equal(993, map[0][:options][:port])
    assert_equal(user, map[0][:options][:user])
    assert_equal(password, map[0][:options][:password])

    assert_equal('imap', map[1][:adapter])
    assert_equal('mx2.zammad.com', map[1][:options][:host])
    assert_equal(993, map[1][:options][:port])
    assert_equal(email, map[1][:options][:user])
    assert_equal(password, map[1][:options][:password])

  end

  test 'provider_inbound_guess' do

    email = 'linus@zammad.com'
    password = 'some_pw'
    user, domain = EmailHelper.parse_email(email)
    map = EmailHelper.provider_inbound_guess(user, email, password, domain)

    assert_equal('imap', map[0][:adapter])
    assert_equal('mail.zammad.com', map[0][:options][:host])
    assert_equal(993, map[0][:options][:port])
    assert_equal(user, map[0][:options][:user])
    assert_equal(password, map[0][:options][:password])

    assert_equal('imap', map[1][:adapter])
    assert_equal('mail.zammad.com', map[1][:options][:host])
    assert_equal(993, map[1][:options][:port])
    assert_equal(email, map[1][:options][:user])
    assert_equal(password, map[1][:options][:password])

    assert_equal('imap', map[2][:adapter])
    assert_equal('imap.zammad.com', map[2][:options][:host])
    assert_equal(993, map[2][:options][:port])
    assert_equal(user, map[2][:options][:user])
    assert_equal(password, map[2][:options][:password])

    assert_equal('imap', map[3][:adapter])
    assert_equal('imap.zammad.com', map[3][:options][:host])
    assert_equal(993, map[3][:options][:port])
    assert_equal(email, map[3][:options][:user])
    assert_equal(password, map[3][:options][:password])
  end

  test 'provider_outbound_mx' do

    email = 'linus@zammad.com'
    password = 'some_pw'
    user, domain = EmailHelper.parse_email(email)
    mx_domains = EmailHelper.mx_records(domain)
    map = EmailHelper.provider_outbound_mx(user, email, password, mx_domains)

    assert_equal('smtp', map[0][:adapter])
    assert_equal('mx2.zammad.com', map[0][:options][:host])
    assert_equal(465, map[0][:options][:port])
    assert_equal(true, map[0][:options][:start_tls])
    assert_equal(user, map[0][:options][:user])
    assert_equal(password, map[0][:options][:password])

    assert_equal('smtp', map[1][:adapter])
    assert_equal('mx2.zammad.com', map[1][:options][:host])
    assert_equal(465, map[1][:options][:port])
    assert_equal(true, map[1][:options][:start_tls])
    assert_equal(email, map[1][:options][:user])
    assert_equal(password, map[1][:options][:password])

    assert_equal('smtp', map[2][:adapter])
    assert_equal('mx2.zammad.com', map[2][:options][:host])
    assert_equal(587, map[2][:options][:port])
    assert_nil(map[2][:options][:start_tls])
    assert_equal(user, map[2][:options][:user])
    assert_equal(password, map[2][:options][:password])

    assert_equal('smtp', map[3][:adapter])
    assert_equal('mx2.zammad.com', map[3][:options][:host])
    assert_equal(587, map[3][:options][:port])
    assert_nil(map[3][:options][:start_tls])
    assert_equal(email, map[3][:options][:user])
    assert_equal(password, map[3][:options][:password])
  end

  test 'provider_outbound_guess' do

    email = 'linus@zammad.com'
    password = 'some_pw'
    user, domain = EmailHelper.parse_email(email)
    map = EmailHelper.provider_outbound_guess(user, email, password, domain)

    assert_equal('smtp', map[0][:adapter])
    assert_equal('mail.zammad.com', map[0][:options][:host])
    assert_equal(465, map[0][:options][:port])
    assert_equal(true, map[0][:options][:start_tls])
    assert_equal(user, map[0][:options][:user])
    assert_equal(password, map[0][:options][:password])

    assert_equal('smtp', map[1][:adapter])
    assert_equal('mail.zammad.com', map[1][:options][:host])
    assert_equal(465, map[1][:options][:port])
    assert_equal(true, map[1][:options][:start_tls])
    assert_equal(email, map[1][:options][:user])
    assert_equal(password, map[1][:options][:password])

    assert_equal('smtp', map[2][:adapter])
    assert_equal('smtp.zammad.com', map[2][:options][:host])
    assert_equal(465, map[2][:options][:port])
    assert_equal(true, map[2][:options][:start_tls])
    assert_equal(user, map[2][:options][:user])
    assert_equal(password, map[2][:options][:password])

    assert_equal('smtp', map[3][:adapter])
    assert_equal('smtp.zammad.com', map[3][:options][:host])
    assert_equal(465, map[3][:options][:port])
    assert_equal(true, map[3][:options][:start_tls])
    assert_equal(email, map[3][:options][:user])
    assert_equal(password, map[3][:options][:password])
  end

  test 'z probe_inbound' do

    # invalid adapter
    result = EmailHelper::Probe.inbound(
      adapter: 'imap2',
      options: {
        host:     'nonexisting_host',
        port:     993,
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )

    assert_equal('failed', result[:result])

    # network issues
    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     'nonexisting_host',
        port:     993,
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )

    assert_equal('invalid', result[:result])
    assert_equal('Hostname not found!', result[:message_human])
    assert_equal('nonexisting_host', result[:settings][:options][:host])

    # try to access imap on host with blocked port to force a "Connection refused!" error
    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     '127.0.0.1',
        port:     8, # no service to be expected
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )
    assert_equal('invalid', result[:result])
    assert_equal('Connection refused!', result[:message_human])
    assert_equal('127.0.0.1', result[:settings][:options][:host])

    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     '172.42.42.42',
        port:     993,
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )
    assert_equal('invalid', result[:result])
    assert_match(%r{Host not reachable|No route to host}, result[:message_human])
    assert_equal('172.42.42.42', result[:settings][:options][:host])

    # gmail
    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     'imap.gmail.com',
        port:     993,
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )
    assert_equal('invalid', result[:result])
    assert_match(%r{Authentication failed, username incorrect|Authentication failed, invalid credentials}, result[:message_human])
    assert_equal('imap.gmail.com', result[:settings][:options][:host])

    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     'imap.gmail.com',
        port:     993,
        ssl:      true,
        user:     'frank.tailor05@googlemail.com',
        password: 'password',
      }
    )
    assert_equal('invalid', result[:result])

    # if we have to many failed logins, we need to handle another error message
    if result[:message_human].present?
      assert_match(%r{Authentication failed, username incorrect|Authentication failed, invalid credentials}, result[:message_human])
    else
      assert_match(%r{Web login required}, result[:message])
    end
    assert_equal('imap.gmail.com', result[:settings][:options][:host])

    # dovecot
    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     'mx2.zammad.com',
        port:     993,
        ssl:      true,
        user:     'some@example.com',
        password: 'password',
      }
    )
    assert_equal('invalid', result[:result])
    assert_equal('Authentication failed!', result[:message_human])
    assert_equal('mx2.zammad.com', result[:settings][:options][:host])

    # realtest - test I
    if !ENV['EMAILHELPER_MAILBOX_1']
      raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
    result = EmailHelper::Probe.inbound(
      adapter: 'imap',
      options: {
        host:     'mx2.zammad.com',
        port:     993,
        ssl:      true,
        user:     mailbox_user,
        password: mailbox_password,
      }
    )
    assert_equal('ok', result[:result])

  end

  test 'z probe_outbound' do

    # invalid adapter
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp2',
        options: {
          host:      'nonexisting_host',
          port:      25,
          start_tls: true,
          user:      'some@example.com',
          password:  'password',
        },
      },
      'some@example.com',
    )

    assert_equal('failed', result[:result])

    # network issues
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:      'nonexisting_host',
          port:      25,
          start_tls: true,
          user:      'some@example.com',
          password:  'password',
        }
      },
      'some@example.com',
    )

    assert_equal('invalid', result[:result])
    assert_equal('Hostname not found!', result[:message_human])
    assert_equal('nonexisting_host', result[:settings][:options][:host])

    # try to access SMTP on host with blocked port to force a "Connection refused!" error
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:      '127.0.0.1',
          port:      8, # no service to be expected
          start_tls: true,
          user:      'some@example.com',
          password:  'password',
        }
      },
      'some@example.com',
    )
    assert_equal('invalid', result[:result])
    assert_equal('Connection refused!', result[:message_human])
    assert_equal('127.0.0.1', result[:settings][:options][:host])

    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:      '172.42.42.42',
          port:      25,
          start_tls: true,
          user:      'some@example.com',
          password:  'password',
        }
      },
      'some@example.com',
    )
    assert_equal('invalid', result[:result])
    assert_match(%r{Host not reachable|No route to host}, result[:message_human])
    assert_equal('172.42.42.42', result[:settings][:options][:host])

    # gmail
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:      'smtp.gmail.com',
          port:      25,
          start_tls: true,
          user:      'some@example.com',
          password:  'password',
        }
      },
      'some@example.com',
    )
    assert_equal('invalid', result[:result])
    assert_equal('Authentication failed!', result[:message_human])
    assert_equal('smtp.gmail.com', result[:settings][:options][:host])

    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:      'smtp.gmail.com',
          port:      25,
          start_tls: true,
          user:      'frank.tailor05@googlemail.com',
          password:  'password',
        }
      },
      'some@example.com',
    )
    assert_equal('invalid', result[:result])

    # if we have to many failed logins, we need to handle another error message
    if result[:message_human].present?
      assert_equal('Authentication failed!', result[:message_human])
    else
      assert_match(%r{Please log in with your web browser and then try again}, result[:message])
    end
    assert_equal('smtp.gmail.com', result[:settings][:options][:host])

    # dovecot
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:     'mx2.zammad.com',
          port:     587,
          user:     'some@example.com',
          password: 'password',
        }
      },
      'some@example.com',
    )
    assert_equal('invalid', result[:result])
    assert_equal('Authentication failed!', result[:message_human])
    assert_equal('mx2.zammad.com', result[:settings][:options][:host])

    # realtest - test I
    if !ENV['EMAILHELPER_MAILBOX_1']
      raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
    result = EmailHelper::Probe.outbound(
      {
        adapter: 'smtp',
        options: {
          host:     'mx2.zammad.com',
          port:     587,
          user:     mailbox_user,
          password: mailbox_password,
        }
      },
      mailbox_user,
    )
    assert_equal('ok', result[:result])
  end

  test 'zz probe' do

    result = EmailHelper::Probe.full(
      email:    'invalid_format',
      password: 'somepass',
    )
    assert_equal('invalid', result[:result])
    assert_not(result[:setting])

    # realtest - test I, with imap
    if !ENV['EMAILHELPER_MAILBOX_1']
      raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]

    result = EmailHelper::Probe.full(
      email:    mailbox_user,
      password: mailbox_password,
    )
    assert_equal('ok', result[:result])
    assert_equal('mx2.zammad.com', result[:setting][:inbound][:options][:host])
    assert_equal('mx2.zammad.com', result[:setting][:outbound][:options][:host])

    # realtest - test II, gmail with only pop3
    if !ENV['EMAILHELPER_MAILBOX_2']
      raise "Need EMAILHELPER_MAILBOX_2 as ENV variable like export EMAILHELPER_MAILBOX_2='hansb36621@gmail.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_2'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_2'].split(':')[1]

    result = EmailHelper::Probe.full(
      email:    mailbox_user,
      password: mailbox_password,
    )
    assert_nil(result[:reason])
    assert_equal('ok', result[:result])
    assert_equal('pop.gmail.com', result[:setting][:inbound][:options][:host])
    assert_equal('smtp.gmail.com', result[:setting][:outbound][:options][:host])

  end

  test 'zz verify' do

    # realtest - test I, with imap
    if !ENV['EMAILHELPER_MAILBOX_1']
      raise "Need EMAILHELPER_MAILBOX_1 as ENV variable like export EMAILHELPER_MAILBOX_1='unittestemailhelper01@zammad.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_1'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_1'].split(':')[1]
    result = EmailHelper::Verify.email(
      inbound:  {
        adapter: 'imap',
        options: {
          host:     'mx2.zammad.com',
          port:     993,
          ssl:      true,
          user:     mailbox_user,
          password: mailbox_password,
        },
      },
      outbound: {
        adapter: 'smtp',
        options: {
          host:     'mx2.zammad.com',
          port:     587,
          user:     mailbox_user,
          password: mailbox_password,
        },
      },
      sender:   mailbox_user,
    )
    assert_equal('ok', result[:result])

    # realtest - test II, gmail with pop3
    if !ENV['EMAILHELPER_MAILBOX_2']
      raise "Need EMAILHELPER_MAILBOX_2 as ENV variable like export EMAILHELPER_MAILBOX_2='hansb36621@gmail.com:somepass'"
    end

    mailbox_user     = ENV['EMAILHELPER_MAILBOX_2'].split(':')[0]
    mailbox_password = ENV['EMAILHELPER_MAILBOX_2'].split(':')[1]

    EmailHelper.parse_email(mailbox_user)

    result = EmailHelper::Verify.email(
      inbound:  {
        adapter: 'pop3',
        options: {
          host:     'pop.gmail.com',
          port:     995,
          ssl:      true,
          user:     mailbox_user,
          password: mailbox_password,
        },
      },
      outbound: {
        adapter: 'smtp',
        options: {
          host:      'smtp.gmail.com',
          port:      587,
          start_tls: true,
          user:      mailbox_user,
          password:  mailbox_password,
        },
      },
      sender:   mailbox_user,
    )
    assert_equal('ok', result[:result])

  end

end