email_address 0.1.13 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,20 +29,22 @@
29
29
  # user.canonical_email #=> "patsmith@gmail.com"
30
30
  ################################################################################
31
31
 
32
- class EmailAddress::CanonicalEmailAddressType < ActiveRecord::Type::Value
32
+ module EmailAddress
33
+ class CanonicalEmailAddressType < ActiveRecord::Type::Value
33
34
 
34
- # From user input, setter
35
- def cast(value)
36
- super(EmailAddress.canonical(value))
37
- end
35
+ # From user input, setter
36
+ def cast(value)
37
+ super(Address.new(value).canonical)
38
+ end
38
39
 
39
- # From a database value
40
- def deserialize(value)
41
- value && EmailAddress.normal(value)
42
- end
40
+ # From a database value
41
+ def deserialize(value)
42
+ value && Address.new(value).normal
43
+ end
43
44
 
44
- # To a database value (string)
45
- def serialize(value)
46
- value && EmailAddress.normal(value)
45
+ # To a database value (string)
46
+ def serialize(value)
47
+ value && Address.new(value).normal
48
+ end
47
49
  end
48
50
  end
@@ -97,67 +97,67 @@ module EmailAddress
97
97
  # * exchanger_match: %w(google.com 127.0.0.1 10.9.8.0/24 ::1/64)
98
98
  #
99
99
 
100
- require 'yaml'
100
+ require "yaml"
101
101
 
102
102
  class Config
103
103
  @config = {
104
- dns_lookup: :mx, # :mx, :a, :off
105
- dns_timeout: nil,
106
- sha1_secret: "",
107
- munge_string: "*****",
108
-
109
- local_downcase: true,
110
- local_fix: false,
111
- local_encoding: :ascii, # :ascii, :unicode,
112
- local_parse: nil, # nil, Proc
113
- local_format: :conventional, # :conventional, :relaxed, :redacted, :standard, Proc
114
- local_size: 1..64,
115
- tag_separator: '+', # nil, character
116
- mailbox_size: 1..64, # without tag
117
- mailbox_canonical: nil, # nil, Proc
118
- mailbox_validator: nil, # nil, Proc
119
-
120
- host_encoding: :punycode || :unicode,
121
- host_validation: :mx || :a || :connect || :syntax,
122
- host_size: 1..253,
123
- host_allow_ip: false,
104
+ dns_lookup: :mx, # :mx, :a, :off
105
+ dns_timeout: nil,
106
+ sha1_secret: "",
107
+ munge_string: "*****",
108
+
109
+ local_downcase: true,
110
+ local_fix: false,
111
+ local_encoding: :ascii, # :ascii, :unicode,
112
+ local_parse: nil, # nil, Proc
113
+ local_format: :conventional, # :conventional, :relaxed, :redacted, :standard, Proc
114
+ local_size: 1..64,
115
+ tag_separator: "+", # nil, character
116
+ mailbox_size: 1..64, # without tag
117
+ mailbox_canonical: nil, # nil, Proc
118
+ mailbox_validator: nil, # nil, Proc
119
+
120
+ host_encoding: :punycode || :unicode,
121
+ host_validation: :mx || :a || :connect || :syntax,
122
+ host_size: 1..253,
123
+ host_allow_ip: false,
124
124
  host_remove_spaces: false,
125
- host_local: false,
125
+ host_local: false,
126
126
 
127
127
  address_validation: :parts, # :parts, :smtp, Proc
128
- address_size: 3..254,
129
- address_fqdn_domain: nil, # Fully Qualified Domain Name = [host].[domain.tld]
128
+ address_size: 3..254,
129
+ address_fqdn_domain: nil # Fully Qualified Domain Name = [host].[domain.tld]
130
130
  }
131
131
 
132
132
  # 2018-04: AOL and Yahoo now under "oath.com", owned by Verizon. Keeping separate for now
