whois-parser-next 0.4.3 → 0.4.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c58e47a473ff8911ebe65e7a94e538d557585d23196f8116cbeb9720a45f4421
4
- data.tar.gz: b2e930eecc35340b1812c7a6e351d87e22adf67f1a9a1efaa48c53d736452382
3
+ metadata.gz: 1006a5373a7bed9d074cee1bc20d02bd78d9bd7a682d740c78c5fa6cf44aaf1f
4
+ data.tar.gz: 99899f0e0a3db2b665119c876d4c561b11deaca2de4172a89bbdecebbb9f4b0d
5
5
  SHA512:
6
- metadata.gz: eb08e46591a409452ee668eb3087b02221afaeec040bba662654e26f8b3b5ea2e02213efe68a7bad508d5cf9a74686534af72394bad0207b4c6ec784111eeeb9
7
- data.tar.gz: 632d2fe08d217051da515695dedec0f62f41bc145aad9cca96429adc7a822361c886a3b8414d99c517e71df6d56518a26528f96f72caf819d130cbcde1ffbdfb
6
+ metadata.gz: 0e32b486fb6c61548fe11307395704a4548ac68ac2187d7dd95f42dc5a9ecee9147466f83639ab9ea2637732f771f38ef2c1331a8cc3514cc386e8228ac80afc
7
+ data.tar.gz: 4939cb1e255090dc84e95a824df8db82cdd11eceda1625a1caf3a465f12c8dc835afdcb023dea059cc874bbf4ea938154d823128951601dedd1b7f3694a3b972
data/CHANGELOG.md CHANGED
@@ -2,6 +2,61 @@
2
2
 
