nominet-epp 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|