133
133
  @providers = {
134
134
  aol: {
135
- host_match: %w(aol. compuserve. netscape. aim. cs.),
135
+ host_match: %w[aol. compuserve. netscape. aim. cs.]
136
136
  },
137
137
  google: {
138
- host_match: %w(gmail.com googlemail.com),
139
- exchanger_match: %w(google.com googlemail.com),
140
- local_size: 5..64,
138
+ host_match: %w[gmail.com googlemail.com],
139
+ exchanger_match: %w[google.com googlemail.com],
140
+ local_size: 5..64,
141
141
  local_private_size: 1..64, # When hostname not in host_match (private label)
142
- mailbox_canonical: ->(m) {m.gsub('.','')},
142
+ mailbox_canonical: ->(m) { m.delete(".") }
143
143
  },
144
144
  msn: {
145
- host_match: %w(msn. hotmail. outlook. live.),
146
- mailbox_validator: ->(m,t) { m =~ /\A\w[\-\w]*(?:\.[\-\w]+)*\z/i},
145
+ host_match: %w[msn. hotmail. outlook. live.],
146
+ exchanger_match: %w[outlook.com],
147
+ mailbox_validator: ->(m, t) { m =~ /\A\w[\-\w]*(?:\.[\-\w]+)*\z/i }
147
148
  },
148
149
  yahoo: {
149
- host_match: %w(yahoo. ymail. rocketmail.),
150
- exchanger_match: %w(yahoodns yahoo-inc),
151
- },
150
+ host_match: %w[yahoo. ymail. rocketmail.],
151
+ exchanger_match: %w[yahoodns yahoo-inc]
152
+ }
152
153
  }
153
154
 
154
-
155
155
  # Loads messages: {"en"=>{"email_address"=>{"invalid_address"=>"Invalid Email Address",...}}}
156
156
  # Rails/I18n gem: t(email_address.error, scope: "email_address")
157
- @errors = YAML.load_file(File.dirname(__FILE__)+"/messages.yaml")
157
+ @errors = YAML.load_file(File.dirname(__FILE__) + "/messages.yaml")
158
158
 
159
159
  # Set multiple default configuration settings
160
- def self.configure(config={})
160
+ def self.configure(config = {})
161
161
  @config.merge!(config)
162
162
  end
163
163
 
@@ -168,34 +168,56 @@ module EmailAddress
168
168
  end
169
169
 
170
170
  # Returns the hash of Provider rules
171
- def self.providers
172
- @providers
171
+ class << self
172
+ attr_reader :providers
173
173
  end
174
174
 
175
175
  # Configure or lookup a provider by name.
176
- def self.provider(name, config={})
176
+ def self.provider(name, config = {})
177
177
  name = name.to_sym
178
178
  if config.size > 0
179
- @providers[name] ||= @config.clone
180
- @providers[name].merge!(config)
179
+ @providers[name.to_sym] = config
181
180
  end
182
181
  @providers[name]
183
182
  end
184
183
 
185
- def self.error_message(name, locale="en")
184
+ def self.error_message(name, locale = "en")
186
185
  @errors[locale]["email_address"][name.to_s] || name.to_s
187
186
  end
188
187
 
189
188
  # Customize your own error message text.
190
- def self.error_messages(hash=nil)
191
- @errors = @errors.merge(hash) if hash
192
- @errors
189
+ def self.error_messages(hash = {}, locale = "en", *extra)
190
+ hash = extra.first if extra.first.is_a? Hash
191
+ unless hash.empty?
192
+ @errors[locale]["email_address"] = @errors[locale]["email_address"].merge(hash)
193
+ end
194
+ @errors[locale]["email_address"]
193
195
  end
194
196
 
195
197
  def self.all_settings(*configs)
196
198
  config = @config.clone
197
- configs.each {|c| config.merge!(c) }
199
+ configs.each { |c| config.merge!(c) }
198
200
  config
199
201
  end
202
+
203
+ def initialize(overrides = {})
204
+ @config = Config.all_settings(overrides)
205
+ end
206
+
207
+ def []=(setting, value)
208
+ @config[setting.to_sym] = value
209
+ end
210
+
211
+ def [](setting)
212
+ @config[setting.to_sym]
213
+ end
214
+
215
+ def configure(settings)
216
+ @config = @config.merge(settings)
217
+ end
218
+
219
+ def to_h
220
+ @config
221
+ end
200
222
  end
201
223
  end
@@ -29,20 +29,22 @@
29
29
  # user.canonical_email #=> "patsmith@gmail.com"
30
30
  ################################################################################
31
31
 
32
- class EmailAddress::EmailAddressType < ActiveRecord::Type::Value
32
+ module EmailAddress
33
+ class EmailAddressType < ActiveRecord::Type::Value
33
34
 
34
- # From user input, setter
35
- def cast(value)
36
- super(EmailAddress.normal(value))
37
- end
35
+ # From user input, setter
36
+ def cast(value)
37
+ super(Address.new(value).normal)
38
+ end
38
39
 
39
- # From a database value
40
- def deserialize(value)
41
- value && EmailAddress.normal(value)
42
- end
43
- #
44
- # To a database value (string)
45
- def serialize(value)
46
- value && EmailAddress.normal(value)
40
+ # From a database value
41
+ def deserialize(value)
42
+ value && Address.new(value).normal
43
+ end
44
+
45
+ # To a database value (string)
46
+ def serialize(value)
47
+ value && Address.new(value).normal
48
+ end
47
49
  end
