gman 5.0.9 → 6.0.0

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.
@@ -1,36 +1,43 @@
1
- require File.expand_path "./lib/gman/version", File.dirname(__FILE__)
1
+ require File.expand_path './lib/gman/version', File.dirname(__FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = "gman"
5
- s.summary = "Check if a given domain or email address belong to a governemnt entity"
6
- s.description = "A ruby gem to check if the owner of a given email address is working for THE MAN."
4
+ s.name = 'gman'
5
+ s.summary = <<-EOF
6
+ Check if a given domain or email address belong to a governemnt entity
7
+ EOF
8
+ s.description = <<-EOF
9
+ A ruby gem to check if the owner of a given email address is working for
10
+ THE MAN.
11
+ EOF
7
12
  s.version = Gman::VERSION
8
- s.authors = ["Ben Balter"]
9
- s.email = "ben.balter@github.com"
10
- s.homepage = "https://github.com/benbalter/gman"
11
- s.licenses = ["MIT"]
13
+ s.authors = ['Ben Balter']
14
+ s.email = 'ben.balter@github.com'
15
+ s.homepage = 'https://github.com/benbalter/gman'
16
+ s.licenses = ['MIT']
12
17
 
13
- s.files = `git ls-files`.split("\n")
14
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
- s.require_paths = ["lib"]
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map do |f|
21
+ File.basename(f)
22
+ end
23
+ s.require_paths = ['lib']
17
24
 
18
- s.require_paths = ["lib"]
25
+ s.require_paths = ['lib']
19
26
  s.required_ruby_version = '~> 2.0'
20
27
 
21
- s.add_dependency( "swot", '~> 1.0' )
22
- s.add_dependency( "iso_country_codes", "~> 0.6" )
23
- s.add_dependency( "naughty_or_nice", "~> 2.0" )
24
- s.add_dependency( "colored", "~> 1.2" )
25
-
26
- s.add_development_dependency( "rake", "~> 10.4" )
27
- s.add_development_dependency( "shoulda", "~> 3.5" )
28
- s.add_development_dependency( "rdoc", "~> 4.2" )
29
- s.add_development_dependency( "bundler", "~> 1.10" )
30
- s.add_development_dependency( "pry", "~> 0.10" )
31
- s.add_development_dependency( "parallel", "~> 1.6" )
32
- s.add_development_dependency( "mechanize", "~> 2.7" )
33
- s.add_development_dependency( "addressable", "~> 2.3" )
34
- s.add_development_dependency( "ruby-prof", "~> 0.15" )
28
+ s.add_dependency('swot', '~> 1.0')
29
+ s.add_dependency('iso_country_codes', '~> 0.6')
30
+ s.add_dependency('naughty_or_nice', '~> 2.0')
31
+ s.add_dependency('colored', '~> 1.2')
35
32
 
33
+ s.add_development_dependency('rake', '~> 10.4')
34
+ s.add_development_dependency('shoulda', '~> 3.5')
35
+ s.add_development_dependency('rdoc', '~> 4.2')
36
+ s.add_development_dependency('bundler', '~> 1.10')
37
+ s.add_development_dependency('pry', '~> 0.10')
38
+ s.add_development_dependency('parallel', '~> 1.6')
39
+ s.add_development_dependency('mechanize', '~> 2.7')
40
+ s.add_development_dependency('addressable', '~> 2.3')
41
+ s.add_development_dependency('ruby-prof', '~> 0.15')
42
+ s.add_development_dependency('rubocop', '~> 0.37')
36
43
  end
@@ -6,30 +6,33 @@ require_relative 'gman/version'
6
6
  require_relative 'gman/country_codes'
7
7
  require_relative 'gman/locality'
8
8
  require_relative 'gman/identifier'
9
- require_relative 'gman/sanctions'
10
9
 
11
10
  class Gman
12
-
13
11
  include NaughtyOrNice
14
12
 
15
13
  class << self
16
14
  # returns an instance of our custom public suffix list
17
- # list behaves like PublicSuffix::List but is limited to our whitelisted domains
15
+ # list behaves like PublicSuffix::List
16
+ # but is limited to our whitelisted domains
18
17
  def list
19
- @@list ||= PublicSuffix::List::parse(list_contents)
18
+ @list ||= PublicSuffix::List.parse(list_contents)
20
19
  end
21
20
 
22
21
  def config_path
23
- File.join(File.dirname(__FILE__), "../config")
22
+ File.expand_path '../config', File.dirname(__FILE__)
24
23
  end
25
24
 
26
25
  # Returns the absolute path to the domain list
27
26
  def list_path
28
- File.join(config_path,"domains.txt")
27
+ if ENV['GMAN_STUB_DOMAINS']
28
+ File.expand_path '../test/fixtures/domains.txt', File.dirname(__FILE__)
29
+ else
30
+ File.expand_path 'domains.txt', config_path
31
+ end
29
32
  end
30
33
 
31
34
  def list_contents
32
- @@list_contents ||= File.new(list_path, "r:utf-8").read
35
+ @list_contents ||= File.new(list_path, 'r:utf-8').read
33
36
  end
34
37
  end
35
38
 
@@ -37,25 +40,28 @@ class Gman
37
40
  #
38
41
  # Returns boolean true if a government domain
39
42
  def valid?
40
- return @valid if defined? @valid
41
-
42
- @valid = begin
43
- # Ensure it's a valid domain
44
- return false unless domain && domain.valid?
43
+ @valid ||= begin
44
+ return false unless valid_domain?
45
+ return false if academic?
46
+ locality? || public_suffix_valid?
47
+ end
48
+ end
45
49
 
46
- # Ensure non-edu
47
- return false if Swot::is_academic?(domain)
50
+ private
48
51
 
49
- # Check for locality by regex
50
- return true if locality?
52
+ def valid_domain?
53
+ domain && domain.valid? && !academic?
54
+ end
51
55
 
52
- # check using public suffix's standard logic
53
- rule = Gman.list.find(to_s)
56
+ def academic?
57
+ domain && Swot.is_academic?(domain)
58
+ end
54
59
 
55
- # domain is on the domain list and
56
- # domain is not explicitly blacklisted and
57
- # domain matches a standard public suffix list rule
58
- !rule.nil? && rule.type != :exception && rule.allow?(".#{domain}")
59
- end
60
+ # domain is on the domain list and
61
+ # domain is not explicitly blacklisted and
62
+ # domain matches a standard public suffix list rule
63
+ def public_suffix_valid?
64
+ rule = Gman.list.find(to_s)
65
+ !rule.nil? && rule.type != :exception && rule.allow?(".#{domain}")
60
66
  end
61
67
  end
@@ -1,21 +1,20 @@
1
1
  class Gman
2
-
3
2
  # Map last part of TLD to alpha2 country code
4
3
  ALPHA2_MAP = {
5
- :ac => 'sh',
6
- :uk => 'gb',
7
- :su => 'ru',
8
- :tp => 'tl',
9
- :yu => 'rs',
10
- :gov => "us",
11
- :mil => "us",
12
- :org => "us",
13
- :com => "us",
14
- :net => "us",
15
- :edu => "us",
16
- :travel => "us",
17
- :info => "us"
18
- }
4
+ ac: 'sh',
5
+ uk: 'gb',
6
+ su: 'ru',
7
+ tp: 'tl',
8
+ yu: 'rs',
9
+ gov: 'us',
10
+ mil: 'us',
11
+ org: 'us',
12
+ com: 'us',
13
+ net: 'us',
14
+ edu: 'us',
15
+ travel: 'us',
16
+ info: 'us'
17
+ }.freeze
19
18
 
