email_address 0.1.13 → 0.1.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -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