48
50
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'resolv'
4
- require 'netaddr'
5
- require 'socket'
3
+ require "resolv"
4
+ require "netaddr"
5
+ require "socket"
6
6
 
7
7
  module EmailAddress
8
8
  class Exchanger
9
9
  include Enumerable
10
10
 
11
- def self.cached(host, config={})
11
+ def self.cached(host, config = {})
12
12
  @host_cache ||= {}
13
- @cache_size ||= ENV['EMAIL_ADDRESS_CACHE_SIZE'].to_i || 100
13
+ @cache_size ||= ENV["EMAIL_ADDRESS_CACHE_SIZE"].to_i || 100
14
14
  if @host_cache.has_key?(host)
15
15
  o = @host_cache.delete(host)
16
16
  @host_cache[host] = o # LRU cache, move to end
@@ -22,22 +22,24 @@ module EmailAddress
22
22
  end
23
23
  end
24
24
 
25
- def initialize(host, config={})
25
+ def initialize(host, config = {})
26
26
  @host = host
27
- @config = config
27
+ @config = config.is_a?(Hash) ? Config.new(config) : config
28
+ @dns_disabled = @config[:host_validation] == :syntax || @config[:dns_lookup] == :off
28
29
  end
29
30
 
30
31
  def each(&block)
32
+ return if @dns_disabled
31
33
  mxers.each do |m|
32
- yield({host:m[0], ip:m[1], priority:m[2]})
34
+ yield({host: m[0], ip: m[1], priority: m[2]})
33
35
  end
34
36
  end
35
37
 
36
38
  # Returns the provider name based on the MX-er host names, or nil if not matched
37
39
  def provider
38
40
  return @provider if defined? @provider
39
- EmailAddress::Config.providers.each do |provider, config|
40
- if config[:exchanger_match] && self.matches?(config[:exchanger_match])
41
+ Config.providers.each do |provider, config|
42
+ if config[:exchanger_match] && matches?(config[:exchanger_match])
41
43
  return @provider = provider
42
44
  end
43
45
  end
@@ -50,40 +52,37 @@ module EmailAddress
50
52
  # may not find provider by MX name or IP. I'm not sure about the "0.0.0.0" ip, it should
51
53
  # be good in this context, but in "listen" context it means "all bound IP's"
52
54
  def mxers
53
- return [["example.com", "0.0.0.0", 1]] if @config[:dns_lookup] == :off
54
- @mxers ||= Resolv::DNS.open do |dns|
55
+ return [["example.com", "0.0.0.0", 1]] if @dns_disabled
56
+ @mxers ||= Resolv::DNS.open { |dns|
55
57
  dns.timeouts = @config[:dns_timeout] if @config[:dns_timeout]
56
58
 
57
59
  ress = begin
58
60
  dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
59
- rescue Resolv::ResolvTimeout
60
- []
61
+ rescue Resolv::ResolvTimeout
62
+ []
61
63
  end
62
64
 
63
- records = ress.map do |r|
64
- begin
65
- if r.exchange.to_s > " "
66
- [r.exchange.to_s, IPSocket::getaddress(r.exchange.to_s), r.preference]
67
- else
68
- nil
69
- end
70
- rescue SocketError # not found, but could also mean network not work or it could mean one record doesn't resolve an address
71
- nil
65
+ records = ress.map { |r|
66
+ if r.exchange.to_s > " "
67
+ [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference]
72
68
  end
73
- end
69
+ }
74
70
  records.compact
75
- end
71
+ }
72
+ # not found, but could also mean network not work or it could mean one record doesn't resolve an address
73
+ rescue SocketError
74
+ [["example.com", "0.0.0.0", 1]]
76
75
  end
77
76
 
78
77
  # Returns Array of domain names for the MX'ers, used to determine the Provider
79
78
  def domains
80
- @_domains ||= mxers.map {|m| EmailAddress::Host.new(m.first).domain_name }.sort.uniq
79
+ @_domains ||= mxers.map { |m| Host.new(m.first).domain_name }.sort.uniq
81
80
  end
82
81
 
83
82
  # Returns an array of MX IP address (String) for the given email domain
84
83
  def mx_ips
85
- return ["0.0.0.0"] if @config[:dns_lookup] == :off
86
- mxers.map {|m| m[1] }
84
+ return ["0.0.0.0"] if @dns_disabled
85
+ mxers.map { |m| m[1] }
87
86
  end
88
87
 
89
88
  # Simple matcher, takes an array of CIDR addresses (ip/bits) and strings.
