truemail-rb/truemail

View on GitHub
lib/truemail/validate/smtp.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module Truemail
  module Validate
    class Smtp < Truemail::Validate::Base
      ERROR = 'smtp error'

      attr_reader :smtp_results

      def initialize(result)
        super(result)
        @smtp_results = []
      end

      def run
        return false unless Truemail::Validate::MxBlacklist.check(result)
        establish_smtp_connection
        return true if success(success_response?)
        result.smtp_debug = smtp_results
        return true if success(not_includes_user_not_found_errors?)
        add_error(Truemail::Validate::Smtp::ERROR)
        false
      end

      private

      def fail_fast?
        configuration.smtp_fail_fast
      end

      def filtered_mail_servers_by_fail_fast_scenario
        fail_fast? ? mail_servers.first(1) : mail_servers
      end

      def attempts
        @attempts ||= begin
          return {} if fail_fast? || !mail_servers.one?
          { attempts: configuration.connection_attempts } # attempts should used for case with one mail server only
        end
      end

      def request
        smtp_results.last
      end

      def rcptto_error
        request.response.errors[:rcptto]
      end

      def establish_smtp_connection
        filtered_mail_servers_by_fail_fast_scenario.each do |mail_server|
          smtp_results << Truemail::Validate::Smtp::Request.new(
            configuration: configuration, host: mail_server, email: result.punycode_email, **attempts
          )
          next unless request.check_port
          request.run || rcptto_error ? break : next
        end
      end

      def success_response?
        smtp_results.map(&:response).any?(&:rcptto)
      end

      def not_includes_user_not_found_errors?
        return false unless configuration.smtp_safe_check
        result.smtp_debug.map { |request| request.response.errors }.all? do |errors|
          next true unless errors.key?(:rcptto)
          errors.slice(:rcptto).values.none? do |error|
            configuration.smtp_error_body_pattern.match?(error)
          end
        end
      end
    end
  end
end