3
3
  This project uses [Semantic Versioning 2.0.0](http://semver.org/).
4
4
 
5
+ #### Release 0.4.5 (2026-10-18)
6
+
7
+ - ADDED: Added current CoCCA response parsing for `.bf`, `.td`, and `.mr`,
8
+ requiring one valid domain identity and an exact `active` or `ok` EPP
9
+ registration state. `.bf` accepts `Domain Status: No Object Found`; `.td`,
10
+ `.mr`, and `.kn` require the exact absence line
11
+ `The queried object does not exist: No Object Found`. `.kn` also recognizes
12
+ its current CoCCA response while retaining the legacy format. Mixed or
13
+ incomplete replies remain unknown.
14
+ - ADDED: Added strict `.mc` parsing: registration requires one valid domain,
15
+ `Status: ACTIVE`, and a parseable `Created` value; absence requires the exact
16
+ `>> No data found for domain : <name>` line. Malformed or mixed replies
17
+ remain unknown.
18
+ - SAFETY: `.ws` recognizes the current `Domain Name` and `Creation Date` format
19
+ while preserving the legacy `Domain Created` format and exact absence
20
+ response. Invalid, inconsistent, or duplicate fields remain unknown.
21
+ - EVIDENCE: IANA lists the official hosts `whois.registre.bf`, `whois.nic.td`,
22
+ `whois.nic.mr`, `whois.nic.kn`, `whois.nic.mc`, and `whois.website.ws` for
23
+ [`.bf`](https://www.iana.org/domains/root/db/bf.html),
24
+ [`.td`](https://www.iana.org/domains/root/db/td.html),
25
+ [`.mr`](https://www.iana.org/domains/root/db/mr.html),
26
+ [`.kn`](https://www.iana.org/domains/root/db/kn.html),
27
+ [`.mc`](https://www.iana.org/domains/root/db/mc.html), and
28
+ [`.ws`](https://www.iana.org/domains/root/db/ws.html). Production-host queries
29
+ on 2026-10-18 observed registered and absent pairs for `.bf`, `.td`, `.mr`,
30
+ `.kn`, and `.mc`. Local-network queries observed both `.ws` states; the
31
+ production-proxy query confirmed a registered response only, so a production
32
+ registered/absent pair remains unverified. Committed examples are minimal
33
+ synthetic technical-shape controls without contact details.
34
+ - TESTED: The full suite passes 7,074 examples with 0 failures on Ruby 3.2.2
35
+ and 3.4.5. Final package smoke and supported-Ruby CI remain release gates.
36
+
37
+ #### Release 0.4.4 (2026-10-18)
38
+
39
+ - ADDED: Added parsers for the current `.gh` and `.do` CoCCA WHOIS response
40
+ shapes, including exact EPP registration states and `No Object Found`
41
+ absence responses. Added `.vu` handling for registered records and the exact
42
+ GoDaddy `No Data Found` response. Domain identity checks remain strict, and
43
+ mixed or ambiguous replies remain unknown.
44
+ - FIXED: Scope JWhois `NO OBJECT FOUND!` blocks to the named object. Missing
45
+ contact objects no longer make a domain with registered record data appear
46
+ available; absence requires one exact domain-object response. Malformed or
47
+ mixed domain absence remains unknown. Added two regression examples.
48
+ - EVIDENCE: IANA lists `whois.nic.gh`, `whois.nic.do`, and `whois.dnrs.vu`
49
+ for [`.gh`](https://www.iana.org/domains/root/db/gh.html),
50
+ [`.do`](https://www.iana.org/domains/root/db/do.html), and
51
+ [`.vu`](https://www.iana.org/domains/root/db/vu.html). IANA lists
52
+ `whois.mediaserv.net` for [`.mq`](https://www.iana.org/domains/root/db/mq.html).
53
+ The `.gh`, `.do`, and `.vu` fixtures are minimal synthetic shape controls;
54
+ the `.mq` regression uses an inline synthetic response. Live response
55
+ originals and personal contact details are not included.
56
+ - TESTED: The full suite passes 7,054 examples with 0 failures on Ruby 3.2.2
57
+ and 3.4.5. Package smoke passes on both versions. The supported-Ruby CI
58
+ matrix is the release gate.
59
+
5
60
  #### Release 0.4.3 (2026-10-18)
6
61
 
7
62
  - ADDED: Add the official `.ls` WHOIS parser for the registered `domain`,
@@ -1,6 +1,6 @@
1
1
  module Whois
2
2
  class Parser
3
3
  # The current library version.
4
- VERSION = "0.4.3".freeze
4
+ VERSION = "0.4.5".freeze
5
5
  end
6
6
  end
@@ -14,17 +14,25 @@ module Whois
14
14
  end
15
15
 
16
16
  property_supported :status do
17
- if available?
18
- :available
19
- elsif registered_evidence?
20
- :registered
17
+ text = content_for_scanner
18
+ domains = text.scan(/^domain:[ \t]*(\S+)[ \t]*\r?$/i).flatten
19
+ missing = text.scan(/^NO OBJECT FOUND![ \t]*\r?\nobject:[ \t]*(\S+)[ \t]*\r?\ntype:[ \t]*([^\r\n]+)$/i)
20
+ markers = text.scan(/^NO OBJECT FOUND![ \t]*\r?$/i).length
21
+ return :unknown unless markers == missing.length
22
+
23
+ if domains.empty?
24
+ missing.one? && missing.first.last.strip.casecmp('domain').zero? &&
25
+ missing.first.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:gf|mq)\z/i) ? :available : :unknown
26
+ elsif domains.one? && domains.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:gf|mq)\z/i) && registered_evidence?
27
+ # Referenced person objects may be absent while the domain exists.
28
+ missing.all? { |name, type| type.strip.casecmp('person').zero? && !name.casecmp(domains.first).zero? } ? :registered : :unknown
21
29
  else
22
30
  :unknown
23
31
  end
24
32
  end
25
33
 
26
34
  property_supported :available? do
27
- content_for_scanner.match?(/^NO OBJECT FOUND!\s*$/i)
35
+ status == :available
28
36
  end
29
37
 
30
38
  property_supported :registered? do
@@ -0,0 +1,26 @@
1
+ require_relative 'whois.biz'
2
+ require_relative 'registry_response_safety'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisDnrsVu < WhoisBiz
7
+ include RegistryResponseSafety
8
+
9
+ property_supported :status do
10
+ text = content_for_scanner
11
+ absence = text.scan(/^No Data Found[ \t]*\r?$/i)
12
+ record = text.match?(/^(?:Domain Name|Registry Domain ID|Creation Date|Registry Expiry Date|Registrar|Domain Status|Name Server):[ \t]*\S/i)
13
+ if absence.any?
14
+ absence.one? && !record ? :available : :unknown
15
+ else
16
+ names = text.scan(/^Domain Name:[ \t]*([^\r\n]+)$/i).flatten.map(&:strip)
17
+ details = text.match?(/^(?:Registry Domain ID|Creation Date|Registry Expiry Date|Registrar):[ \t]*\S/i)
18
+ names.one? && names.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+vu\z/i) && details ? :registered : :unknown
19
+ end
20
+ end
21
+
22
+ property_supported(:available?) { status == :available }
23
+ property_supported(:registered?) { status == :registered }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base_cocca2'
2
+ require_relative 'cocca_epp_status'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisNicDo < BaseCocca2
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "do".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || :unknown }
11
+ property_supported(:available?) { status == :available }
12
+ property_supported(:registered?) { status == :registered }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base_cocca2'
2
+ require_relative 'cocca_epp_status'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisNicGh < BaseCocca2
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "gh".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || :unknown }
11
+ property_supported(:available?) { status == :available }
12
+ property_supported(:registered?) { status == :registered }
13
+ end
14
+ end
15
+ end
@@ -1,8 +1,17 @@
1
1
  require_relative 'base_icann_compliant'
2
+ require_relative 'cocca_epp_status'
2
3
 
3
4
  module Whois
4
5
  class Parsers
5
6
  class WhoisNicKn < BaseIcannCompliant
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "kn".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || super() }
11
+ property_supported :available? do
12
+ current = current_cocca_status
13
+ current.nil? ? super() : current == :available
14
+ end
6
15
  end
7
16
  end
8
17
  end
@@ -0,0 +1,46 @@
1
+ require_relative 'base'
2
+ require_relative 'registry_response_safety'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisNicMc < Base
7
+ include RegistryResponseSafety
8
+
9
+ property_supported(:domain) { content_for_scanner[/^Domain[ \t]*:[ \t]*(\S+)[ \t]*\r?$/i, 1]&.downcase }
10
+
11
+ property_supported :status do
12
+ text = content_for_scanner
13
+ names = text.scan(/^Domain[ \t]*:[ \t]*(\S+)[ \t]*\r?$/i).flatten
14
+ absent = text.scan(/^>> No data found for domain : (\S+)[ \t]*\r?$/i).flatten
15
+ dates = text.scan(/^Created[ \t]*:[ \t]*([^\r\n]+)$/i).flatten
16
+ statuses = text.scan(/^Status[ \t]*:[ \t]*([^\r\n]+)$/i).flatten.map(&:strip)
17
+ if absent.any?
18
+ absent.one? && valid_mc_domain?(absent.first) && names.empty? && dates.empty? && statuses.empty? ? :available : :unknown
19
+ else
20
+ valid_date = dates.one? && begin
21
+ !!parse_time(dates.first.strip)
22
+ rescue ArgumentError
23
+ false
24
+ end
25
+ names.one? && valid_mc_domain?(names.first) && statuses == ['ACTIVE'] && valid_date ? :registered : :unknown
26
+ end
27
+ end
28
+ property_supported(:registered?) { status == :registered }
29
+ property_supported(:available?) { status == :available }
30
+ property_supported :created_on do
31
+ value = content_for_scanner[/^Created[ \t]*:[ \t]*([^\r\n]+)$/i, 1]
32
+ parse_time(value.strip) if value
33
+ end
34
+ property_supported :expires_on do
35
+ value = content_for_scanner[/^Expires on[ \t]*:[ \t]*([^\r\n]+)$/i, 1]
36
+ parse_time(value.strip) if value
37
+ end
38
+
39
+ private
40
+
41
+ def valid_mc_domain?(name)
42
+ name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+mc\z/i)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base_cocca2'
2
+ require_relative 'cocca_epp_status'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisNicMr < BaseCocca2
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "mr".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || :unknown }
11
+ property_supported(:available?) { status == :available }
12
+ property_supported(:registered?) { status == :registered }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base_cocca2'
2
+ require_relative 'cocca_epp_status'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisNicTd < BaseCocca2
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "td".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || :unknown }
11
+ property_supported(:available?) { status == :available }
12
+ property_supported(:registered?) { status == :registered }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'base_cocca2'
2
+ require_relative 'cocca_epp_status'
3
+
4
+ module Whois
5
+ class Parsers
6
+ class WhoisRegistreBf < BaseCocca2
7
+ include CoccaEppStatus
8
+ CURRENT_COCCA_SUFFIX = "bf".freeze
9
+
10
+ property_supported(:status) { current_cocca_status || :unknown }
11
+ property_supported(:available?) { status == :available }
12
+ property_supported(:registered?) { status == :registered }
13
+ end
14
+ end
15
+ end
@@ -26,19 +26,25 @@ module Whois
26
26
  class WhoisWebsiteWs < Base