@@ -96,9 +95,9 @@ module EmailAddress
96
95
  rules = Array(rules)
97
96
  rules.each do |rule|
98
97
  if rule.include?("/")
99
- return rule if self.in_cidr?(rule)
98
+ return rule if in_cidr?(rule)
100
99
  else
101
- self.each {|mx| return rule if mx[:host].end_with?(rule) }
100
+ each { |mx| return rule if mx[:host].end_with?(rule) }
102
101
  end
103
102
  end
104
103
  false
@@ -1,8 +1,7 @@
1
-
2
- require 'simpleidn'
3
- require 'resolv'
4
- require 'netaddr'
5
- require 'net/smtp'
1
+ require "simpleidn"
2
+ require "resolv"
3
+ require "netaddr"
4
+ require "net/smtp"
6
5
 
7
6
  module EmailAddress
8
7
  ##############################################################################
@@ -34,12 +33,12 @@ module EmailAddress
34
33
  class Host
35
34
  attr_reader :host_name
36
35
  attr_accessor :dns_name, :domain_name, :registration_name,
37
- :tld, :tld2, :subdomains, :ip_address, :config, :provider,
38
- :comment, :error_message, :reason
36
+ :tld, :tld2, :subdomains, :ip_address, :config, :provider,
37
+ :comment, :error_message, :reason
39
38
  MAX_HOST_LENGTH = 255
40
39
 
41
40
  # Sometimes, you just need a Regexp...
42
- DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: \-{1,2} | \.) [\p{L}\p{N}]+ )*/x
41
+ DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: \-{1,2} | \.) [\p{L}\p{N}]+ )*/x
43
42
 
44
43
  # The IPv4 and IPv6 were lifted from Resolv::IPv?::Regex and tweaked to not
45
44
  # \A...\z anchor at the edges.
@@ -85,46 +84,45 @@ module EmailAddress
85
84
 
86
85
  # host name -
87
86
  # * host type - :email for an email host, :mx for exchanger host
88
- def initialize(host_name, config={})
89
- @original = host_name ||= ''
87
+ def initialize(host_name, config = {})
88
+ @original = host_name ||= ""
90
89
  config[:host_type] ||= :email
91
- @config = config
92
- @error = @error_message = nil
90
+ @config = config.is_a?(Hash) ? Config.new(config) : config
91
+ @error = @error_message = nil
93
92
  parse(host_name)
94
93
  end
95
94
 
96
95
  # Returns the String representation of the host name (or IP)
97
96
  def name
98
- if self.ipv4?
99
- "[#{self.ip_address}]"
100
- elsif self.ipv6?
101
- "[IPv6:#{self.ip_address}]"
97
+ if ipv4?
98
+ "[#{ip_address}]"
99
+ elsif ipv6?
100
+ "[IPv6:#{ip_address}]"
102
101
  elsif @config[:host_encoding] && @config[:host_encoding] == :unicode
103
- ::SimpleIDN.to_unicode(self.host_name)
102
+ ::SimpleIDN.to_unicode(host_name)
104
103
  else
105
- self.dns_name
104
+ dns_name
106
105
  end
107
106
  end
108
- alias :to_s :name
107
+ alias to_s name
109
108
 
110
109
  # The canonical host name is the simplified, DNS host name
111
110
  def canonical
112
- self.dns_name
111
+ dns_name
113
112
  end
114
113
 
115
114
  # Returns the munged version of the name, replacing everything after the
116
115
  # initial two characters with "*****" or the configured "munge_string".
117
116
  def munge
118
- self.host_name.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] }
117
+ host_name.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] }
119
118
  end
120
119
 
121
120
  ############################################################################
122
121
  # Parsing
123
122
  ############################################################################
124
123
 
125
-
126
124
  def parse(host) # :nodoc:
127
- host = self.parse_comment(host)
125
+ host = parse_comment(host)
128
126
 
129
127
  if host =~ /\A\[IPv6:(.+)\]/i
130
128
  self.ip_address = $1
@@ -149,34 +147,34 @@ module EmailAddress
149
147
  name = fully_qualified_domain_name(name.downcase)
150
148
  @host_name = name
151
149
  if @config[:host_remove_spaces]
152
- @host_name = @host_name.gsub(' ', '')
150
+ @host_name = @host_name.delete(" ")
153
151
  end
154
- if host_name =~ /[^[:ascii:]]/
155
- @dns_name = ::SimpleIDN.to_ascii(self.host_name)
152
+ @dns_name = if /[^[:ascii:]]/.match(host_name)
153
+ ::SimpleIDN.to_ascii(host_name)
156
154
  else
157
- @dns_name = self.host_name
155
+ host_name
158
156
  end
