email_address 0.1.19 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adfafdd9c68cbc88c13f77175ab945fce253b0c67d93cbf92f27d95358d9286d
4
- data.tar.gz: 07ea20d4fddbc9a2626a870b40162fe5ecaedfd7f588ba56f8d9efffce9a4eac
3
+ metadata.gz: c6784502377cd86fe2b6bb53c53ef7700673ed0317d2c96850ba0e9faad32c72
4
+ data.tar.gz: 5a558e95e390bf608225149fa2cb0948dcd9455607592e0c92156f34dced0807
5
5
  SHA512:
6
- metadata.gz: 45005aaae90329115140e038aa9f17325d66a939de376a2efc0fdda6542a1b47850ae40b80503fd69e1308f3cfe2fb5392904d2ca5ba0f997b00b6712e01ccb9
7
- data.tar.gz: e69900c156e676aac54f5f2d3ca395f08f08d07f768913df990d8b3c012e12e5cda678145f1dcdb0d141691ebf93d2beb85ba3b8dd654522fbb121962529585c
6
+ metadata.gz: 65cd1a6acccd37f3580762ba5d97e2956833c979a9fa639369385427bfe0ebf3cca693014d442ab530a005349e6fd36bce3d0b7372f432230bfcc4e0d3d15d79
7
+ data.tar.gz: 6543b62b3d0650717c81d2b25de2008fe6b898707a82c8779646364cb351654322958d38dfbf6b9420728a1eb7d6047d2d4205f0e11254e9d868930f6db74243
@@ -0,0 +1,18 @@
1
+ name: CI Build
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ matrix:
8
+ ruby-version: [2.6, 2.7, 3.0, 3.1, jruby]
9
+ steps:
10
+ - uses: actions/checkout@v2
11
+ - uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: ${{ matrix.ruby-version }}
14
+ bundler-cache: true # runs `bundle install` and caches installed gems automatically
15
+ - name: Install dependencies
16
+ run: bundle install
17
+ - name: Run tests
18
+ run: bundle exec rake
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in email_address.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Email Address
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/email_address.svg)](http://rubygems.org/gems/email_address)
4
- [![Build Status](https://travis-ci.org/afair/email_address.svg?branch=v0.1)](https://travis-ci.org/afair/email_address)
4
+ [![CI Build](https://github.com/afair/email_address/actions/workflows/ci.yml/badge.svg)](https://github.com/afair/email_address/actions/workflows/ci.yml)
5
5
  [![Code Climate](https://codeclimate.com/github/afair/email_address/badges/gpa.svg)](https://codeclimate.com/github/afair/email_address)
6
6
 
7
7
  The `email_address` gem provides a ruby language library for working
@@ -98,7 +98,7 @@ introduces terms to distinguish types of email addresses.
98
98
  madness!."()<>[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a(comment )"@example.org
99
99
 
100
100
  * *Base* - A unique mailbox without tags. For gmail, is uses the incoming
101
- punctation, essential when building an MD5 or SHA1 to match services
101
+ punctation, essential when building an MD5, SHA1, or SHA256 to match services
102
102
  like Gravatar, and email address digest interchange.
103
103
 
104
104
  * *Canonical* - An unique account address, lower-cased, without the
@@ -234,6 +234,7 @@ Here are some other methods that are available.
234
234
  ```ruby
235
235
  email.redact #=> "{bea3f3560a757f8142d38d212a931237b218eb5e}@gmail.com"
236
236
  email.sha1 #=> "bea3f3560a757f8142d38d212a931237b218eb5e"
237
+ email.sha256 #=> "9e2a0270f2d6778e5f647fc9eaf6992705ca183c23d1ed1166586fd54e859f75"
237
238
  email.md5 #=> "c5be3597c391169a5ad2870f9ca51901"
238
239
  email.host_name #=> "gmail.com"
239
240
  email.provider #=> :google
@@ -272,6 +273,25 @@ class User < ActiveRecord::Base
272
273
  end
273
274
  ```
274
275
 
276
+ #### Rails I18n
277
+
278
+ Copy and adapt `lib/email_address/messages.yaml` into your locales and
279
+ create an after initialization callback:
280
+
281
+ ```ruby
282
+ # config/initializers/email_address.rb
283
+
284
+ Rails.application.config.after_initialize do
285
+ I18n.available_locales.each do |locale|
286
+ translations = I18n.t(:email_address, locale: locale)
287
+
288
+ next unless translations.is_a? Hash
289
+
290
+ EmailAddress::Config.error_messages translations.transform_keys(&:to_s), locale.to_s
291
+ end
292
+ end
293
+ ```
294
+
275
295
  #### Rails Email Address Type Attribute
276
296
 
277
297
  Initial support is provided for Active Record 5.0 attributes API.
@@ -476,6 +496,11 @@ Full translation support would be ideal though.
476
496
  the SHA1 Digest, making it unique to your application so it can't easily be
477
497
  discovered by comparing against a known list of email/sha1 pairs.
478
498
 
499
+ * sha256_secret -
500
+ This application-level secret is appended to the email_address to compute
501
+ the SHA256 Digest, making it unique to your application so it can't easily be
502
+ discovered by comparing against a known list of email/sha256 pairs.
503
+
479
504
  * munge_string - "*****", the string to replace into munged addresses.
480
505
 
481
506
  For local part configuration:
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
3
  require "bundler/setup"
4
- require 'rake/testtask'
4
+ require "rake/testtask"
5
5
 
6
- task :default => :test
6
+ task default: :test
7
7
 
8
8
  desc "Run the Test Suite, toot suite"
9
9
  Rake::TestTask.new do |t|
@@ -1,37 +1,37 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'email_address/version'
3
+ require "email_address/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "email_address"
8
- spec.version = EmailAddress::VERSION
9
- spec.authors = ["Allen Fair"]
10
- spec.email = ["allen.fair@gmail.com"]
11
- spec.description = %q{The EmailAddress Gem to work with and validate email addresses.}
12
- spec.summary = %q{This gem provides a ruby language library for working with and validating email addresses. By default, it validates against conventional usage, the format preferred for user email addresses. It can be configured to validate against RFC “Standard” formats, common email service provider formats, and perform DNS validation.}
13
- spec.homepage = "https://github.com/afair/email_address"
14
- spec.license = "MIT"
6
+ spec.name = "email_address"
7
+ spec.version = EmailAddress::VERSION
8
+ spec.authors = ["Allen Fair"]
9
+ spec.email = ["allen.fair@gmail.com"]
10
+ spec.description = "The EmailAddress Gem to work with and validate email addresses."
11
+ spec.summary = "This gem provides a ruby language library for working with and validating email addresses. By default, it validates against conventional usage, the format preferred for user email addresses. It can be configured to validate against RFC “Standard” formats, common email service provider formats, and perform DNS validation."
12
+ spec.homepage = "https://github.com/afair/email_address"
13
+ spec.license = "MIT"
15
14
 
16
- #spec.required_ruby_version = ">= 2.3.0"
17
- spec.files = `git ls-files`.split($/)
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.required_ruby_version = ">= 2.5", "< 4"
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
19
  spec.require_paths = ["lib"]
21
20
 
22
21
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency "minitest", "~> 5.11"
24
- spec.add_development_dependency "bundler" #, "~> 1.16.0"
25
- if RUBY_PLATFORM == 'java'
26
- spec.add_development_dependency "activerecord", "= 4.2.10"
27
- spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", '~> 1.3.24'
22
+ spec.add_development_dependency "minitest", "~> 5.11"
23
+ spec.add_development_dependency "bundler" # , "~> 1.16.0"
24
+ if RUBY_PLATFORM == "java"
25
+ spec.add_development_dependency "activerecord", "~> 5.2.6"
26
+ spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 52.7"
28
27
  else
29
- spec.add_development_dependency "activerecord", "~> 5.2.4"
28
+ spec.add_development_dependency "activerecord", "~> 6.1.4"
30
29
  spec.add_development_dependency "sqlite3"
30
+ spec.add_development_dependency "standard", "~> 1.5.0"
31
31
  end
32
- #spec.add_development_dependency "codeclimate-test-reporter"
32
+ # spec.add_development_dependency "net-smtp"
33
33
  spec.add_development_dependency "simplecov"
34
+ spec.add_development_dependency "pry"
34
35
 
35
36
  spec.add_dependency "simpleidn"
36
- spec.add_dependency "netaddr", '>= 2.0.4', '< 3'
37
37
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EmailAddress
4
-
5
4
  # ActiveRecord validator class for validating an email
6
5
  # address with this library.
7
6
  # Note the initialization happens once per process.
@@ -16,14 +15,13 @@ module EmailAddress
16
15
  # Default field: :email or :email_address (first found)
17
16
  #
18
17
  class ActiveRecordValidator < ActiveModel::Validator
19
-
20
- def initialize(options={})
18
+ def initialize(options = {})
21
19
  @opt = options
22
20
  end
23
21
 
24
22
  def validate(r)
25
23
  if @opt[:fields]
26
- @opt[:fields].each {|f| validate_email(r, f) }
24
+ @opt[:fields].each { |f| validate_email(r, f) }
27
25
  elsif @opt[:field]
28
26
  validate_email(r, @opt[:field])
29
27
  elsif r.respond_to? :email
@@ -33,17 +31,15 @@ module EmailAddress
33
31
  end
34
32
  end
35
33
 
36
- def validate_email(r,f)
34
+ def validate_email(r, f)
37
35
  return if r[f].nil?
38
36
  e = Address.new(r[f])
39
37
  unless e.valid?
40
38
  error_message = @opt[:message] ||
41
- Config.error_messages[:invalid_address] ||
42
- "Invalid Email Address"
39
+ Config.error_message(:invalid_address, I18n.locale.to_s) ||
40
+ "Invalid Email Address"
43
41
  r.errors.add(f, error_message)
44
42
  end
45
43
  end
46
-
47
44
  end
48
-
49
45
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest/sha1"
4
+ require "digest/sha2"
4
5
  require "digest/md5"
5
6
 
6
7
  module EmailAddress
@@ -10,7 +11,7 @@ module EmailAddress
10
11
  include Comparable
11
12
  include Rewriter
12
13
 
13
- attr_accessor :original, :local, :host, :config, :reason
14
+ attr_accessor :original, :local, :host, :config, :reason, :locale
14
15
 
15
16
  CONVENTIONAL_REGEX = /\A#{Local::CONVENTIONAL_MAILBOX_WITHIN}
16
17
  @#{Host::DNS_HOST_REGEX}\z/x
@@ -22,15 +23,16 @@ module EmailAddress
22
23
  # Given an email address of the form "local@hostname", this sets up the
23
24
  # instance, and initializes the address to the "normalized" format of the
24
25
  # address. The original string is available in the #original method.
25
- def initialize(email_address, config = {})
26
+ def initialize(email_address, config = {}, locale = "en")
26
27
  @config = Config.new(config)
27
28
  @original = email_address
29
+ @locale = locale
28
30
  email_address = (email_address || "").strip
29
31
  email_address = parse_rewritten(email_address) unless config[:skip_rewrite]
30
32
  local, host = Address.split_local_host(email_address)
31
33
 
32
- @host = Host.new(host, @config)
33
- @local = Local.new(local, @config, @host)
34
+ @host = Host.new(host, @config, locale)
35
+ @local = Local.new(local, @config, @host, locale)
34
36
  @error = @error_message = nil
35
37
  end
36
38
 
@@ -86,8 +88,8 @@ module EmailAddress
86
88
  def host_name
87
89
  @host.host_name
88
90
  end
89
- alias right host_name
90
- alias hostname host_name
91
+ alias_method :right, :host_name
92
+ alias_method :hostname, :host_name
91
93
 
92
94
  # Returns the ESP (Email Service Provider) or ISP name derived
93
95
  # using the provider configuration rules.
@@ -111,7 +113,7 @@ module EmailAddress
111
113
  "#{local}@#{host}"
112
114
  end
113
115
  end
114
- alias to_s normal
116
+ alias_method :to_s, :normal
115
117
 
116
118
  def inspect
117
119
  "#<#{self.class}:0x#{object_id.to_s(16)} address=\"#{self}\">"
@@ -159,32 +161,36 @@ module EmailAddress
159
161
  [local.munge, host.munge].join("@")
160
162
  end
161
163
 
162
- # Returns and MD5 of the canonical address form. Some cross-system systems
164
+ # Returns and MD5 of the base address form. Some cross-system systems
163
165
  # use the email address MD5 instead of the actual address to refer to the
164
166
  # same shared user identity without exposing the actual address when it
165
167
  # is not known in common.
166
168
  def reference(form = :base)
167
169
  Digest::MD5.hexdigest(send(form))
168
170
  end
169
- alias md5 reference
171
+ alias_method :md5, :reference
170
172
 
171
- # This returns the SHA1 digest (in a hex string) of the canonical email
173
+ # This returns the SHA1 digest (in a hex string) of the base email
172
174
  # address. See #md5 for more background.
173
175
  def sha1(form = :base)
174
176
  Digest::SHA1.hexdigest((send(form) || "") + (@config[:sha1_secret] || ""))
175
177
  end
176
178
 
179
+ def sha256(form = :base)
180
+ Digest::SHA256.hexdigest((send(form) || "") + (@config[:sha256_secret] || ""))
181
+ end
182
+
177
183
  #---------------------------------------------------------------------------
178
184
  # Comparisons & Matching
179
185
  #---------------------------------------------------------------------------
180
186
 
181
187
  # Equal matches the normalized version of each address. Use the Threequal to check
182
188
  # for match on canonical or redacted versions of addresses
183
- def ==(other_email)
184
- to_s == other_email.to_s
189
+ def ==(other)
190
+ to_s == other.to_s
185
191
  end
186
- alias eql? ==
187
- alias equal? ==
192
+ alias_method :eql?, :==
193
+ alias_method :equal?, :==
188
194
 
189
195
  # Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
190
196
  # of this addres with another, using the canonical or redacted forms.
@@ -197,12 +203,12 @@ module EmailAddress
197
203
  redact == other_email.canonical ||
198
204
  canonical == other_email.redact
199
205
  end
200
- alias include? same_as?
206
+ alias_method :include?, :same_as?
201
207
 
202
208
  # Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
203
209
  # of this addres with another, using the normalized form.
204
- def <=>(other_email)
205
- to_s <=> other_email.to_s
210
+ def <=>(other)
211
+ to_s <=> other.to_s
206
212
  end
207
213
 
208
214
  # Address matches one of these Matcher rule patterns
@@ -214,7 +220,7 @@ module EmailAddress
214
220
 
215
221
  # Does "root@*.com" match "root@example.com" domain name
216
222
  rules.each do |r|
217
- if r.match(/.+@.+/)
223
+ if /.+@.+/.match?(r)
218
224
  return r if File.fnmatch?(r, to_s)
219
225
  end
220
226
  end
@@ -252,6 +258,10 @@ module EmailAddress
252
258
  # Connects to host to test if user can receive email. This should NOT be performed
253
259
  # as an email address check, but is provided to assist in problem resolution.
254
260
  # If you abuse this, you *could* be blocked by the ESP.
261
+ #
262
+ # NOTE: As of Ruby 3.1, Net::SMTP was moved from the standard library to the
263
+ # 'net-smtp' gem. In order to avoid adding that dependency for this experimental
264
+ # feature, please add the gem to your Gemfile and require it to use this feature.
255
265
  def connect
256
266
  smtp = Net::SMTP.new(host_name || ip_address)
257
267
  smtp.start(@config[:smtp_helo_name] || "localhost")
@@ -276,7 +286,7 @@ module EmailAddress
276
286
  def set_error(err, reason = nil)
277
287
  @error = err
278
288
  @reason = reason
279
- @error_message = Config.error_message(err)
289
+ @error_message = Config.error_message(err, locale)
280
290
  false
281
291
  end
282
292
 
@@ -17,6 +17,11 @@ module EmailAddress
17
17
  # the SHA1 Digest, making it unique to your application so it can't easily be
18
18
  # discovered by comparing against a known list of email/sha1 pairs.
19
19
  #
20
+ # * sha256_secret ""
21
+ # This application-level secret is appended to the email_address to compute
22
+ # the SHA256 Digest, making it unique to your application so it can't easily be
23
+ # discovered by comparing against a known list of email/sha256 pairs.
24
+ #
20
25
  # For local part configuration:
21
26
  # * local_downcase: true
22
27
  # Downcase the local part. You probably want this for uniqueness.
@@ -104,6 +109,7 @@ module EmailAddress
104
109
  dns_lookup: :mx, # :mx, :a, :off
105
110
  dns_timeout: nil,
106
111
  sha1_secret: "",
112
+ sha256_secret: "",
107
113
  munge_string: "*****",
108
114
 
109
115
  local_downcase: true,
@@ -182,15 +188,20 @@ module EmailAddress
182
188
  end
183
189
 
184
190
  def self.error_message(name, locale = "en")
185
- @errors[locale]["email_address"][name.to_s] || name.to_s
191
+ @errors.dig(locale, "email_address", name.to_s) || name.to_s
186
192
  end
187
193
 
188
194
  # Customize your own error message text.
189
195
  def self.error_messages(hash = {}, locale = "en", *extra)
190
196
  hash = extra.first if extra.first.is_a? Hash
191
- unless hash.empty?
197
+
198
+ @errors[locale] ||= {}
199
+ @errors[locale]["email_address"] ||= {}
200
+
201
+ unless hash.nil? || hash.empty?
192
202
  @errors[locale]["email_address"] = @errors[locale]["email_address"].merge(hash)
193
203
  end
204
+
194
205
  @errors[locale]["email_address"]
195
206
  end
196
207
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "resolv"
4
- require "netaddr"
5
4
  require "socket"
6
5
 
7
6
  module EmailAddress
@@ -58,8 +57,8 @@ module EmailAddress
58
57
 
59
58
  ress = begin
60
59
  dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
61
- rescue Resolv::ResolvTimeout
62
- []
60
+ rescue Resolv::ResolvTimeout
61
+ []
63
62
  end
64
63
 
65
64
  records = ress.map { |r|
@@ -105,22 +104,9 @@ module EmailAddress
105
104
 
106
105
  # Given a cidr (ip/bits) and ip address, returns true on match. Caches cidr object.
107
106
  def in_cidr?(cidr)
108
- if cidr.include?(":")
109
- c = NetAddr::IPv6Net.parse(cidr)
110
- return true if mx_ips.find do |ip|
111
- next unless ip.include?(":")
112
- rel = c.rel NetAddr::IPv6Net.parse(ip)
113
- !rel.nil? && rel >= 0
114
- end
115
- elsif cidr.include?(".")
116
- c = NetAddr::IPv4Net.parse(cidr)
117
- return true if mx_ips.find do |ip|
118
- next if ip.include?(":")
119
- rel = c.rel NetAddr::IPv4Net.parse(ip)
120
- !rel.nil? && rel >= 0
121
- end
122
- end
123
- false
107
+ net = IPAddr.new(cidr)
108
+ found = mx_ips.detect { |ip| net.include?(IPAddr.new(ip)) }
109
+ !!found
124
110
  end
125
111
  end
126
112
  end
@@ -1,7 +1,5 @@
1
1
  require "simpleidn"
2
2
  require "resolv"
3
- require "netaddr"
4
- require "net/smtp"
5
3
 
6
4
  module EmailAddress
7
5
  ##############################################################################
@@ -34,15 +32,15 @@ module EmailAddress
34
32
  attr_reader :host_name
35
33
  attr_accessor :dns_name, :domain_name, :registration_name,
36
34
  :tld, :tld2, :subdomains, :ip_address, :config, :provider,
37
- :comment, :error_message, :reason
35
+ :comment, :error_message, :reason, :locale
38
36
  MAX_HOST_LENGTH = 255
39
37
 
40
38
  # Sometimes, you just need a Regexp...
41
- DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: \-{1,2} | \.) [\p{L}\p{N}]+ )*/x
39
+ DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: -{1,2} | \.) [\p{L}\p{N}]+ )*/x
42
40
 
43
41
  # The IPv4 and IPv6 were lifted from Resolv::IPv?::Regex and tweaked to not
44
42
  # \A...\z anchor at the edges.
45
- IPv6_HOST_REGEX = /\[IPv6:
43
+ IPV6_HOST_REGEX = /\[IPv6:
46
44
  (?: (?:(?x-mi:
47
45
  (?:[0-9A-Fa-f]{1,4}:){7}
48
46
  [0-9A-Fa-f]{1,4}
@@ -61,7 +59,7 @@ module EmailAddress
61
59
  (?: \d+)\.(?: \d+)\.(?: \d+)\.(?: \d+)
62
60
  )))\]/ix
63
61
 
64
- IPv4_HOST_REGEX = /\[((?x-mi:0
62
+ IPV4_HOST_REGEX = /\[((?x-mi:0
65
63
  |1(?:[0-9][0-9]?)?
66
64
  |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
67
65
  |[3-9][0-9]?))\.((?x-mi:0
@@ -80,12 +78,13 @@ module EmailAddress
80
78
 
81
79
  # Matches Host forms: DNS name, IPv4, or IPv6 formats
82
80
  STANDARD_HOST_REGEX = /\A (?: #{DNS_HOST_REGEX}
83
- | #{IPv4_HOST_REGEX} | #{IPv6_HOST_REGEX}) \z/ix
81
+ | #{IPV4_HOST_REGEX} | #{IPV6_HOST_REGEX}) \z/ix
84
82
 
85
83
  # host name -
86
84
  # * host type - :email for an email host, :mx for exchanger host
87
- def initialize(host_name, config = {})
85
+ def initialize(host_name, config = {}, locale = "en")
88
86
  @original = host_name ||= ""
87
+ @locale = locale
89
88
  config[:host_type] ||= :email
90
89
  @config = config.is_a?(Hash) ? Config.new(config) : config
91
90
  @error = @error_message = nil
@@ -104,7 +103,7 @@ module EmailAddress
104
103
  dns_name
105
104
  end
106
105
  end
107
- alias to_s name
106
+ alias_method :to_s, :name
108
107
 
109
108
  # The canonical host name is the simplified, DNS host name
110
109
  def canonical
@@ -149,7 +148,7 @@ module EmailAddress
149
148
  if @config[:host_remove_spaces]
150
149
  @host_name = @host_name.delete(" ")
151
150
  end
152
- @dns_name = if /[^[:ascii:]]/.match(host_name)
151
+ @dns_name = if /[^[:ascii:]]/.match?(host_name)
153
152
  ::SimpleIDN.to_ascii(host_name)
154
153
  else
155
154
  host_name
@@ -229,7 +228,7 @@ module EmailAddress
229
228
  def parts
230
229
  {host_name: host_name, dns_name: dns_name, subdomain: subdomains,
231
230
  registration_name: registration_name, domain_name: domain_name,
232
- tld2: tld2, tld: tld, ip_address: ip_address,}
231
+ tld2: tld2, tld: tld, ip_address: ip_address}
233
232
  end
234
233
 
235
234
  def hosted_provider
@@ -246,7 +245,7 @@ module EmailAddress
246
245
  end
247
246
 
248
247
  def ip?
249
- ip_address.nil? ? false : true
248
+ !!ip_address
250
249
  end
251
250
 
252
251
  def ipv4?
@@ -290,7 +289,7 @@ module EmailAddress
290
289
  # Does "sub.example.com" match ".com" and ".example.com" top level names?
291
290
  # Matches TLD (uk) or TLD2 (co.uk)
292
291
  def tld_matches?(rule)
293
- rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) ? true : false
292
+ rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) # ? true : false
294
293
  end
295
294
 
296
295
  def provider_matches?(rule)
@@ -310,13 +309,8 @@ module EmailAddress
310
309
  # the passed CIDR string ("10.9.8.0/24" or "2001:..../64")
311
310
  def ip_matches?(cidr)
312
311
  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))
318
- end
319
- false
312
+ net = IPAddr.new(cidr)
313
+ net.include?(IPAddr.new(ip_address))
320
314
  end
321
315
 
322
316
  ############################################################################
@@ -333,7 +327,7 @@ module EmailAddress
333
327
  # Returns: [official_hostname, alias_hostnames, address_family, *address_list]
334
328
  def dns_a_record
335
329
  @_dns_a_record = "0.0.0.0" if @config[:dns_lookup] == :off
336
- @_dns_a_record ||= Socket.gethostbyname(dns_name)
330
+ @_dns_a_record ||= Addrinfo.getaddrinfo(dns_name, 80) # Port 80 for A rec, 25 for MX
337
331
  rescue SocketError # not found, but could also mean network not work
338
332
  @_dns_a_record ||= []
339
333
  end
@@ -353,8 +347,8 @@ module EmailAddress
353
347
  records = begin
354
348
  dns.getresources(alternate_host || dns_name,
355
349
  Resolv::DNS::Resource::IN::TXT)
356
- rescue Resolv::ResolvTimeout
357
- []
350
+ rescue Resolv::ResolvTimeout
351
+ []
358
352
  end
359
353
 
360
354
  records.empty? ? nil : records.map(&:data).join(" ")
@@ -407,11 +401,7 @@ module EmailAddress
407
401
  # True if the host name has a DNS A Record
408
402
  def valid_dns?
409
403
  return true unless dns_enabled?
410
- bool = dns_a_record.size > 0 || set_error(:domain_unknown)
411
- if localhost? && !@config[:host_local]
412
- bool = set_error(:domain_no_localhost)
413
- end
414
- bool
404
+ dns_a_record.size > 0 || set_error(:domain_unknown)
415
405
  end
416
406
 
417
407
  # True if the host name has valid MX servers configured in DNS
@@ -435,7 +425,9 @@ module EmailAddress
435
425
  # True if the host_name passes Regular Expression match and size limits.
436
426
  def valid_format?
437
427
  if host_name =~ CANONICAL_HOST_REGEX && to_s.size <= MAX_HOST_LENGTH
438
- return true if localhost?
428
+ if localhost?
429
+ return @config[:host_local] ? true : set_error(:domain_no_localhost)
430
+ end
439
431
  return true if host_name.include?(".") # require FQDN
440
432
  end
441
433
  set_error(:domain_invalid)
@@ -459,26 +451,18 @@ module EmailAddress
459
451
  end
460
452
 
461
453
  def localhost?
462
- if ip_address
463
- rel =
464
- if ip_address.include?(":")
465
- NetAddr::IPv6Net.parse("" + "::1").rel(
466
- NetAddr::IPv6Net.parse(ip_address)
467
- )
468
- else
469
- NetAddr::IPv4Net.parse("" + "127.0.0.0/8").rel(
470
- NetAddr::IPv4Net.parse(ip_address)
471
- )
472
- end
473
- !rel.nil? && rel >= 0
474
- else
475
- host_name == "localhost"
476
- end
454
+ return true if host_name == "localhost"
455
+ return false unless ip_address
456
+ IPAddr.new(ip_address).loopback?
477
457
  end
478
458
 
479
459
  # Connects to host to test it can receive email. This should NOT be performed
480
460
  # as an email address check, but is provided to assist in problem resolution.
481
461
  # If you abuse this, you *could* be blocked by the ESP.
462
+ #
463
+ # NOTE: As of Ruby 3.1, Net::SMTP was moved from the standard library to the
464
+ # 'net-smtp' gem. In order to avoid adding that dependency for this experimental
465
+ # feature, please add the gem to your Gemfile and require it to use this feature.
482
466
  def connect
483
467
  smtp = Net::SMTP.new(host_name || ip_address)
484
468
  smtp.start(@config[:helo_name] || "localhost")
@@ -497,7 +481,7 @@ module EmailAddress
497
481
  def set_error(err, reason = nil)
498
482
  @error = err
499
483
  @reason = reason
500
- @error_message = Config.error_message(err)
484
+ @error_message = Config.error_message(err, locale)
501
485
  false
502
486
  end
503
487