27
27
  include RegistryResponseSafety
28
28
 
29
+ property_supported :domain do
30
+ content_for_scanner[/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*\r?$/i, 1]&.downcase
31
+ end
32
+
29
33
  property_supported :status do
30
- if available?
31
- :available
32
- elsif content_for_scanner.match?(/^\s+Domain Created:\s+/i)
33
- :registered
34
+ body = content_for_scanner
35
+ names = body.scan(/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*\r?$/i).flatten
36
+ missing = body.scan(/^(?:No match for "([^"\r\n]+)"\.|The queried object does not exist: ([^\r\n]+)\.)[ \t]*\r?$/i).map { |parts| parts.compact.first }
37
+ dates = body.scan(/^[ \t]*(?:Creation Date|Domain Created):[ \t]*([^\r\n]+)$/i).flatten
38
+ if missing.any?
39
+ missing.one? && names.empty? && dates.empty? && valid_ws_domain?(missing.first) ? :available : :unknown
34
40
  else
35
- :unknown
41
+ times = dates.map { |date| begin parse_time(date.strip); rescue ArgumentError; nil; end }
42
+ names.one? && valid_ws_domain?(names.first) && times.any? && times.none?(&:nil?) && times.uniq.one? ? :registered : :unknown
36
43
  end
37
44
  end