159
157
 
160
158
  # Subdomain only (root@localhost)
161
- if name.index('.').nil?
159
+ if name.index(".").nil?
162
160
  self.subdomains = name
163
161
 
164
162
  # Split sub.domain from .tld: *.com, *.xx.cc, *.cc
165
163
  elsif name =~ /\A(.+)\.(\w{3,10})\z/ ||
166
- name =~ /\A(.+)\.(\w{1,3}\.\w\w)\z/ ||
167
- name =~ /\A(.+)\.(\w\w)\z/
164
+ name =~ /\A(.+)\.(\w{1,3}\.\w\w)\z/ ||
165
+ name =~ /\A(.+)\.(\w\w)\z/
168
166
 
169
167
  sub_and_domain, self.tld2 = [$1, $2] # sub+domain, com || co.uk
170
- self.tld = self.tld2.sub(/\A.+\./, '') # co.uk => uk
168
+ self.tld = tld2.sub(/\A.+\./, "") # co.uk => uk
171
169
  if sub_and_domain =~ /\A(.+)\.(.+)\z/ # is subdomain? sub.example [.tld2]
172
- self.subdomains = $1
170
+ self.subdomains = $1
173
171
  self.registration_name = $2
174
172
  else
175
173
  self.registration_name = sub_and_domain
176
- #self.domain_name = sub_and_domain + '.' + self.tld2
174
+ # self.domain_name = sub_and_domain + '.' + self.tld2
177
175
  end
178
- self.domain_name = self.registration_name + '.' + self.tld2
179
- self.find_provider
176
+ self.domain_name = registration_name + "." + tld2
177
+ find_provider
180
178
  else # Bad format
181
179
  self.subdomains = self.tld = self.tld2 = ""
182
180
  self.domain_name = self.registration_name = name
@@ -187,7 +185,7 @@ module EmailAddress
187
185
  dn = @config[:address_fqdn_domain]
188
186
  if !dn
189
187
  if (host_part.nil? || host_part <= " ") && @config[:host_local]
190
- 'localhost'
188
+ "localhost"
191
189
  else
192
190
  host_part
193
191
  end
@@ -205,39 +203,37 @@ module EmailAddress
205
203
  return false unless registration_name
206
204
  find_provider
207
205
  return false unless config[:host_match]
208
- ! matches?(config[:host_match])
206
+ !matches?(config[:host_match])
209
207
  end
210
208
 
211
209
  def find_provider # :nodoc:
212
- return self.provider if self.provider
210
+ return provider if provider
213
211
 
214
- EmailAddress::Config.providers.each do |provider, config|
215
- if config[:host_match] && self.matches?(config[:host_match])
216
- return self.set_provider(provider, config)
212
+ Config.providers.each do |provider, config|
213
+ if config[:host_match] && matches?(config[:host_match])
214
+ return set_provider(provider, config)
217
215
  end
218
216
  end
219
217
 
220
- return self.set_provider(:default) unless self.dns_enabled?
218
+ return set_provider(:default) unless dns_enabled?
221
219
 
222
- provider = self.exchangers.provider
223
- if provider != :default
224
- self.set_provider(provider,
225
- EmailAddress::Config.provider(provider))
226
- end
227
-
228
- self.provider ||= self.set_provider(:default)
220
+ self.provider ||= set_provider(:default)
229
221
  end
230
222
 
231
- def set_provider(name, provider_config={}) # :nodoc:
232
- self.config = EmailAddress::Config.all_settings(provider_config, @config)
233
- self.provider = name
223
+ def set_provider(name, provider_config = {}) # :nodoc:
224
+ config.configure(provider_config)
225
+ @provider = name
234
226
  end
235
227
 
236
228
  # Returns a hash of the parts of the host name after parsing.
237
229
  def parts
238
- { host_name:self.host_name, dns_name:self.dns_name, subdomain:self.subdomains,
239
- registration_name:self.registration_name, domain_name:self.domain_name,
240
- tld2:self.tld2, tld:self.tld, ip_address:self.ip_address }
230
+ {host_name: host_name, dns_name: dns_name, subdomain: subdomains,
231
+ registration_name: registration_name, domain_name: domain_name,
232
+ tld2: tld2, tld: tld, ip_address: ip_address,}
233
+ end
234
+
235
+ def hosted_provider
236
+ Exchanger.cached(dns_name).provider
241
237
  end
242
238
 
243
239
  ############################################################################
@@ -246,19 +242,19 @@ module EmailAddress
246
242
 
247
243
  # Is this a fully-qualified domain name?
248
244
  def fqdn?
249
- self.tld ? true : false
245
+ tld ? true : false
250
246
  end
