# frozen_string_literal: true require "email_assessor" require "resolv" require "mail" module EmailAssessor class Address attr_accessor :parsed PROHIBITED_DOMAIN_PREFIXES = [ '.', '-', ].freeze PROHIBITED_DOMAIN_CONTENT = [ '+', '!', '_', '/', ' ', '..', '-.', "'", ].freeze PROHIBITED_DOMAIN_SUFFIXES = [ # none ].freeze PROHIBITED_LOCAL_PREFIXES = [ '.', ].freeze PROHIBITED_LOCAL_CONTENT = [ '..', ].freeze PROHIBITED_LOCAL_SUFFIXES = [ '.', ].freeze class << self def prohibited_domain_regex @prohibited_domain_content_regex ||= make_regex( prefixes: PROHIBITED_DOMAIN_PREFIXES, content: PROHIBITED_DOMAIN_CONTENT, suffixes: PROHIBITED_DOMAIN_SUFFIXES ) end def prohibited_local_regex @prohibited_local_content_regex ||= make_regex( prefixes: PROHIBITED_LOCAL_PREFIXES, content: PROHIBITED_LOCAL_CONTENT, suffixes: PROHIBITED_LOCAL_SUFFIXES ) end private def make_regex(prefixes: nil, content: nil, suffixes: nil) parts = [] unless prefixes.nil? prefixes.each do |prefix| parts << "\\A#{Regexp.escape(prefix)}" end end unless content.nil? content.each do |prefix| parts << Regexp.escape(prefix) end end unless suffixes.nil? suffixes.each do |prefix| parts << "#{Regexp.escape(prefix)}\\z" end end Regexp.new(parts.join("|"), Regexp::IGNORECASE) end end def initialize(raw_address) @parse_error = false @raw_address = raw_address @address = nil @valid = nil @mx_servers = nil @domain_tokens = nil begin @address = Mail::Address.new(raw_address) rescue Mail::Field::ParseError @parse_error = true end end def valid? return @valid unless @valid.nil? return false if @parse_error @valid = if @address.domain && @address.address == @raw_address domain = @address.domain domain.include?('.') && !domain.match?(self.class.prohibited_domain_regex) && !@address.local.match?(self.class.prohibited_local_regex) else false end end def disposable? domain_in_file?(EmailAssessor::DISPOSABLE_DOMAINS_FILE_NAME) end def blacklisted? domain_in_file?(EmailAssessor::BLACKLISTED_DOMAINS_FILE_NAME) end def educational? domain_in_file?(EmailAssessor::EDUCATIONAL_DOMAINS_FILE_NAME) end def fastpass? domain_in_file?(EmailAssessor::FASTPASS_DOMAINS_FILE_NAME) end def domain_in_file?(filename) valid? && EmailAssessor.any_token_in_file?(domain_tokens, filename) end def valid_mx? valid? && mx_servers.any? end def mx_server_is_in?(domain_list_file) mx_servers.any? do |mx_server| return false unless mx_server.respond_to?(:exchange) mx_server = mx_server.exchange.to_s EmailAssessor.domain_in_file?(mx_server, domain_list_file) end end def mx_servers @mx_servers ||= Resolv::DNS.open do |dns| mx_servers = dns.getresources(@address.domain, Resolv::DNS::Resource::IN::MX) (mx_servers.any? && mx_servers) || dns.getresources(@address.domain, Resolv::DNS::Resource::IN::A) end end private def domain_tokens @domain_tokens ||= EmailAssessor.tokenize_domain(@address.domain) end end end