38
45
 
39
46
  property_supported :available? do
40
- !!(content_for_scanner =~ /^No match for "\S+"\.$/ ||
41
- content_for_scanner =~ /^The queried object does not exist: \S+\.$/)
47
+ status == :available
42
48
  end
43
49
 
44
50
  property_supported :registered? do
@@ -47,9 +53,8 @@ module Whois
47
53
 
48
54
 
49
55
  property_supported :created_on do
50
- if content_for_scanner =~ /\s+Domain Created:\s+(.*)\n/
51
- parse_time(::Regexp.last_match(1))
52
- end
56
+ value = content_for_scanner[/^[ \t]*(?:Creation Date|Domain Created):[ \t]*([^\r\n]+)$/i, 1]
57
+ parse_time(value.strip) if value
53
58
  end
54
59
 
55
60
  property_supported :updated_on do
@@ -73,6 +78,12 @@ module Whois
73
78
  end
74
79
  end
75
80
 
81
+ private
82
+
83
+ def valid_ws_domain?(name)
84
+ name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ws\z/i)
85
+ end
86
+
76
87
  end
77
88
 
78
89
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whois-parser-next
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Carletti
@@ -237,6 +237,7 @@ files:
237
237
  - lib/whois/parsers/whois.corporatedomains.com.rb