251
247
 
252
248
  def ip?
253
- self.ip_address.nil? ? false : true
249
+ ip_address.nil? ? false : true
254
250
  end
255
251
 
256
252
  def ipv4?
257
- self.ip? && self.ip_address.include?(".")
253
+ ip? && ip_address.include?(".")
258
254
  end
259
255
 
260
256
  def ipv6?
261
- self.ip? && self.ip_address.include?(":")
257
+ ip? && ip_address.include?(":")
262
258
  end
263
259
 
264
260
  ############################################################################
@@ -276,25 +272,25 @@ module EmailAddress
276
272
  rules = Array(rules)
277
273
  return false if rules.empty?
278
274
  rules.each do |rule|
279
- return rule if rule == self.domain_name || rule == self.dns_name
275
+ return rule if rule == domain_name || rule == dns_name
280
276
  return rule if registration_name_matches?(rule)
281
277
  return rule if tld_matches?(rule)
282
278
  return rule if domain_matches?(rule)
283
279
  return rule if self.provider && provider_matches?(rule)
284
- return rule if self.ip_matches?(rule)
280
+ return rule if ip_matches?(rule)
285
281
  end
286
282
  false
287
283
  end
288
284
 
289
285
  # Does "example." match any tld?
290
286
  def registration_name_matches?(rule)
291
- "#{self.registration_name}." == rule ? true : false
287
+ rule == "#{registration_name}."
292
288
  end
293
289
 
294
290
  # Does "sub.example.com" match ".com" and ".example.com" top level names?
295
291
  # Matches TLD (uk) or TLD2 (co.uk)
296
292
  def tld_matches?(rule)
297
- rule.match(/\A\.(.+)\z/) && ($1 == self.tld || $1 == self.tld2) ? true : false
293
+ rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) ? true : false
298
294
  end
299
295
 
300
296
  def provider_matches?(rule)
@@ -305,20 +301,20 @@ module EmailAddress
305
301
  # Requires optionally starts with a "@".
306
302
  def domain_matches?(rule)
307
303
  rule = $1 if rule =~ /\A@(.+)/
308
- return rule if File.fnmatch?(rule, self.domain_name) if self.domain_name
309
- return rule if File.fnmatch?(rule, self.dns_name) if self.dns_name
304
+ return rule if domain_name && File.fnmatch?(rule, domain_name)
305
+ return rule if dns_name && File.fnmatch?(rule, dns_name)
310
306
  false
311
307
  end
312
308
 
313
309
  # True if the host is an IP Address form, and that address matches
314
310
  # the passed CIDR string ("10.9.8.0/24" or "2001:..../64")
315
311
  def ip_matches?(cidr)
316
- return false unless self.ip_address
317
- return cidr if !cidr.include?("/") && cidr == self.ip_address
318
- if cidr.include?(":") && self.ip_address.include?(":")
319
- return cidr if NetAddr::IPv6Net.parse(cidr).contains(NetAddr::IPv6.parse(self.ip_address))
320
- elsif cidr.include?(".") && self.ip_address.include?(".")
321
- return cidr if NetAddr::IPv4Net.parse(cidr).contains(NetAddr::IPv4.parse(self.ip_address))
312
+ return false unless ip_address
313
+ return cidr if !cidr.include?("/") && cidr == ip_address
314
+ if cidr.include?(":") && ip_address.include?(":")
315
+ return cidr if NetAddr::IPv6Net.parse(cidr).contains(NetAddr::IPv6.parse(ip_address))
316
+ elsif cidr.include?(".") && ip_address.include?(".")
317
+ return cidr if NetAddr::IPv4Net.parse(cidr).contains(NetAddr::IPv4.parse(ip_address))
322
318
  end
323
319
  false
324
320
  end
@@ -329,33 +325,36 @@ module EmailAddress
329
325
 
330
326
  # True if the :dns_lookup setting is enabled
331
327
  def dns_enabled?
332
- [:mx, :a].include?(EmailAddress::Config.setting(:host_validation))
328
+ return false if @config[:dns_lookup] == :off
329
+ return false if @config[:host_validation] == :syntax
330
+ true
333
331
  end
334
332
 
335
333
  # Returns: [official_hostname, alias_hostnames, address_family, *address_list]
336
334
  def dns_a_record
337
335
  @_dns_a_record = "0.0.0.0" if @config[:dns_lookup] == :off
338
- @_dns_a_record ||= Socket.gethostbyname(self.dns_name)
336
+ @_dns_a_record ||= Socket.gethostbyname(dns_name)
339
337
  rescue SocketError # not found, but could also mean network not work
