nominet-epp 0.0.2
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.
- data/.document +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/nominet-epp.rb +139 -0
- data/lib/nominet-epp/helpers.rb +83 -0
- data/lib/nominet-epp/operations.rb +20 -0
- data/lib/nominet-epp/operations/check.rb +39 -0
- data/lib/nominet-epp/operations/create.rb +166 -0
- data/lib/nominet-epp/operations/delete.rb +20 -0
- data/lib/nominet-epp/operations/fork.rb +53 -0
- data/lib/nominet-epp/operations/hello.rb +11 -0
- data/lib/nominet-epp/operations/info.rb +93 -0
- data/lib/nominet-epp/operations/list.rb +83 -0
- data/lib/nominet-epp/operations/lock.rb +56 -0
- data/lib/nominet-epp/operations/merge.rb +41 -0
- data/lib/nominet-epp/operations/poll.rb +38 -0
- data/lib/nominet-epp/operations/renew.rb +40 -0
- data/lib/nominet-epp/operations/transfer.rb +84 -0
- data/lib/nominet-epp/operations/unlock.rb +56 -0
- data/lib/nominet-epp/operations/unrenew.rb +36 -0
- data/lib/nominet-epp/operations/update.rb +67 -0
- data/nominet-epp.gemspec +74 -0
- data/test/helper.rb +10 -0
- data/test/test_nominet-epp.rb +7 -0
- metadata +135 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Delete Operation
|
4
|
+
module Delete
|
5
|
+
# Delete a domain from the registry
|
6
|
+
#
|
7
|
+
# @param [String] name Domain name
|
8
|
+
# @return [Boolean] success status
|
9
|
+
def delete(name)
|
10
|
+
resp = @client.delete do
|
11
|
+
domain('delete') do |node, ns|
|
12
|
+
node << XML::Node.new('name', name, ns)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
resp.success?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Fork Operation
|
4
|
+
module Fork
|
5
|
+
# Splits a selection of domains from one account into another.
|
6
|
+
#
|
7
|
+
# The returned hash contains the following keys
|
8
|
+
# - (String) +:roid+ -- New account ID
|
9
|
+
# - (String) +:name+ -- New account name
|
10
|
+
# - (String) +:crDate+ -- Date the account was created
|
11
|
+
# - (Hash) +:contact+ -- Contact details
|
12
|
+
#
|
13
|
+
# The +:contact+ hash contains the following keys
|
14
|
+
# - (String) +:roid+ -- Contact ID
|
15
|
+
# - (String) +:name+ -- Contact Name
|
16
|
+
# - (String) +:type+ -- Contact Type
|
17
|
+
# - (Integer) +:order+ -- Contact Order
|
18
|
+
#
|
19
|
+
# @param [String] account_num Account Number
|
20
|
+
# @param [String, ...] *names Domain names to fork from the account
|
21
|
+
# @return [false] fork failed
|
22
|
+
# @return [Hash] new account details
|
23
|
+
def fork(account_num, *names)
|
24
|
+
resp = @client.update do
|
25
|
+
account('fork') do |node, ns|
|
26
|
+
node << XML::Node.new('roid', account_num, ns)
|
27
|
+
names.each do |name|
|
28
|
+
node << XML::Node.new('domain-name', name, ns)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return false unless resp.success?
|
34
|
+
|
35
|
+
hash = {
|
36
|
+
:roid => node_value(resp.data, '//account:creData/account:roid'),
|
37
|
+
:name => node_value(resp.data, '//account:creData/account:name'),
|
38
|
+
:crDate => node_value(resp.data, '//account:creData/account:crDate'),
|
39
|
+
:contact => {
|
40
|
+
:roid => node_value(resp.data, '//account:creData/account:contact/contact:creData/contact:roid'),
|
41
|
+
:name => node_value(resp.data, '//account:creData/account:contact/contact:creData/contact:name')
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
contact = resp.data.find('//account:creData/account:contact', namespaces).first
|
46
|
+
hash[:contact][:type] = contact['type']
|
47
|
+
hash[:contact][:order] = contact['order']
|
48
|
+
|
49
|
+
hash
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Info Operation
|
4
|
+
module Info
|
5
|
+
# @param [Symbol] entity Type of entity to get information about
|
6
|
+
# @param [String] id Identifier of the entity
|
7
|
+
# @return [false] failed
|
8
|
+
# @return [Hash]
|
9
|
+
def info(entity, id)
|
10
|
+
raise ArgumentError, "entity must be :domain, :contact or :account" unless [:domain, :contact, :account].include?(entity)
|
11
|
+
|
12
|
+
resp = @client.info do
|
13
|
+
case entity
|
14
|
+
when :domain
|
15
|
+
domain('info') { |node, ns| node << XML::Node.new('name', id, ns) }
|
16
|
+
when :account
|
17
|
+
account('info') { |node, ns| node << XML::Node.new('roid', id, ns) }
|
18
|
+
when :contact
|
19
|
+
contact('info') { |node, ns| node << XML::Node.new('roid', id, ns) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
return false unless resp.success?
|
24
|
+
self.send(:"info_#{entity}", resp.data)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
# @param [XML::Node] data Domain data
|
29
|
+
# @return [Hash]
|
30
|
+
def info_domain(data)
|
31
|
+
hash = {}
|
32
|
+
data.find('//domain:infData', namespaces).first.children.reject{|n| n.empty?}.each do |node|
|
33
|
+
key = node.name.gsub('-', '_').to_sym
|
34
|
+
case node.name
|
35
|
+
when 'account'
|
36
|
+
hash[:account] = info_account(node)
|
37
|
+
when 'ns'
|
38
|
+
hash[:ns] = node.find('domain:host', namespaces).map do |hostnode|
|
39
|
+
{ :name => node_value(hostnode, 'domain:hostName'),
|
40
|
+
:v4 => node_value(hostnode, 'domain:hostAddr[@ip="v4"]'),
|
41
|
+
:v6 => node_value(hostnode, 'domain:hostAddr[@ip="v6"]') }.reject{|k,v| v.nil?}
|
42
|
+
end
|
43
|
+
when /date/i
|
44
|
+
hash[key] = Time.parse(node.content.strip)
|
45
|
+
else
|
46
|
+
hash[key] = node.content.strip
|
47
|
+
end
|
48
|
+
end
|
49
|
+
hash
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [XML::Node] data Account data
|
53
|
+
# @return [Hash]
|
54
|
+
def info_account(data)
|
55
|
+
hash = {}
|
56
|
+
data.find('//account:infData', namespaces).first.children.reject{|n| n.empty?}.each do |node|
|
57
|
+
key = node.name.gsub('-', '_').to_sym
|
58
|
+
case node.name
|
59
|
+
when 'addr'
|
60
|
+
hash[:addr] = {}
|
61
|
+
node.children.reject{|n| n.empty?}.each do |n|
|
62
|
+
hash[:addr][n.name.to_sym] = n.content.strip
|
63
|
+
end
|
64
|
+
when 'contact'
|
65
|
+
hash[:contacts] ||= Array.new
|
66
|
+
hash[:contacts] << info_contact(node)
|
67
|
+
hash[:contacts].last[:type] = node['type']
|
68
|
+
hash[:contacts].last[:order] = node['order']
|
69
|
+
when /date/i
|
70
|
+
hash[key] = Time.parse(node.content.strip)
|
71
|
+
else
|
72
|
+
hash[key] = node.content.strip
|
73
|
+
end
|
74
|
+
end
|
75
|
+
hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param [XML::Node] data Contact data
|
79
|
+
# @return [Hash]
|
80
|
+
def info_contact(data)
|
81
|
+
hash = {}
|
82
|
+
data.find('//contact:infData', namespaces).first.children.reject{|n| n.empty?}.each do |node|
|
83
|
+
if node.name =~ /date/i
|
84
|
+
hash[node.name.to_sym] = Time.parse(node.content.strip)
|
85
|
+
else
|
86
|
+
hash[node.name.to_sym] = node.content.strip
|
87
|
+
end
|
88
|
+
end
|
89
|
+
hash
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP List Operatons
|
4
|
+
module List
|
5
|
+
# Obtain a list of domains, and optionally their details, which either
|
6
|
+
# expire or were registered in the month given.
|
7
|
+
#
|
8
|
+
# @param [Symbol] type Listing type, either +:expiry+ or +:month+
|
9
|
+
# @param [String, #strftime] date Date of either expiry or registration to list
|
10
|
+
# @param [String] fields Verbosity of the response, either 'none' or 'all'
|
11
|
+
# @raise [ArgumentError] type must be +:expiry+ or +:month+
|
12
|
+
# @return [nil] list failed
|
13
|
+
# @return [Array<String>] list of domains
|
14
|
+
# @return [Array<Hash>] list of domains with details
|
15
|
+
def list(type, date, fields = 'none')
|
16
|
+
raise ArgumentError, "type must be :expiry or :month" unless [:expiry, :month].include?(type)
|
17
|
+
|
18
|
+
date = date.strftime("%Y-%m") if date.respond_to?(:strftime)
|
19
|
+
resp = @client.info do
|
20
|
+
domain('list') do |node, ns|
|
21
|
+
node << XML::Node.new(type, date, ns)
|
22
|
+
node << XML::Node.new('fields', fields, ns)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
return nil unless resp.success?
|
27
|
+
|
28
|
+
if fields == 'none'
|
29
|
+
resp.data.find('//domain:name', namespaces).map do |node|
|
30
|
+
node.content.strip
|
31
|
+
end
|
32
|
+
else
|
33
|
+
resp.data.find('//domain:infData', namespaces).map do |infData|
|
34
|
+
hash = {}
|
35
|
+
infData.children.reject{|n| n.empty?}.each do |node|
|
36
|
+
key = node.name.gsub('-', '_').to_sym
|
37
|
+
case node.name
|
38
|
+
when 'account'
|
39
|
+
hash[:account] = info_account(node)
|
40
|
+
when 'ns'
|
41
|
+
hash[:ns] = node.find('domain:host', namespaces).map do |hostnode|
|
42
|
+
{ :name => node_value(hostnode, 'domain:hostName'),
|
43
|
+
:v4 => node_value(hostnode, 'domain:hostAddr[@ip="v4"]'),
|
44
|
+
:v6 => node_value(hostnode, 'domain:hostAddr[@ip="v6"]') }.reject{|k,v| v.nil?}
|
45
|
+
end
|
46
|
+
when /date/i
|
47
|
+
hash[key] = Time.parse(node.content.strip)
|
48
|
+
else
|
49
|
+
hash[key] = node.content.strip
|
50
|
+
end
|
51
|
+
end
|
52
|
+
hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# list of all tags that accept tag changes along with their handshake settings
|
58
|
+
#
|
59
|
+
# The returned array of hashes contain the following keys
|
60
|
+
# - (String) +:registrar_tag+ -- TAG name
|
61
|
+
# - (String) +:name+ -- Name of the TAG owner
|
62
|
+
# - (String) +:trad_name+ -- TAG trading name
|
63
|
+
# - (BOOL) +:handshake+ -- Whether the TAG accepts handshakes
|
64
|
+
#
|
65
|
+
# @return [false] failure
|
66
|
+
# @return [Array<Hash>] tag details
|
67
|
+
def tag_list
|
68
|
+
resp = @client.info do
|
69
|
+
tag('list')
|
70
|
+
end
|
71
|
+
|
72
|
+
return false unless resp.success?
|
73
|
+
|
74
|
+
resp.data.find('//tag:infData', namespaces).map do |node|
|
75
|
+
{ :registrar_tag => node_value(node, 'tag:registrar-tag'),
|
76
|
+
:name => node_value(node, 'tag:name'),
|
77
|
+
:trad_name => node_value(node, 'tag:trad-name'),
|
78
|
+
:handshake => node_value(node, 'tag:handshake') == 'Y' }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Lock Operation
|
4
|
+
module Lock
|
5
|
+
# Lock domain or account 'investigation' or 'opt-out'
|
6
|
+
#
|
7
|
+
# @param [Symbol] entity Entity to lock
|
8
|
+
# @param [String] type Type of lock to set
|
9
|
+
# @param [String] id Domain name or account ID
|
10
|
+
# @raise [ArgumentError] entity is not +:domain+ or +:account+
|
11
|
+
# @raise [ArgumentError] type is not 'investigation' or 'opt-out'
|
12
|
+
# @return [Boolean] locking successful
|
13
|
+
def lock(entity, type, id)
|
14
|
+
raise ArgumentError, "entity must be :domain or :account" unless [:domain, :account].include?(entity)
|
15
|
+
raise ArgumentError, "type must be 'investigation' or 'opt-out'" unless %w(investigation opt-out).include?(type)
|
16
|
+
|
17
|
+
resp = @client.update do
|
18
|
+
case type
|
19
|
+
when 'investigation'
|
20
|
+
lock_investigation(entity, id)
|
21
|
+
when 'opt-out'
|
22
|
+
lock_opt_out(entity, id)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
return resp.success?
|
27
|
+
end
|
28
|
+
private
|
29
|
+
# Create +account:lock+ XML element for opt-out lock
|
30
|
+
# @param [Symbol] entity Entity to set opt out lock on
|
31
|
+
# @param [String] id Domain name or Account ID to lock
|
32
|
+
# @return [XML::Node] +account:lock+ element
|
33
|
+
def lock_opt_out(entity, id)
|
34
|
+
account('lock') do |node, ns|
|
35
|
+
node['type'] = 'opt-out'
|
36
|
+
node << XML::Node.new((entity == :domain ? 'domain-name' : 'roid'), id, ns)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create +lock+ XML element for investigation lock
|
41
|
+
#
|
42
|
+
# We don't support 'investigation' on account:domain-name as this ability
|
43
|
+
# is already provided via domain:name
|
44
|
+
#
|
45
|
+
# @param [Symbol] entity Entity to set investigation lock on
|
46
|
+
# @param [String] id Domain name or account ID to set lock on
|
47
|
+
# @return [XML::Node] +lock+ element
|
48
|
+
def lock_investigation(entity, id)
|
49
|
+
self.send(entity, 'lock') do |node, ns|
|
50
|
+
node['type'] = 'investigation'
|
51
|
+
node << XML::Node.new((entity == :domain ? 'name' : 'roid'), id, ns)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Merge Operation
|
4
|
+
module Merge
|
5
|
+
# Merge accounts or domains into an Account
|
6
|
+
#
|
7
|
+
# @param [String] target Account ID to merge into
|
8
|
+
# @param [Hash] sources Account IDs to merge into the target
|
9
|
+
# @option sources [Array] :accounts Account numbers to merge
|
10
|
+
# @option sources [Array] :names Account names to merge
|
11
|
+
# @option sources [Array] :domains Domain names to merge
|
12
|
+
# @return [false] merge failed
|
13
|
+
# @return [Response] merge succeded
|
14
|
+
def merge(target, sources = {})
|
15
|
+
resp = @client.update do
|
16
|
+
account('merge') do |node, ns|
|
17
|
+
node << XML::Node.new('roid', target, ns)
|
18
|
+
|
19
|
+
(sources[:accounts] || []).each do |acct|
|
20
|
+
n = XML::Node.new('roid', acct, ns)
|
21
|
+
n['source'] = 'y'
|
22
|
+
node << n
|
23
|
+
end
|
24
|
+
|
25
|
+
(sources[:names] || []).each do |name|
|
26
|
+
node << XML::Node.new('name', name, ns)
|
27
|
+
end
|
28
|
+
|
29
|
+
(sources[:domains] || []).each do |domain|
|
30
|
+
node << XML::Node.new('domain-name', domain, ns)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
return false unless resp.success?
|
36
|
+
|
37
|
+
resp # Need to test this to see what gets returned
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Poll Operation
|
4
|
+
module Poll
|
5
|
+
# Poll the EPP server for events
|
6
|
+
#
|
7
|
+
# @yield [data] process data if messages to poll
|
8
|
+
# @yieldparam [XML::Node] data Response data
|
9
|
+
# @return [nil] no messages to handle
|
10
|
+
# @return [Boolean] ack successful
|
11
|
+
# @see ack
|
12
|
+
def poll
|
13
|
+
resp = @client.poll do |poll|
|
14
|
+
poll['op'] = 'req'
|
15
|
+
end
|
16
|
+
|
17
|
+
return if resp.code != 1301 || resp.msgQ['count'] == '0'
|
18
|
+
|
19
|
+
yield resp.data
|
20
|
+
|
21
|
+
ack(resp.msgQ['id'])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Acknowledges a polled message ID
|
25
|
+
#
|
26
|
+
# @param [String] msgID Message ID to acknowledge
|
27
|
+
# @return [Boolean] ack successful
|
28
|
+
def ack(msgID)
|
29
|
+
resp = @client.poll do |poll|
|
30
|
+
poll['op'] = 'ack'
|
31
|
+
poll['msgID'] = msgID
|
32
|
+
end
|
33
|
+
|
34
|
+
return resp.success?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NominetEPP
|
2
|
+
module Operations
|
3
|
+
# EPP Renew Operation
|
4
|
+
module Renew
|
5
|
+
# Renew a domain name
|
6
|
+
#
|
7
|
+
# @param [String] name Domain name to renew
|
8
|
+
# @param [String] period Length of time to renew for. Currently has to be '2y'.
|
9
|
+
# @raise [ArgumentError] invalid period specified
|
10
|
+
# @raise [RuntimeError] renewed domain name does not match +name+
|
11
|
+
# @return [false] renewal failed
|
12
|
+
# @return [Time] domain expiration date
|
13
|
+
def renew(name, period = '2y')
|
14
|
+
period = '2y' # reset period to 2 years as nominet don't currently support other options
|
15
|
+
|
16
|
+
unit = period[-1..1]
|
17
|
+
num = period.to_i.to_s
|
18
|
+
|
19
|
+
raise ArgumentError, "period suffix must either be 'm' or 'y'" unless %w(m y).include?(unit)
|
20
|
+
|
21
|
+
resp = @client.renew do
|
22
|
+
domain('renew') do |node, ns|
|
23
|
+
node << XML::Node.new('name', name, ns)
|
24
|
+
p = XML::Node.new('period', num, ns);
|
25
|
+
p['unit'] = unit
|
26
|
+
node << p
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return false unless resp.success?
|
31
|
+
|
32
|
+
renName = node_value(resp.data, '//domain:renData/domain:name')
|
33
|
+
renExp = node_value(resp.data, '//domain:renData/domain:exDate')
|
34
|
+
|
35
|
+
raise "Renewed name #{renName} does not match #{name}" if renName != name
|
36
|
+
return Time.parse(renExp)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|