238
238
  - lib/whois/parsers/whois.denic.de.rb
239
239
  - lib/whois/parsers/whois.dk-hostmaster.dk.rb
240
+ - lib/whois/parsers/whois.dnrs.vu.rb
240
241
  - lib/whois/parsers/whois.dns.be.rb
241
242
  - lib/whois/parsers/whois.dns.hr.rb
242
243
  - lib/whois/parsers/whois.dns.lu.rb
@@ -399,6 +400,7 @@ files:
399
400
  - lib/whois/parsers/whois.nic.direct.rb
400
401
  - lib/whois/parsers/whois.nic.directory.rb
401
402
  - lib/whois/parsers/whois.nic.dm.rb
403
+ - lib/whois/parsers/whois.nic.do.rb
402
404
  - lib/whois/parsers/whois.nic.doctor.rb
403
405
  - lib/whois/parsers/whois.nic.dog.rb
404
406
  - lib/whois/parsers/whois.nic.domains.rb
@@ -442,6 +444,7 @@ files:
442
444
  - lib/whois/parsers/whois.nic.gd.rb
443
445
  - lib/whois/parsers/whois.nic.ge.rb
444
446
  - lib/whois/parsers/whois.nic.gent.rb
447
+ - lib/whois/parsers/whois.nic.gh.rb
445
448
  - lib/whois/parsers/whois.nic.gifts.rb
446
449
  - lib/whois/parsers/whois.nic.gives.rb
447
450
  - lib/whois/parsers/whois.nic.giving.rb
@@ -510,6 +513,7 @@ files:
510
513
  - lib/whois/parsers/whois.nic.management.rb
511
514
  - lib/whois/parsers/whois.nic.market.rb
512
515
  - lib/whois/parsers/whois.nic.mba.rb
516
+ - lib/whois/parsers/whois.nic.mc.rb
513
517
  - lib/whois/parsers/whois.nic.md.rb
514
518
  - lib/whois/parsers/whois.nic.me.rb
515
519
  - lib/whois/parsers/whois.nic.media.rb
@@ -525,6 +529,7 @@ files:
525
529
  - lib/whois/parsers/whois.nic.monster.rb
526
530
  - lib/whois/parsers/whois.nic.mortgage.rb
527
531
  - lib/whois/parsers/whois.nic.moscow.rb
532
+ - lib/whois/parsers/whois.nic.mr.rb
528
533
  - lib/whois/parsers/whois.nic.ms.rb
529
534
  - lib/whois/parsers/whois.nic.mu.rb
530
535
  - lib/whois/parsers/whois.nic.museum.rb
@@ -621,6 +626,7 @@ files:
621
626
  - lib/whois/parsers/whois.nic.taipei.rb
622
627
  - lib/whois/parsers/whois.nic.tatar.rb
623
628
  - lib/whois/parsers/whois.nic.tc.rb
629
+ - lib/whois/parsers/whois.nic.td.rb
624
630
  - lib/whois/parsers/whois.nic.team.rb
625
631
  - lib/whois/parsers/whois.nic.tech.rb
626
632
  - lib/whois/parsers/whois.nic.technology.rb
@@ -695,6 +701,7 @@ files:
695
701
  - lib/whois/parsers/whois.register.bg.rb
696
702
  - lib/whois/parsers/whois.register.com.rb
697
703
  - lib/whois/parsers/whois.register.si.rb
704
+ - lib/whois/parsers/whois.registre.bf.rb
698
705
  - lib/whois/parsers/whois.registre.ma.rb
699
706
  - lib/whois/parsers/whois.registro.br.rb
700
707
  - lib/whois/parsers/whois.registry.click.rb