340
338
  @_dns_a_record ||= []
341
339
  end
342
340
 
343
- # Returns an array of EmailAddress::Exchanger hosts configured in DNS.
341
+ # Returns an array of Exchanger hosts configured in DNS.
344
342
  # The array will be empty if none are configured.
345
343
  def exchangers
346
- #return nil if @config[:host_type] != :email || !self.dns_enabled?
347
- @_exchangers ||= EmailAddress::Exchanger.cached(self.dns_name, @config)
344
+ # return nil if @config[:host_type] != :email || !self.dns_enabled?
345
+ @_exchangers ||= Exchanger.cached(dns_name, @config)
348
346
  end
349
347
 
350
348
  # Returns a DNS TXT Record
351
- def txt(alternate_host=nil)
349
+ def txt(alternate_host = nil)
350
+ return nil unless dns_enabled?
352
351
  Resolv::DNS.open do |dns|
353
352
  dns.timeouts = @config[:dns_timeout] if @config[:dns_timeout]
354
353
  records = begin
355
- dns.getresources(alternate_host || self.dns_name,
356
- Resolv::DNS::Resource::IN::TXT)
357
- rescue Resolv::ResolvTimeout
358
- []
354
+ dns.getresources(alternate_host || dns_name,
355
+ Resolv::DNS::Resource::IN::TXT)
356
+ rescue Resolv::ResolvTimeout
357
+ []
359
358
  end
360
359
 
361
360
  records.empty? ? nil : records.map(&:data).join(" ")
@@ -363,13 +362,13 @@ module EmailAddress
363
362
  end
364
363
 
365
364
  # Parses TXT record pairs into a hash
366
- def txt_hash(alternate_host=nil)
365
+ def txt_hash(alternate_host = nil)
367
366
  fields = {}
368
- record = self.txt(alternate_host)
367
+ record = txt(alternate_host)
369
368
  return fields unless record
370
369
 
371
370
  record.split(/\s*;\s*/).each do |pair|
372
- (n,v) = pair.split(/\s*=\s*/)
371
+ (n, v) = pair.split(/\s*=\s*/)
373
372
  fields[n.to_sym] = v
374
373
  end
375
374
  fields
@@ -378,7 +377,7 @@ module EmailAddress
378
377
  # Returns a hash of the domain's DMARC (https://en.wikipedia.org/wiki/DMARC)
379
378
  # settings.
380
379
  def dmarc
381
- self.dns_name ? self.txt_hash("_dmarc." + self.dns_name) : {}
380
+ dns_name ? txt_hash("_dmarc." + dns_name) : {}
382
381
  end
383
382
 
384
383
  ############################################################################
@@ -386,13 +385,13 @@ module EmailAddress
386
385
  ############################################################################
387
386
 
388
387
  # Returns true if the host name is valid according to the current configuration
389
- def valid?(rules={})
388
+ def valid?(rules = {})
390
389
  host_validation = rules[:host_validation] || @config[:host_validation] || :mx
391
- dns_lookup = rules[:dns_lookup] || host_validation
390
+ dns_lookup = rules[:dns_lookup] || host_validation
392
391
  self.error_message = nil
393
- if self.ip_address
392
+ if ip_address
394
393
  valid_ip?
395
- elsif ! valid_format?
394
+ elsif !valid_format?
396
395
  false
397
396
  elsif dns_lookup == :connect
398
397
  valid_mx? && connect
@@ -407,8 +406,9 @@ module EmailAddress
407
406
 
408
407
  # True if the host name has a DNS A Record
409
408
  def valid_dns?
409
+ return true unless dns_enabled?
410
410
  bool = dns_a_record.size > 0 || set_error(:domain_unknown)
411
- if self.localhost? && !@config[:host_local]
411
+ if localhost? && !@config[:host_local]
412
412
  bool = set_error(:domain_no_localhost)
413
413
  end
414
414
  bool
@@ -416,10 +416,11 @@ module EmailAddress
416
416
 
417
417
  # True if the host name has valid MX servers configured in DNS
418
418
  def valid_mx?
419
- if self.exchangers.nil?
419
+ return true unless dns_enabled?
420
+ if exchangers.nil?
420
421
  set_error(:domain_unknown)
421
- elsif self.exchangers.mx_ips.size > 0
422
- if self.localhost? && !@config[:host_local]
422
+ elsif exchangers.mx_ips.size > 0
423
+ if localhost? && !@config[:host_local]
423
424
  set_error(:domain_no_localhost)
424
425
  else
425
426
  true
@@ -433,9 +434,9 @@ module EmailAddress
433
434
 
434
435
  # True if the host_name passes Regular Expression match and size limits.
