whois 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/CHANGELOG.rdoc +61 -0
  2. data/Manifest +87 -4
  3. data/README.rdoc +13 -9
  4. data/Rakefile +4 -1
  5. data/bin/ruby-whois +35 -21
  6. data/lib/whois.rb +6 -0
  7. data/lib/whois/answer.rb +30 -14
  8. data/lib/whois/answer/parser.rb +48 -11
  9. data/lib/whois/answer/parser/ast.rb +72 -0
  10. data/lib/whois/answer/parser/base.rb +14 -9
  11. data/lib/whois/answer/parser/blank.rb +1 -1
  12. data/lib/whois/answer/parser/scanners/verisign.rb +117 -0
  13. data/lib/whois/answer/parser/whois.aero.rb +74 -0
  14. data/lib/whois/answer/parser/whois.afilias-grs.info.rb +72 -0
  15. data/lib/whois/answer/parser/whois.afilias.info.rb +72 -0
  16. data/lib/whois/answer/parser/whois.crsnic.net.rb +5 -115
  17. data/lib/whois/answer/parser/whois.denic.de.rb +28 -25
  18. data/lib/whois/answer/parser/whois.domainregistry.ie.rb +74 -0
  19. data/lib/whois/answer/parser/whois.educause.edu.rb +76 -0
  20. data/lib/whois/answer/parser/whois.eu.org.rb +73 -0
  21. data/lib/whois/answer/parser/whois.iana.org.rb +75 -0
  22. data/lib/whois/answer/parser/whois.nic.asia.rb +74 -0
  23. data/lib/whois/answer/parser/whois.nic.fr.rb +77 -0
  24. data/lib/whois/answer/parser/whois.nic.gov.rb +71 -0
  25. data/lib/whois/answer/parser/whois.nic.hu.rb +332 -0
  26. data/lib/whois/answer/parser/whois.nic.it.rb +5 -23
  27. data/lib/whois/answer/parser/whois.nic.name.rb +73 -0
  28. data/lib/whois/answer/parser/whois.nic.tv.rb +99 -0
  29. data/lib/whois/answer/parser/whois.publicinterestregistry.net.rb +6 -24
  30. data/lib/whois/answer/parser/whois.za.net.rb +75 -0
  31. data/lib/whois/answer/parser/whois.za.org.rb +75 -0
  32. data/lib/whois/client.rb +4 -3
  33. data/lib/whois/definitions/tld.notes.txt +1 -0
  34. data/lib/whois/definitions/tlds.rb +11 -49
  35. data/lib/whois/version.rb +5 -6
  36. data/lib/whois/whois.rb +0 -6
  37. data/test/answer/parser/base_test.rb +21 -0
  38. data/test/answer/parser/blank_test.rb +1 -1
  39. data/test/answer/parser/whois.aero_test.rb +51 -0
  40. data/test/answer/parser/whois.afilias-grs.info_test.rb +51 -0
  41. data/test/answer/parser/whois.afilias.info_test.rb +51 -0
  42. data/test/answer/parser/{whois_crsnic_net_test.rb → whois.crsnic.net_test.rb} +15 -68
  43. data/test/answer/parser/{whois_denic_de_test.rb → whois.denic.de_test.rb} +9 -34
  44. data/test/answer/parser/whois.domainregistry.ie_test.rb +51 -0
  45. data/test/answer/parser/whois.educause.edu_test.rb +51 -0
  46. data/test/answer/parser/whois.eu.org_test.rb +51 -0
  47. data/test/answer/parser/whois.iana.org_test.rb +51 -0
  48. data/test/answer/parser/whois.nic.asia_test.rb +51 -0
  49. data/test/answer/parser/whois.nic.fr_test.rb +51 -0
  50. data/test/answer/parser/whois.nic.gov_test.rb +51 -0
  51. data/test/answer/parser/whois.nic.hu_test.rb +226 -0
  52. data/test/answer/parser/{whois_nic_it_test.rb → whois.nic.it_test.rb} +3 -25
  53. data/test/answer/parser/whois.nic.name_test.rb +51 -0
  54. data/test/answer/parser/whois.nic.tv_test.rb +122 -0
  55. data/test/answer/parser/{whois_publicinterestregistry_net_test.rb → whois.publicinterestregistry.net_test.rb} +4 -26
  56. data/test/answer/parser/whois.za.net_test.rb +51 -0
  57. data/test/answer/parser/whois.za.org_test.rb +51 -0
  58. data/test/answer/parser_test.rb +44 -3
  59. data/test/answer_test.rb +27 -13
  60. data/test/client_test.rb +18 -11
  61. data/test/list_tld +319 -0
  62. data/test/test_helper.rb +24 -0
  63. data/test/testcases/responses/whois.aero/available.txt +1 -0
  64. data/test/testcases/responses/whois.aero/registered.txt +102 -0
  65. data/test/testcases/responses/whois.afilias-grs.info/available.txt +1 -0
  66. data/test/testcases/responses/whois.afilias-grs.info/bz/available.txt +1 -0
  67. data/test/testcases/responses/whois.afilias-grs.info/bz/registered.txt +122 -0
  68. data/test/testcases/responses/whois.afilias-grs.info/gi/available.txt +1 -0
  69. data/test/testcases/responses/whois.afilias-grs.info/gi/registered.txt +98 -0
  70. data/test/testcases/responses/whois.afilias-grs.info/hn/available.txt +1 -0
  71. data/test/testcases/responses/whois.afilias-grs.info/hn/registered.txt +85 -0
  72. data/test/testcases/responses/whois.afilias-grs.info/lc/available.txt +1 -0
  73. data/test/testcases/responses/whois.afilias-grs.info/lc/registered.txt +83 -0
  74. data/test/testcases/responses/whois.afilias-grs.info/mn/available.txt +1 -0
  75. data/test/testcases/responses/whois.afilias-grs.info/mn/registered.txt +100 -0
  76. data/test/testcases/responses/whois.afilias-grs.info/registered.txt +122 -0
  77. data/test/testcases/responses/whois.afilias-grs.info/sc/available.txt +1 -0
  78. data/test/testcases/responses/whois.afilias-grs.info/sc/registered.txt +85 -0
  79. data/test/testcases/responses/whois.afilias-grs.info/vc/available.txt +1 -0
  80. data/test/testcases/responses/whois.afilias-grs.info/vc/registered.txt +100 -0
  81. data/test/testcases/responses/whois.afilias.info/available.txt +1 -0
  82. data/test/testcases/responses/whois.afilias.info/registered.txt +104 -0
  83. data/test/testcases/responses/whois.crsnic.net/MULTIPLE.txt +3 -0
  84. data/test/testcases/responses/whois.domainregistry.ie/available.txt +7 -0
  85. data/test/testcases/responses/whois.domainregistry.ie/registered.txt +26 -0
  86. data/test/testcases/responses/whois.educause.edu/available.txt +28 -0
  87. data/test/testcases/responses/whois.educause.edu/registered.txt +66 -0
  88. data/test/testcases/responses/whois.eu.org/available.txt +1 -0
  89. data/test/testcases/responses/whois.eu.org/registered.txt +19 -0
  90. data/test/testcases/responses/whois.iana.org/MULTIPLE.txt +3 -0
  91. data/test/testcases/responses/whois.iana.org/available.txt +7 -0
  92. data/test/testcases/responses/whois.iana.org/registered.txt +89 -0
  93. data/test/testcases/responses/whois.nic.asia/available.txt +1 -0
  94. data/test/testcases/responses/whois.nic.asia/registered.txt +159 -0
  95. data/test/testcases/responses/whois.nic.fr/MULTIPLE.txt +4 -0
  96. data/test/testcases/responses/whois.nic.fr/available.txt +19 -0
  97. data/test/testcases/responses/whois.nic.fr/registered.txt +98 -0
  98. data/test/testcases/responses/whois.nic.gov/available.txt +1 -0
  99. data/test/testcases/responses/whois.nic.gov/registered.txt +5 -0
  100. data/test/testcases/responses/whois.nic.hu/available.txt +12 -0
  101. data/test/testcases/responses/whois.nic.hu/in_progress.txt +13 -0
  102. data/test/testcases/responses/whois.nic.hu/registered.txt +65 -0
  103. data/test/testcases/responses/whois.nic.name/available.txt +29 -0
  104. data/test/testcases/responses/whois.nic.name/registered.txt +36 -0
  105. data/test/testcases/responses/whois.nic.name/reserved.txt +30 -0
  106. data/test/testcases/responses/whois.nic.tv/available.txt +44 -0
  107. data/test/testcases/responses/whois.nic.tv/registered.txt +54 -0
  108. data/test/testcases/responses/whois.za.net/available.txt +9 -0
  109. data/test/testcases/responses/whois.za.net/registered.txt +42 -0
  110. data/test/testcases/responses/whois.za.org/available.txt +9 -0
  111. data/test/testcases/responses/whois.za.org/registered.txt +44 -0
  112. data/whois.gemspec +9 -6
  113. metadata +138 -15