20
19
  # Returns the two character alpha county code represented by the domain
21
20
  #
@@ -1,13 +1,12 @@
1
1
  class Gman
2
2
  class DomainList
3
-
4
3
  attr_accessor :list
5
- alias_method :to_h, :list
4
+ alias to_h list
6
5
 
7
- COMMENT_REGEX = /\/\/[\/\s]*(.*)$/i
6
+ COMMENT_REGEX = %r{//[/\s]*(.*)$}i
8
7
 
9
8
  def initialize(list)
10
- @list = list.reject { |group, domains| domains.compact.empty? }
9
+ @list = list.reject { |_group, domains| domains.compact.empty? }
11
10
  end
12
11
 
13
12
  def groups
@@ -15,7 +14,7 @@ class Gman
15
14
  end
16
15
 
17
16
  def domains
18
- list.values.flatten.sort.uniq
17
+ list.values.flatten.compact.sort.uniq
19
18
  end
20
19
 
21
20
  def count
@@ -23,18 +22,18 @@ class Gman
23
22
  end
24
23
 
25
24
  def alphabetize
26
- @list = @list.sort_by { |k,v| k.downcase }.to_h
27
- @list.each { |group, domains| domains.sort!.uniq! }
25
+ @list = @list.sort_by { |k, _v| k.downcase }.to_h
26
+ @list.each { |_group, domains| domains.sort!.uniq! }
28
27
  end
29
28
 
30
29
  def write
30
+ alphabetize
31
31
  File.write(Gman.list_path, to_public_suffix)
32
32
  end
33
33
 
34
34
  def to_public_suffix
35
- current_group = ""
36
- output = ""
37
- list.sort_by { |group, domains| group.downcase }.each do |group, domains|
35
+ current_group = output = ''
36
+ list.sort_by { |group, _domains| group.downcase }.each do |group, domains|
38
37
  if group != current_group
39
38
  output << "\n\n" unless current_group.empty? # first entry
40
39
  output << "// #{group}\n"
@@ -46,7 +45,7 @@ class Gman
46
45
  end
47
46
 
48
47
  def self.current
49
- current = File.open(Gman::list_path).read
48
+ current = File.open(Gman.list_path).read
50
49
  DomainList.from_public_suffix(current)
51
50
  end
52
51
 
@@ -56,23 +55,33 @@ class Gman
56
55
  DomainList.new(hash)
57
56
  end
58
57
 
59
- private
58
+ def parent_domain(domain)
59
+ domains.find { |c| domain =~ /\.#{Regexp.escape(c)}$/ }
60
+ end
61
+
62
+ class << self
63
+ private
60
64
 
61
- # Given an array of comments/domains in public suffix format
62
- # Converts to a hash in the form of :group => [domain1, domain2...]
63
- def self.array_to_hash(domains)
64
- group = ""
65
- domain_hash = {}
66
- domains.each do |line|
67
- next if line.empty?
68
- if match = COMMENT_REGEX.match(line)
69
- group = match[1]
70
- else
71
- domain_hash[group] = [] if domain_hash[group].nil?
72
- domain_hash[group].push line.downcase
65
+ # Given an array of comments/domains in public suffix format
66
+ # Converts to a hash in the form of :group => [domain1, domain2...]
67
+ def array_to_hash(domains)
68
+ domain_hash = {}
69
+ group = ''
70
+ domains.each do |line|
71
+ if line =~ COMMENT_REGEX
72
+ group = COMMENT_REGEX.match(line)[1]
73
+ else
74
+ safe_push(domain_hash, group, line.downcase)
75
+ end
73
76
  end
77
+ domain_hash
78
+ end
79
+
80
+ def safe_push(hash, key, value)
81
+ return if value.empty?
82
+ hash[key] ||= []
83
+ hash[key].push value
74
84
  end
75
- domain_hash
76
85
  end
77
86
  end
78
87
  end
@@ -1,21 +1,10 @@
1
1
  class Gman
2
-
3
2
  def type
4
- if state?
5
- :state
6
- elsif district?
7
- :district
8
- elsif cog?
9
- :cog
10
- elsif city?
11
- :city
12
- elsif federal?
13
- :federal
14
- elsif county?
15
- :county
16
- elsif list_category.nil?
17
- nil
18
- elsif list_category.include?("usagov")
3
+ [:state, :district, :cog, :city, :federal, :county].each do |type|
4
+ return type if send "#{type}?"
5
+ end
6
+ return if list_category.nil?
7
+ if list_category.include?('usagov')
19
8
  :unknown
20
9
  else
21
10
  list_category.to_sym
@@ -26,7 +15,7 @@ class Gman
26
15
  if matches
27
16
  matches[4].upcase
28
17
  elsif dotgov_listing
29
- dotgov_listing["State"]
18
+ dotgov_listing['State']
30
19
  elsif list_category
31
20
  matches = list_category.match(/usagov([A-Z]{2})/)
32
21
  matches[1] if matches
@@ -34,80 +23,87 @@ class Gman
34
23
  end
35
24
 
36
25
  def city
37
- dotgov_listing["City"] if dotgov_listing
26
+ dotgov_listing['City'] if dotgov_listing
38
27
  end
39
28
 
40
29
  def agency
41
- dotgov_listing["Agency"] if federal?
30
+ dotgov_listing['Agency'] if federal?
42
31
  end
43
32
 
44
33
  def dotgov?
45
- domain.tld == "gov"
34
+ domain.tld == 'gov'
46
35
  end
47
36
 
48
37
  def federal?
49
- dotgov_listing && dotgov_listing["Domain Type"] == "Federal Agency"
38
+ dotgov_listing && dotgov_listing['Domain Type'] == 'Federal Agency'
50
39
  end
51
40
 
52
41
  def city?
53
42
  if matches
54
- %w[ci town vil].include?(matches[3])
43
+ %w(ci town vil).include?(matches[3])
55
44
  elsif dotgov_listing
56
- dotgov_listing["Domain Type"] == "City"
45
+ dotgov_listing['Domain Type'] == 'City'
57
46
  end
58
47
  end
59
48
 
60
49
  def county?
61
50
  if matches
62
- matches[3] == "co"
51
+ matches[3] == 'co'
63
52
  elsif dotgov_listing
64
- dotgov_listing["Domain Type"] == "County"
53
+ dotgov_listing['Domain Type'] == 'County'
65
54
  end
66
55
  end
67
56
 
68
57
  def state?
69
58
  if matches
70
- matches[1] == "state"
59
+ matches[1] == 'state'
71
60
  elsif dotgov_listing
72
- dotgov_listing["Domain Type"] == "State/Local Govt"
61
+ dotgov_listing['Domain Type'] == 'State/Local Govt'
73
62
  end
74
63
  end
75
64
 
76
65
  def district?
77
- !!matches && matches[1] == "dst"
66
+ matches && matches[1] == 'dst'
78
67
  end
79
68
 
80
69
  def cog?
81
- !!matches && matches[1] == "cog"
70
+ matches && matches[1] == 'cog'
82
71
  end
83
72
 
84
73
  private
85
74
 
86
75
  def list_category
87
76
  @list_category ||= begin
88
- if match = Gman.list.find(domain.to_s)
89
- regex = Regexp.new "\/\/ ([^\\n]+)\\n?[^\/\/]*\\n#{Regexp.escape(match.name)}\\n", "im"
90
- matches = Gman.list_contents.match(regex)
91
- matches[1] if matches
92
- end
77
+ match = Gman.list.find(domain.to_s)
78
+ return unless match
79
+ regex = %r{// ([^\n]+)\n?[^/]*\n#{Regexp.escape(match.name)}\n}im
80
+ matches = Gman.list_contents.match(regex)
81
+ matches[1] if matches
93
82
  end
94
83
  end
95
84
 
96
85
  def matches
97
86
  return @matches if defined? @matches
98
- @matches = domain.to_s.match(LOCALITY_REGEX)
87
+ @matches = domain.to_s.match(Locality::REGEX)
99
88
  end
100
89
 
101
- def self.dotgov_list_path
102
- File.join Gman.config_path, "vendor/dotgovs.csv"
90
+ def dotgov_listing
91
+ return @dotgov_listing if defined? @dotgov_listing
92
+ return unless dotgov?
93
+ @dotgov_listing = Gman.dotgov_list.find do |listing|
94
+ listing['Domain Name'].casecmp("#{domain.sld}.gov") == 0
95
+ end
103
96
  end
104
97
 
105
- def self.dotgov_list
106
- @@dotgov_list ||= CSV.read(dotgov_list_path, :headers => true)
107
- end
98
+ class << self
99
+ def dotgov_list
100
+ @dotgov_list ||= CSV.read(dotgov_list_path, headers: true)
101
+ end
108
102
 
109
- def dotgov_listing
110
- return @dotgov_listing if defined? @dotgov_listing
111
- @dotgov_listing = Gman.dotgov_list.find { |d| d["Domain Name"].downcase == "#{domain.sld}.gov" } if dotgov?
103
+ private
104
+
105
+ def dotgov_list_path
106
+ File.join Gman.config_path, 'vendor/dotgovs.csv'
107
+ end
112
108
  end
113
109
  end
@@ -1,4 +1,4 @@
1
- # Utility functions for parsing and manipulating public-suffix formatted domain lists
1
+ # Utility functions for parsing and manipulating public-suffix domain lists
2
2
  # Only used in development and not loaded by default
3
3
  require 'yaml'
4
4
  require 'open-uri'
@@ -9,11 +9,10 @@ require_relative './domain_list'
9
9
 
10
10
  class Gman
11
11
  class Importer
12
-
13
12
  attr_accessor :domains
14
13
 
15
14
  # Known false positives from vendored lists
16
- BLACKLIST = %w[
15
+ BLACKLIST = %w(
17
16
  business.centurytel.net
18
17
  chesnee.net
19
18
  citlink.net
@@ -39,7 +38,24 @@ class Gman
39
38
  wctc.net
40
39
  webconnections.net
41
40
  webpages.charter.net
42
- ]
41
+ ).freeze
42
+
43
+ REGEX_CHECKS = {
44
+ 'home. regex' => /^home\./,
45
+ 'user. regex' => /^users?\./,
46
+ 'sites. regex' => /^sites?\./,
47
+ 'weebly' => /weebly\.com$/,
48
+ 'wordpress' => /wordpress\.com$/,
49
+ 'govoffice' => /govoffice\d?\.com$/,
50
+ 'homestead' => /homestead\.com$/,
51
+ 'wix.com' => /wix\.com$/,
52
+ 'blogspot.com' => /blogspot\.com$/,
53
+ 'tripod.com' => /tripod\.com$/,
54
+ 'squarespace.com' => /squarespace\.com$/,
55
+ 'github.io' => /github\.io$/,
56
+ 'tumblr' => /tumblr\.com$/,
57
+ 'locality' => Gman::Locality::REGEX
58
+ }.freeze
43
59
 
44
60
  def initialize(domains)
45
61
  @domains = DomainList.new(domains)
@@ -50,40 +66,21 @@ class Gman
50
66
  end
51
67
 
52
68
  def normalize_domain(domain)
53
- domain.to_s.downcase.strip.gsub(/^www./, "").gsub(/\/$/, "")
69
+ domain = Gman.new(domain).to_s
70
+ domain.to_s.downcase.strip.gsub(/^www./, '').gsub(%r{/$}, '')
54
71
  end
55
72
 
56
- def valid_domain?(domain, options={})
57
- return false if domain.empty?
58
- return reject(domain, "home. regex") if domain =~ /^home\./
59
- return reject(domain, "user. regex") if domain =~ /^users?\./
60
- return reject(domain, "sites. regex") if domain =~ /^sites?\./
61
- return reject(domain, "weebly") if domain =~ /weebly\.com$/
62
- return reject(domain, "wordpress") if domain =~ /wordpress\.com$/
63
- return reject(domain, "govoffice") if domain =~ /govoffice\d?\.com$/
64
- return reject(domain, "homestead") if domain =~ /homestead\.com$/
65
- return reject(domain, "wix.com") if domain =~ /wix\.com$/
66
- return reject(domain, "blogspot.com") if domain =~ /blogspot\.com$/
67
- return reject(domain, "tripod.com") if domain =~ /tripod\.com$/
68
- return reject(domain, "squarespace.com") if domain =~ /squarespace\.com$/
69
- return reject(domain, "github.io") if domain =~ /github\.io$/
70
- return reject(domain, "locality") if domain =~ Gman::LOCALITY_REGEX
71
- return reject(domain, "blacklist") if BLACKLIST.include?(domain)
72
- return reject(domain, "duplicate") if !options[:skip_dupe] && current.domains.include?(domain)
73
- return reject(domain, "invalid") unless PublicSuffix.valid?(".#{domain}")
74
- return reject(domain, "academic") if Swot::is_academic?(domain)
75
-
76
- if !options[:skip_dupe] && subdomain = current.domains.find { |c| domain =~ /\.#{Regexp.escape(c)}$/}
77
- return reject(domain, "subdomain of #{subdomain}")
78
- end
79
-
80
- return reject(domain, "unresolvable") if !options[:skip_resolve] && !domain_resolves?(domain)
73
+ def valid_domain?(domain, options = {})
74
+ return false unless ensure_valid(domain)
75
+ return false if !options[:skip_dupe] && !ensure_not_dupe(domain)
76
+ return false if !options[:skip_resolve] && !ensure_resolves(domain)
81
77
  true
82
78
  end
83
79
 
84
- # if RECONCILING=true, return the reason, rather than a bool and silence log output
80
+ # if RECONCILING=true, return the reason,
81
+ # rather than a bool and silence log output
85
82
  def reject(domain, reason)
86
- return reason if ENV["RECONCILING"]
83
+ return reason if ENV['RECONCILING']
87
84
  logger.info "👎 `#{domain}`: #{reason}"
88
85
  false
89
86
  end
@@ -92,59 +89,112 @@ class Gman
92
89
  @current ||= DomainList.current
93
90
  end
94
91
 
95
- def import(options={})
92
+ def import(options)
96
93
  logger.info "Current: #{Gman::DomainList.current.count} domains"
97
94
  logger.info "Adding: #{domains.count} domains"
98
95
 
99
- domains.list.each do |group, domains|
100
- domains.map! { |domain| Gman.new(domain).to_s }
101
- domains.map! { |domain| normalize_domain(domain) }
102
- domains.select! { |domain| valid_domain?(domain, options) }
103
- end
104
-
105
- logger.info "Filtered to: #{domains.count} domains"
96
+ normalize_domains!
97
+ ensure_validity!(options)
106
98
 
107
99
  if domains.count == 0
108
- logger.info "Nothing to add. Aborting"
100
+ logger.info 'Nothing to add. Aborting'
109
101
  exit 0
110
102
  end
111
103
 
112
- domains.list.each do |group,domains|
113
- current.list[group] = [] if current.list[group].nil?
114
- current.list[group].concat domains
115
- current.list[group].sort! # Alphabetize
116
- current.list[group].uniq! # Ensure uniqueness
104
+ add_to_current
105
+ logger.info "New: #{current.count} domains"
106
+ end
107
+
108
+ def resolver
109
+ @resolver ||= Resolv::DNS.new(nameserver: ['8.8.8.8', '8.8.4.4'])
110
+ end
111
+
112
+ # Verifies that the given domain has an MX record, and thus is valid
113
+ def domain_resolves?(domain)
114
+ domain = Addressable::URI.new(host: domain).normalize.host
115
+ return true if ip?(domain)
116
+ returns_record?(domain, 'NS') || returns_record?(domain, 'MX')
117
+ end
118
+
119
+ private
120
+
121
+ def ensure_regex(domain)
122
+ REGEX_CHECKS.each do |msg, regex|
123
+ return reject(domain, msg) if domain =~ regex
117
124
  end
125
+ true
126
+ end
118
127
 
119
- logger.info "New: #{current.count} domains"
128
+ def ensure_valid(domain)
129
+ return false if domain.empty?
130
+ if BLACKLIST.include?(domain)
131
+ reject(domain, 'blacklist')
132
+ elsif !PublicSuffix.valid?(".#{domain}")
133
+ reject(domain, 'invalid')
134
+ elsif Swot.is_academic?(domain)
135
+ reject(domain, 'academic')
136
+ else
137
+ ensure_regex(domain)
138
+ end
139
+ end
120
140
 
121
- logger.info "Writing to disk..."
122
- current.write
123
- logger.info "Fin."
141
+ def ensure_resolves(domain)
142
+ return reject(domain, 'unresolvable') unless domain_resolves?(domain)
143
+ true
124
144
  end
125
145
 
126
- def resolver
127
- @resolver ||= Resolv::DNS.new(:nameserver => ["8.8.8.8","8.8.4.4"])
146
+ def ensure_not_dupe(domain)
147
+ return true unless dupe?(domain)
148
+ if current.domains.include?(domain)
149
+ reject(domain, 'duplicate')
150
+ else
151
+ parent = current.parent_domain(domain)
152
+ reject(domain, "subdomain of #{parent}")
153
+ end
154
+ end
155
+
156
+ def dupe?(domain)
157
+ current.domains.include?(domain) || current.parent_domain(domain)
158
+ end
159
+
160
+ def normalize_domains!
161
+ domains.list.each do |_group, domains|
162
+ domains.map! { |domain| normalize_domain(domain) }
163
+ domains.uniq!
164
+ end
165
+ end
166
+
167
+ def ensure_validity!(options = {})
168
+ domains.list.each do |_group, domains|
169
+ domains.select! { |domain| valid_domain?(domain, options) }
170
+ end
171
+ end
172
+
173
+ def add_to_current
174
+ domains.list.each do |group, domains|
175
+ current.list[group] ||= []
176
+ current.list[group].concat domains
177
+ end
178
+ current.write
128
179
  end
129
180
 
130
- def resolve_without_errors
131
- yield
181
+ def ip?(domain)
182
+ resolver.getaddress(domain)
132
183
  rescue Resolv::ResolvError
133
184
  false
134
185
  end
135
186
 
136
- # Verifies that the given domain has an MX record, and thus is valid
137
- def domain_resolves?(domain)
138
- domain = Addressable::URI.new(:host => domain).normalize.host
139
- resolve_without_errors { resolver.getaddress(domain) } ||
140
- resolve_without_errors { resolver.getresource(domain, Resolv::DNS::Resource::IN::NS) } ||
141
- resolve_without_errors { resolver.getresource(domain, Resolv::DNS::Resource::IN::MX) }
187
+ def returns_record?(domain, type)
188
+ type = Object.const_get "Resolv::DNS::Resource::IN::#{type}"
189
+ resolver.getresource(domain, type)
190
+ rescue Resolv::ResolvError
191
+ false
142
192
  end
143
193
  end
144
194
  end
145
195
 
146
196
  class Gman
147
- def self.import(hash, options={})
197
+ def self.import(hash, options = {})
148
198
  Gman::Importer.new(hash).import(options)
149
199
  end
150
200
  end