435
436
  def valid_format?
436
- if self.host_name =~ CANONICAL_HOST_REGEX && self.to_s.size <= MAX_HOST_LENGTH
437
+ if host_name =~ CANONICAL_HOST_REGEX && to_s.size <= MAX_HOST_LENGTH
437
438
  return true if localhost?
438
- return true if self.host_name.include?(".") # require FQDN
439
+ return true if host_name.include?(".") # require FQDN
439
440
  end
440
441
  set_error(:domain_invalid)
441
442
  end
@@ -444,12 +445,12 @@ module EmailAddress
444
445
  # is a potentially valid IP address. It does not check if the address
445
446
  # is reachable.
446
447
  def valid_ip?
447
- if ! @config[:host_allow_ip]
448
+ if !@config[:host_allow_ip]
448
449
  bool = set_error(:ip_address_forbidden)
449
- elsif self.ip_address.include?(":")
450
- bool = self.ip_address =~ Resolv::IPv6::Regex ? true : set_error(:ipv6_address_invalid)
451
- elsif self.ip_address.include?(".")
452
- bool = self.ip_address =~ Resolv::IPv4::Regex ? true : set_error(:ipv4_address_invalid)
450
+ elsif ip_address.include?(":")
451
+ bool = ip_address.match(Resolv::IPv6::Regex) ? true : set_error(:ipv6_address_invalid)
452
+ elsif ip_address.include?(".")
453
+ bool = ip_address.match(Resolv::IPv4::Regex) ? true : set_error(:ipv4_address_invalid)
453
454
  end
454
455
  if bool && (localhost? && !@config[:host_local])
455
456
  bool = set_error(:ip_address_no_localhost)
@@ -458,20 +459,20 @@ module EmailAddress
458
459
  end
459
460
 
460
461
  def localhost?
461
- if self.ip_address
462
+ if ip_address
462
463
  rel =
463
- if self.ip_address.include?(":")
464
- NetAddr::IPv6Net.parse(""+"::1").rel(
465
- NetAddr::IPv6Net.parse(self.ip_address)
464
+ if ip_address.include?(":")
465
+ NetAddr::IPv6Net.parse("" + "::1").rel(
466
+ NetAddr::IPv6Net.parse(ip_address)
466
467
  )
467
468
  else
468
- NetAddr::IPv4Net.parse(""+"127.0.0.0/8").rel(
469
- NetAddr::IPv4Net.parse(self.ip_address)
469
+ NetAddr::IPv4Net.parse("" + "127.0.0.0/8").rel(
470
+ NetAddr::IPv4Net.parse(ip_address)
470
471
  )
471
472
  end
472
473
  !rel.nil? && rel >= 0
473
474
  else
474
- self.host_name == 'localhost'
475
+ host_name == "localhost"
475
476
  end
476
477
  end
477
478
 
@@ -479,33 +480,30 @@ module EmailAddress
479
480
  # as an email address check, but is provided to assist in problem resolution.
480
481
  # If you abuse this, you *could* be blocked by the ESP.
481
482
  def connect
482
- begin
483
- smtp = Net::SMTP.new(self.host_name || self.ip_address)
484
- smtp.start(@config[:helo_name] || 'localhost')
483
+ smtp = Net::SMTP.new(host_name || ip_address)
484
+ smtp.start(@config[:helo_name] || "localhost")
485
+ smtp.finish
486
+ true
487
+ rescue Net::SMTPFatalError => e
488
+ set_error(:server_not_available, e.to_s)
489
+ rescue SocketError => e
490
+ set_error(:server_not_available, e.to_s)
491
+ ensure
492
+ if smtp&.started?
485
493
  smtp.finish
486
- true
487
- rescue Net::SMTPFatalError => e
488
- set_error(:server_not_available, e.to_s)
489
- rescue SocketError => e
490
- set_error(:server_not_available, e.to_s)
491
- ensure
492
- if smtp && smtp.started?
493
- smtp.finish
494
- end
495
494
  end
496
495
  end
497
496
 
498
- def set_error(err, reason=nil)
499
- @error = err
500
- @reason = reason
501
- @error_message = EmailAddress::Config.error_message(err)
497
+ def set_error(err, reason = nil)
498
+ @error = err
499
+ @reason = reason
500
+ @error_message = Config.error_message(err)
502
501
  false
503
502
  end
504
503
 
505
504
  # The inverse of valid? -- Returns nil (falsey) if valid, otherwise error message
506
505
  def error
507
- self.valid? ? nil : @error_message
506
+ valid? ? nil : @error_message
508
507
  end
509
-
510
508
  end
511
509
  end