@@ -22,7 +22,7 @@ module Whois
22
22
  #
23
23
  class Parser
24
24
 
25
- @@registrable_methods = [
25
+ @@properties = [
26
26
  :disclaimer,
27
27
  :domain, :domain_id,
28
28
  :referral_whois, :referral_url,
@@ -32,11 +32,11 @@ module Whois
32
32
  :nameservers,
33
33
  ]
34
34
 
35
- # Returns an array containing the name of all methods
35
+ # Returns an array containing the name of all properties
36
36
  # that can be registered and should be implemented by
37
37
  # server-specific parsers.
38
- def self.registrable_methods
39
- @@registrable_methods
38
+ def self.properties
39
+ @@properties
40
40
  end
41
41
 
42
42
  attr_reader :answer
@@ -56,15 +56,20 @@ module Whois
56
56
  # Returns <tt>true</tt> if the <tt>property</tt> passed as symbol
57
57
  # is supported by any available parser.
58
58
  # See also <tt>Whois::Answer::Parser::Base.supported?</tt>.
59
- def supported?(property)
60
- parsers.any? { |parser| parser.supported?(property) }
59
+ def property_supported?(property)
60
+ parsers.any? { |parser| parser.property_supported?(property) }
61
+ end
62
+
63
+ def supported?(*args)
64
+ ::Whois.deprecate "supported? is deprecated. Use property_supported? instead."
65
+ property_supported?(*args)
61
66
  end
62
67
 
63
68
 
64
69
  protected
65
70
 
66
71
  def method_missing(method, *args, &block)
67
- if Parser.registrable_methods.include?(method)
72
+ if Parser.properties.include?(method)
68
73
  if parsers.empty?
69
74
  raise ParserError, "Unable to select a parser because the answer is empty"
70
75
  elsif parser = select_parser(method)
@@ -101,28 +106,60 @@ module Whois
101
106
  end
102
107
 
103
108
 
109
+ # Returns the proper <tt>Whois::Answer::Parser::Base</tt> instance
110
+ # for given <tt>part</tt>.
111
+ # The parser class depends on the <tt>Whois::Answer::Part</tt> host.
104
112
  def self.parser_for(part)
105
113
  parser_klass(part.host).new(part)
106
114
  end
107
115
 
116
+ # Detects the proper parser class according to given <tt>host</tt>
117
+ # and returns the class constant.
118
+ # If no parser exists for <tt>host</tt>, then returns a generic
119
+ # <tt>Whois::Answer::Parser::Blank</tt>.
120
+ #
121
+ # This method autoloads missing parser classes. If you want to define
122
+ # a custom parser, simple make sure the class is loaded in the Ruby
123
+ # environment before this method is called.
124
+ #
125
+ # Parser.parser_klass("whois.missing.com")
126
+ # # => Whois::Answer::Parser::Blank
127
+ #
128
+ # class Whois::Answer::Parser::WhoisMissingCom
129
+ # end
130
+ # Parser.parser_klass("whois.missing.com")
131
+ # # => Whois::Answer::Parser::WhoisMissingCom
132
+ #
108
133
  def self.parser_klass(host)
109
- file = "whois/answer/parser/#{host}"
110
- require file
111
-
112
134
  name = host_to_parser(host)
135
+ Parser.const_defined?(name) || autoload(host)
113
136
  Parser.const_get(name)
114
137
 
115
138
  rescue LoadError
116
- require "whois/answer/parser/blank"
139
+ Parser.const_defined?("Blank") || autoload("blank")
117
140
  Parser::Blank
118
141
  end
119
142
 
143
+ # Converts <tt>host</tt> to a parser class name.
144
+ # Note. Returns a <tt>String</tt>, not a <tt>Class</tt>.
145
+ #
146
+ # Parser.host_to_parser("whois.nic.it")
147
+ # # => "WhoisNicIt"
148
+ # Parser.host_to_parser("whois.nic-info.it")
149
+ # # => "WhoisNicInfoIt"
150
+ #
120
151
  def self.host_to_parser(host)
121
152
  host.to_s.
122
153
  gsub(/\./, '_').
123
154
  gsub(/(?:^|_)(.)/) { $1.upcase }
124
155
  end
125
156
 
157
+ # Requires the file at "whois/answer/parser/#{name}".
158
+ def self.autoload(name)
159
+ file = "whois/answer/parser/#{name}"
160
+ require file
161
+ end
162
+
126
163
  end
127
164
 
128
165
  end
@@ -0,0 +1,72 @@
1
+ #
2
+ # = Ruby Whois
3
+ #
4
+ # An intelligent pure Ruby WHOIS client and parser.
5
+ #
6
+ #
7
+ # Category:: Net
8
+ # Package:: Whois
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # License:: MIT License
11
+ #
12
+ #--
13
+ #
14
+ #++
15
+
16
+
17
+ require 'strscan'
18
+
19
+
20
+ module Whois
21
+ class Answer
22
+ class Parser
23
+
24
+ # The Ast module tries to emulate a super-simple Abstract Syntax Tree structure
25
+ # including method for accessing ast nodes.
26
+ #
27
+ # == Usage
28
+ #
29
+ # Include the Ast module and provide a <tt>parse</tt> instance method.
30
+ # <tt>parse</tt> should returns a Hash representing the AST.
31
+ #
32
+ # def parse
33
+ # Scanner.new.parse
34
+ # end
35
+ # # => { "created_on" => "2009-12-12", ... }
36
+ #
37
+ # Now you can access the AST using the <tt>node</tt> method.
38
+ #
39
+ # node "created_on"
40
+ # # => "2009-12-12"
41
+ #
42
+ # node? "created_on"
43
+ # # => true
44
+ #
45
+ # node? "created_at"
46
+ # # => false
47
+ #
48
+ module Ast
49
+
50
+ def ast
51
+ @ast ||= parse
52
+ end
53
+
54
+ def node(key, &block)
55
+ if block_given?
56
+ value = ast[key]
57
+ value = yield(value) unless value.nil?
58
+ value
59
+ else
60
+ ast[key]
61
+ end
62
+ end
63
+
64
+ def node?(key)
65
+ !ast[key].nil?
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -14,10 +14,10 @@
14
14
  #++
15
15
 
16
16
 
17
- require 'strscan'
18
17
  require 'time'
19
18
  require 'whois/answer/contact'
20
19
  require 'whois/answer/registrar'
20
+ require 'whois/answer/parser/ast'
21
21
 
22
22
 
23
23
  module Whois
@@ -44,7 +44,7 @@ module Whois
44
44
  @part = part
45
45
  end
46
46
 
47
- ::Whois::Answer::Parser.registrable_methods.each do |method|
47
+ ::Whois::Answer::Parser.properties.each do |method|
48
48
  define_method(method) do
49
49
  raise PropertyNotImplemented, "You should overwrite this method."
50
50
  end
@@ -53,40 +53,45 @@ module Whois
53
53
  # Returns <tt>true</tt> if the <tt>property</tt> passed as symbol
54
54
  # is supported by current parser.
55
55
  #
56
- # parser.supported? :disclaimer
56
+ # parser.property_supported? :disclaimer
57
57
  # # => false
58
58
  #
59
59
  # parser.register_method(:disclaimer) {}
60
- # parser.supported? :disclaimer
60
+ # parser.property_supported? :disclaimer
61
61
  # # => true
62
62
  #
63
63
  # This method is different than <tt>respond_to?</tt>.
64
64
  # While <tt>respond_to?</tt> always returns true for any registrable property,
65
65
  # including those not effectively implemented,
66
66
  # this method only returns <tt>true</tt> if the parser implements <tt>property</tt>.
67
- # Also, <tt>supported?</tt> returns <tt>false</tt> if <tt>property</tt> exists as a method
67
+ # Also, <tt>property_supported?</tt> returns <tt>false</tt> if <tt>property</tt> exists as a method
68
68
  # but it's not a property method.
69
69
  #
70
70
  # parser.respond_to?(:disclaimer)
71
71
  # # => true
72
- # parser.supported?(:disclaimer)
72
+ # parser.property_supported?(:disclaimer)
73
73
  # # => false
74
74
  #
75
75
  # parser.register_method(:disclaimer) {}
76
76
  # parser.respond_to?(:disclaimer)
77
77
  # # => true
78
- # parser.supported?(:disclaimer)
78
+ # parser.property_supported?(:disclaimer)
79
79
  # # => true
80
80
  #
81
81
  # parser.respond_to?(:contact)
82
82
  # # => true
83
- # parser.supported?(:contact)
83
+ # parser.property_supported?(:contact)
84
84
  # # => false
85
85
  #
86
- def supported?(property)
86
+ def property_supported?(property)
87
87
  method_registered?(property.to_s.to_sym)
88
88
  end
89
89
 
90
+ def supported?(*args)
91
+ ::Whois.deprecate "supported? is deprecated. Use property_supported? instead."
92
+ property_supported?(*args)
93
+ end
94
+
90
95
 
91
96
  # This is an internal method primaly used as a common access point
92
97
  # to get the content to be parsed as a string.
@@ -32,7 +32,7 @@ module Whois
32
32
  #
33
33
  class Blank < Base
34
34
 
35
- ::Whois::Answer::Parser.registrable_methods.each do |method|
35
+ ::Whois::Answer::Parser.properties.each do |method|
36
36
  define_method(method) do
37
37
  raise ParserNotFound, "Unable to find a parser for the server `#{part.host}'"
38
38
  end
@@ -0,0 +1,117 @@
1
+ #
2
+ # = Ruby Whois
3
+ #
4
+ # An intelligent pure Ruby WHOIS client and parser.
5
+ #
6
+ #
7
+ # Category:: Net
8
+ # Package:: Whois
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # License:: MIT License
11
+ #
12
+ #--
13
+ #
14
+ #++
15
+
16
+
17
+ module Whois
18
+ class Answer
19
+ class Parser
20
+ module Scanners
21
+
22
+ class VerisignScanner
23
+
24
+ def initialize(content)
25
+ content = content.to_s.gsub("\r", "")
26
+ @input = StringScanner.new(content.to_s)
27
+ end
28
+
29
+ def parse
30
+ @ast = {}
31
+ while !@input.eos?
32
+ parse_content
33
+ end
34
+ @ast
35
+ end
36
+
37
+ private
38
+
39
+ def parse_content
40
+ trim_newline ||
41
+ parse_not_found ||
42
+ parse_disclaimer ||
43
+ parse_notice ||
44
+ parse_pair ||
45
+ trim_last_update ||
46
+ trim_fuffa ||
47
+ error("Unexpected token")
48
+ end
49
+
50
+ def trim_newline
51
+ @input.scan(/\n/)
52
+ end
53
+
54
+ def trim_last_update
55
+ @input.scan(/>>>(.*?)<<<\n/)
56
+ end
57
+
58
+ def trim_fuffa
59
+ @input.scan(/^\w(.*)\n/) ||
60
+ (@input.scan(/^\w(.*)/) and @input.eos?)
61
+ end
62
+
63
+ def parse_not_found
64
+ if @input.scan(/No match for "(.*?)"\.\n/)
65
+ @ast["Domain Name"] = @input[1].strip
66
+ end
67
+ end
68
+
69
+ # NOTE: parse_notice and parse_disclaimer are similar!
70
+ def parse_notice
71
+ if @input.match?(/NOTICE:/)
72
+ lines = []
73
+ while !@input.match?(/\n/) && @input.scan(/(.*)\n/)
74
+ lines << @input[1].strip
75
+ end
76
+ @ast["Notice"] = lines.join(" ")
77
+ else
78
+ false
79
+ end
80
+ end
81
+
82
+ def parse_disclaimer
83
+ if @input.match?(/TERMS OF USE:/)
84
+ lines = []
85
+ while !@input.match?(/\n/) && @input.scan(/(.*)\n/)
86
+ lines << @input[1].strip
87
+ end
88
+ @ast["Disclaimer"] = lines.join(" ")
89
+ else
90
+ false
91
+ end
92
+ end
93
+
94
+ def parse_pair
95
+ if @input.scan(/\s+(.*?):(.*?)\n/)
96
+ key, value = @input[1].strip, @input[2].strip
97
+ if @ast[key].nil?
98
+ @ast[key] = value
99
+ else
100
+ @ast[key].is_a?(Array) || @ast[key] = [@ast[key]]
101
+ @ast[key] << value
102
+ end
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ def error(message)
109
+ raise "#{message}: #{@input.peek(@input.string.length)}"
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # = Ruby Whois
3
+ #
4
+ # An intelligent pure Ruby WHOIS client and parser.
5
+ #
6
+ #
7
+ # Category:: Net
8
+ # Package:: Whois
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # License:: MIT License
11
+ #
12
+ #--
13
+ #
14
+ #++
15
+
16
+
17
+ require 'whois/answer/parser/base'
18
+
19
+
20
+ module Whois
21
+ class Answer
22
+ class Parser
23
+
24
+ #
25
+ # = whois.aero parser
26
+ #
27
+ # Parser for the whois.aero server.
28
+ #
29
+ # NOTE: This parser is just a stub and provides only a few basic methods
30
+ # to check for domain availability and get domain status.
31
+ # Please consider to contribute implementing missing methods.
32
+ # See WhoisNicIt parser for an explanation of all available methods
33
+ # and examples.
34
+ #
35
+ class WhoisAero < Base
36
+
37
+ register_method :status do
38
+ @status ||= if content.to_s =~ /Domain Status:(.*?)[\r\n]+/
39
+ $1.downcase.to_sym
40
+ end
41
+ end
42
+
43
+ register_method :available? do
44
+ @available ||= (content.to_s.strip == "NOT FOUND")
45
+ end
46
+
47
+ register_method :registered? do
48
+ !available?
49
+ end
50
+
51
+
52
+ register_method :created_on do
53
+ @created_on ||= if content.to_s =~ /Created On:(.*?)[\r\n]+/
54
+ Time.parse($1)
55
+ end
56
+ end
57
+
58
+ register_method :updated_on do
59
+ @updated_on ||= if content.to_s =~ /Updated On:(.*?)[\r\n]+/
60
+ Time.parse($1)
61
+ end
62
+ end
63
+
64
+ register_method :expires_on do
65
+ @expires_on ||= if content.to_s =~ /Expires On:(.*?)[\r\n]+/
66
+ Time.parse($1)
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
74
+ end