email_address 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ################################################################################
2
4
  # ActiveRecord v5.0 Custom Type
3
5
  #
@@ -27,20 +29,22 @@
27
29
  # user.canonical_email #=> "patsmith@gmail.com"
28
30
  ################################################################################
29
31
 
30
- class EmailAddress::EmailAddressType < ActiveRecord::Type::Value
32
+ module EmailAddress
33
+ class EmailAddressType < ActiveRecord::Type::Value
31
34
 
32
- # From user input, setter
33
- def cast(value)
34
- super(EmailAddress.normal(value))
35
- end
35
+ # From user input, setter
36
+ def cast(value)
37
+ super(Address.new(value).normal)
38
+ end
36
39
 
37
- # From a database value
38
- def deserialize(value)
39
- value && EmailAddress.normal(value)
40
- end
41
- #
42
- # To a database value (string)
43
- def serialize(value)
44
- 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
45
49
  end
46
50
  end
@@ -1,41 +1,44 @@
1
- require 'resolv'
2
- require 'netaddr'
3
- require 'socket'
1
+ # frozen_string_literal: true
2
+
3
+ require "resolv"
4
+ require "socket"
4
5
 
5
6
  module EmailAddress
6
7
  class Exchanger
7
8
  include Enumerable
8
9
 
9
- def self.cached(host)
10
+ def self.cached(host, config = {})
10
11
  @host_cache ||= {}
11
- @cache_size ||= ENV['EMAIL_ADDRESS_CACHE_SIZE'].to_i || 100
12
+ @cache_size ||= ENV["EMAIL_ADDRESS_CACHE_SIZE"].to_i || 100
12
13
  if @host_cache.has_key?(host)
13
14
  o = @host_cache.delete(host)
14
15
  @host_cache[host] = o # LRU cache, move to end
15
16
  elsif @host_cache.size >= @cache_size
16
17
  @host_cache.delete(@host_cache.keys.first)
17
- @host_cache[host] = new(host)
18
+ @host_cache[host] = new(host, config)
18
19
  else
19
- @host_cache[host] = new(host)
20
+ @host_cache[host] = new(host, config)
20
21
  end
21
22
  end
22
23
 
23
- def initialize(host, config={})
24
+ def initialize(host, config = {})
24
25
  @host = host
25
- @config = config
26
+ @config = config.is_a?(Hash) ? Config.new(config) : config
27
+ @dns_disabled = @config[:host_validation] == :syntax || @config[:dns_lookup] == :off
26
28
  end
27
29
 
28
30
  def each(&block)
31
+ return if @dns_disabled
29
32
  mxers.each do |m|
30
- yield({host:m[0], ip:m[1], priority:m[2]})
33
+ yield({host: m[0], ip: m[1], priority: m[2]})
31
34
  end
32
35
  end
33
36
 
34
37
  # Returns the provider name based on the MX-er host names, or nil if not matched
35
38
  def provider
36
39
  return @provider if defined? @provider
37
- EmailAddress::Config.providers.each do |provider, config|
38
- if config[:exchanger_match] && self.matches?(config[:exchanger_match])
40
+ Config.providers.each do |provider, config|
41
+ if config[:exchanger_match] && matches?(config[:exchanger_match])
39
42
  return @provider = provider
40
43
  end
41
44
  end
@@ -44,28 +47,41 @@ module EmailAddress
44
47
 
45
48
  # Returns: [["mta7.am0.yahoodns.net", "66.94.237.139", 1], ["mta5.am0.yahoodns.net", "67.195.168.230", 1], ["mta6.am0.yahoodns.net", "98.139.54.60", 1]]
46
49
  # If not found, returns []
50
+ # Returns a dummy record when dns_lookup is turned off since it may exists, though
51
+ # may not find provider by MX name or IP. I'm not sure about the "0.0.0.0" ip, it should
52
+ # be good in this context, but in "listen" context it means "all bound IP's"
47
53
  def mxers
48
- @mxers ||= Resolv::DNS.open do |dns|
49
- ress = dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
50
- records = ress.map do |r|
51
- begin
52
- [r.exchange.to_s, IPSocket::getaddress(r.exchange.to_s), r.preference]
53
- rescue SocketError # not found, but could also mean network not work or it could mean one record doesn't resolve an address
54
- nil
55
- end
54
+ return [["example.com", "0.0.0.0", 1]] if @dns_disabled
55
+ @mxers ||= Resolv::DNS.open { |dns|
56
+ dns.timeouts = @config[:dns_timeout] if @config[:dns_timeout]
57
+
58
+ ress = begin
59
+ dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
60
+ rescue Resolv::ResolvTimeout
61
+ []
56
62
  end
63
+
64
+ records = ress.map { |r|
65
+ if r.exchange.to_s > " "
66
+ [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference]
67
+ end
68
+ }
57
69
  records.compact
58
- end
70
+ }
71
+ # not found, but could also mean network not work or it could mean one record doesn't resolve an address
72
+ rescue SocketError
73
+ [["example.com", "0.0.0.0", 1]]
59
74
  end
60
75
 
61
76
  # Returns Array of domain names for the MX'ers, used to determine the Provider
62
77
  def domains
63
- @_domains ||= mxers.map {|m| EmailAddress::Host.new(m.first).domain_name }.sort.uniq
78
+ @_domains ||= mxers.map { |m| Host.new(m.first).domain_name }.sort.uniq
64
79
  end
65
80
 
66
81
  # Returns an array of MX IP address (String) for the given email domain
67
82
  def mx_ips
68
- mxers.map {|m| m[1] }
83
+ return ["0.0.0.0"] if @dns_disabled
84
+ mxers.map { |m| m[1] }
69
85
  end
70
86
 
71
87
  # Simple matcher, takes an array of CIDR addresses (ip/bits) and strings.
@@ -78,9 +94,9 @@ module EmailAddress
78
94
  rules = Array(rules)
79
95
  rules.each do |rule|
80
96
  if rule.include?("/")
81
- return rule if self.in_cidr?(rule)
97
+ return rule if in_cidr?(rule)
82
98
  else
83
- self.each {|mx| return rule if mx[:host].end_with?(rule) }
99
+ each { |mx| return rule if mx[:host].end_with?(rule) }
84
100
  end
85
101
  end
86
102
  false
@@ -88,14 +104,9 @@ module EmailAddress
88
104
 
89
105
  # Given a cidr (ip/bits) and ip address, returns true on match. Caches cidr object.
90
106
  def in_cidr?(cidr)
91
- c = NetAddr::CIDR.create(cidr)
92
- if cidr.include?(":")
93
- mx_ips.find { |ip| ip.include?(":") && c.matches?(ip) } ? true : false
94
- elsif cidr.include?(".")
95
- mx_ips.find { |ip| !ip.include?(":") && c.matches?(ip) } ? true : false
96
- else
97
- false
98
- end
107
+ net = IPAddr.new(cidr)
108
+ found = mx_ips.detect { |ip| net.include?(IPAddr.new(ip)) }
109
+ !!found
99
110
  end
100
111
  end
101
112
  end