epp-client 0.0.1
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/epp-client.gemspec +65 -0
- data/lib/epp-client.rb +14 -0
- data/lib/epp-client/client.rb +48 -0
- data/lib/epp-client/hello_request.rb +9 -0
- data/lib/epp-client/old_server.rb +25 -0
- data/lib/epp-client/request.rb +96 -0
- data/lib/epp-client/response.rb +63 -0
- data/lib/epp-client/response_error.rb +15 -0
- data/lib/epp-client/server.rb +207 -0
- data/test/helper.rb +10 -0
- data/test/test_epp-client.rb +7 -0
- metadata +125 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Geoff Garside
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= epp-client
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Geoff Garside. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "epp-client"
|
8
|
+
gem.summary = %Q{EPP (Extensible Provisioning Protocol) Client}
|
9
|
+
gem.description = %Q{Client for communicating with EPP services}
|
10
|
+
gem.email = "geoff@geoffgarside.co.uk"
|
11
|
+
gem.homepage = "http://github.com/geoffgarside/epp-client"
|
12
|
+
gem.authors = ["Geoff Garside"]
|
13
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
14
|
+
gem.add_development_dependency "yard", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
|
17
|
+
gem.add_dependency "libxml-ruby", ">= 0"
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
Rake::TestTask.new(:test) do |test|
|
26
|
+
test.libs << 'lib' << 'test'
|
27
|
+
test.pattern = 'test/**/test_*.rb'
|
28
|
+
test.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'rcov/rcovtask'
|
33
|
+
Rcov::RcovTask.new do |test|
|
34
|
+
test.libs << 'test'
|
35
|
+
test.pattern = 'test/**/test_*.rb'
|
36
|
+
test.verbose = true
|
37
|
+
end
|
38
|
+
rescue LoadError
|
39
|
+
task :rcov do
|
40
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
task :test => :check_dependencies
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
begin
|
49
|
+
require 'yard'
|
50
|
+
YARD::Rake::YardocTask.new
|
51
|
+
rescue LoadError
|
52
|
+
task :yardoc do
|
53
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
54
|
+
end
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/epp-client.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{epp-client}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Geoff Garside"]
|
12
|
+
s.date = %q{2010-12-20}
|
13
|
+
s.description = %q{Client for communicating with EPP services}
|
14
|
+
s.email = %q{geoff@geoffgarside.co.uk}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"epp-client.gemspec",
|
26
|
+
"lib/epp-client.rb",
|
27
|
+
"lib/epp-client/client.rb",
|
28
|
+
"lib/epp-client/hello_request.rb",
|
29
|
+
"lib/epp-client/old_server.rb",
|
30
|
+
"lib/epp-client/request.rb",
|
31
|
+
"lib/epp-client/response.rb",
|
32
|
+
"lib/epp-client/response_error.rb",
|
33
|
+
"lib/epp-client/server.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_epp-client.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/geoffgarside/epp-client}
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.7}
|
40
|
+
s.summary = %q{EPP (Extensible Provisioning Protocol) Client}
|
41
|
+
s.test_files = [
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/test_epp-client.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
53
|
+
s.add_runtime_dependency(%q<libxml-ruby>, [">= 0"])
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
56
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
57
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
58
|
+
end
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
61
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
62
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/epp-client.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'socket'
|
3
|
+
require 'xml'
|
4
|
+
|
5
|
+
# EPP Module
|
6
|
+
module EPP
|
7
|
+
autoload :Client, File.dirname(__FILE__) + '/epp-client/client.rb'
|
8
|
+
autoload :Server, File.dirname(__FILE__) + '/epp-client/server.rb'
|
9
|
+
autoload :OldServer, File.dirname(__FILE__) + '/epp-client/old_server.rb'
|
10
|
+
autoload :Request, File.dirname(__FILE__) + '/epp-client/request.rb'
|
11
|
+
autoload :Response, File.dirname(__FILE__) + '/epp-client/response.rb'
|
12
|
+
autoload :ResponseError, File.dirname(__FILE__) + '/epp-client/response_error.rb'
|
13
|
+
autoload :HelloRequest, File.dirname(__FILE__) + '/epp-client/hello_request.rb'
|
14
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module EPP
|
2
|
+
# Front facing EPP Client.
|
3
|
+
#
|
4
|
+
# Establishes a connection to an EPP server and allows for sending commands
|
5
|
+
# to that EPP server.
|
6
|
+
class Client
|
7
|
+
# Create new instance of EPP::Client.
|
8
|
+
#
|
9
|
+
# @param [String] tag EPP Tag
|
10
|
+
# @param [String] passwd EPP Tag password
|
11
|
+
# @param [String] host EPP Host address
|
12
|
+
# @param [Hash] options Options
|
13
|
+
# @option options [Boolean] :compatibility If compatibility mode should be used?
|
14
|
+
def initialize(tag, passwd, host, options = {})
|
15
|
+
@conn = if options.delete(:compatibility) == true
|
16
|
+
OldServer.new(tag, passwd, host, options)
|
17
|
+
else
|
18
|
+
Server.new(tag, passwd, host, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Send hello command
|
23
|
+
def hello
|
24
|
+
@conn.connection do
|
25
|
+
@conn.hello
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Calls an EPP command after connecting to the EPP Server and logging in.
|
30
|
+
#
|
31
|
+
# @overload method_missing(command, payload)
|
32
|
+
# @param [String, #to_s] command EPP Command to call
|
33
|
+
# @param [XML::Node, XML::Document, String] payload EPP XML Payload
|
34
|
+
# @overload method_missing(command)
|
35
|
+
# @param [String, #to_s] command EPP Command to call
|
36
|
+
# @yield [xml] block to construct payload
|
37
|
+
# @yieldparam [XML::Node] xml XML Node of the command
|
38
|
+
# for the payload to be added into
|
39
|
+
# @return [Response] EPP Response object
|
40
|
+
def method_missing(command, payload = nil, &block)
|
41
|
+
@conn.connection do
|
42
|
+
@conn.with_login do
|
43
|
+
@conn.request(command, payload, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module EPP
|
2
|
+
# Handles sending and receiving data to older EPP servers.
|
3
|
+
#
|
4
|
+
# These servers transmit the payload with CRLF at the end
|
5
|
+
# and recieve one byte at a time until EOF is reached.
|
6
|
+
class OldServer < Server
|
7
|
+
# Sends frame using old method
|
8
|
+
#
|
9
|
+
# @param [String] xml XML payload to send
|
10
|
+
def send_frame(xml)
|
11
|
+
@sock.write(xml + "\r\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Receives frame using old method
|
15
|
+
#
|
16
|
+
# @return [String] XML Payload response
|
17
|
+
def recv_frame
|
18
|
+
data = ''
|
19
|
+
until @sock.eof?
|
20
|
+
data << @sock.read(1)
|
21
|
+
end
|
22
|
+
data
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module EPP
|
2
|
+
# An EPP XML Request
|
3
|
+
class Request
|
4
|
+
# Create new instance of EPP::Request.
|
5
|
+
#
|
6
|
+
# @overload initialize(command, payload, transaction_id)
|
7
|
+
# @param [String, #to_s] command EPP Command to call
|
8
|
+
# @param [XML::Node, XML::Document, String] payload XML Payload to transmit
|
9
|
+
# @param [String] transaction_id EPP Transaction ID
|
10
|
+
# @overload initialize(command, transaction_id) {|xml| payload }
|
11
|
+
# @param [String, #to_s] command EPP Command to call
|
12
|
+
# @param [String] transaction_id EPP Transaction ID
|
13
|
+
# @yield [xml] block to construct payload
|
14
|
+
# @yieldparam [XML::Node] xml XML Node of the command
|
15
|
+
# for the payload to be added into
|
16
|
+
def initialize(command, *args, &block)
|
17
|
+
@command = XML::Node.new(command)
|
18
|
+
|
19
|
+
cmd = XML::Node.new('command')
|
20
|
+
cmd << @command
|
21
|
+
xml.root << cmd
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
tid, _ = args
|
25
|
+
case block.arity
|
26
|
+
when 1
|
27
|
+
block.call(@command)
|
28
|
+
else
|
29
|
+
@command << block.call
|
30
|
+
end
|
31
|
+
else
|
32
|
+
payload, tid = args
|
33
|
+
unless payload.nil?
|
34
|
+
@command << case payload.class
|
35
|
+
when XML::Node
|
36
|
+
payload
|
37
|
+
when XML::Document
|
38
|
+
xml.import(payload.root)
|
39
|
+
else
|
40
|
+
doc = XML::Parser.string(payload.to_s).parse
|
41
|
+
xml.import(doc.root)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
unless command == 'logout'
|
47
|
+
cmd << XML::Node.new('clTRID', tid || 'ABC-12345')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Name of the receivers command
|
52
|
+
# @return [String] command name
|
53
|
+
def command
|
54
|
+
@command.name
|
55
|
+
end
|
56
|
+
|
57
|
+
# Receiver in XML form
|
58
|
+
# @return [XML::Document] XML of the receiver
|
59
|
+
def to_xml
|
60
|
+
xml
|
61
|
+
end
|
62
|
+
|
63
|
+
# Convert the receiver to a string
|
64
|
+
#
|
65
|
+
# @param [Hash] opts Formatting options, passed to the XML::Document
|
66
|
+
def to_s(opts = {})
|
67
|
+
xml.to_s({:indent => false}.merge(opts))
|
68
|
+
end
|
69
|
+
|
70
|
+
# @see Object#inspect
|
71
|
+
def inspect
|
72
|
+
xml.inspect
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
# Request XML Payload
|
77
|
+
# @see prepare_request
|
78
|
+
def xml
|
79
|
+
@xml ||= prepare_request
|
80
|
+
end
|
81
|
+
|
82
|
+
# Prepares the base XML for the request
|
83
|
+
#
|
84
|
+
# @return [XML::Document]
|
85
|
+
def prepare_request
|
86
|
+
xml = XML::Document.new('1.0')
|
87
|
+
xml.root = XML::Node.new('epp')
|
88
|
+
xml.root.namespaces.namespace =
|
89
|
+
XML::Namespace.new(xml.root, nil, 'urn:ietf:params:xml:ns:epp-1.0')
|
90
|
+
XML::Namespace.new(xml.root, 'xsi', 'http://www.w3.org/2001/XMLSchema-instance')
|
91
|
+
xml.root['xsi:schemaLocation'] = "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd"
|
92
|
+
|
93
|
+
xml
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module EPP
|
2
|
+
# An EPP XML Response
|
3
|
+
class Response
|
4
|
+
# Creates an instance of an EPP::Response
|
5
|
+
#
|
6
|
+
# @param [String] xml_string XML Response
|
7
|
+
def initialize(xml_string)
|
8
|
+
@xml = XML::Parser.string(xml_string).parse
|
9
|
+
@xml.root.namespaces.default_prefix = 'e'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Indicates if the response is successful.
|
13
|
+
# @return [Boolean] if the response is successful
|
14
|
+
def success?
|
15
|
+
code == 1000
|
16
|
+
end
|
17
|
+
|
18
|
+
# Response code
|
19
|
+
# @return [Integer] response code
|
20
|
+
def code
|
21
|
+
@code ||= result['code'].to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
# Response message
|
25
|
+
# @return [String] response message
|
26
|
+
def message
|
27
|
+
@message ||= result.find('e:msg/text()').first.content.strip
|
28
|
+
end
|
29
|
+
|
30
|
+
# Response data
|
31
|
+
# @return [String] response data
|
32
|
+
def data
|
33
|
+
unless @data
|
34
|
+
list = @xml.find('/e:epp/e:response/e:resData/node()').reject{|n| n.empty?}
|
35
|
+
@data = list.size > 1 ? list : list[0]
|
36
|
+
end
|
37
|
+
@data
|
38
|
+
end
|
39
|
+
|
40
|
+
# Response Message Queue
|
41
|
+
# @return [XML::Node] message queue
|
42
|
+
def msgQ
|
43
|
+
@msgQ ||= @xml.find('/e:epp/e:response/e:msgQ').first
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see Object#inspect
|
47
|
+
def inspect
|
48
|
+
@xml.inspect
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a the formatted response XML
|
52
|
+
# @return [String] formatted XML response
|
53
|
+
def to_xml
|
54
|
+
@xml.to_s(:indent => true)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
# @return [XML::Node] Result node
|
59
|
+
def result
|
60
|
+
@result ||= @xml.find('/e:epp/e:response/e:result').first
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module EPP
|
2
|
+
# Error response
|
3
|
+
class ResponseError < RuntimeError
|
4
|
+
attr_accessor :code, :xml
|
5
|
+
# Create new ResponseError
|
6
|
+
def initialize(code, msg, xml)
|
7
|
+
super(msg)
|
8
|
+
@code, @xml = code, xml
|
9
|
+
end
|
10
|
+
# @return [String] Formatted Response error
|
11
|
+
def to_s
|
12
|
+
"#{message} (code #{code})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module EPP
|
2
|
+
# A server error
|
3
|
+
class ServerError < RuntimeError; end
|
4
|
+
|
5
|
+
# Handles sending and receiving data to EPP servers.
|
6
|
+
# Supports new style EPP servers which include length of payloads in transmission.
|
7
|
+
class Server
|
8
|
+
|
9
|
+
# Default Service URNs
|
10
|
+
DEFAULT_SERVICES = %w(urn:ietf:params:xml:ns:domain-1.0
|
11
|
+
urn:ietf:params:xml:ns:contact-1.0 urn:ietf:params:xml:ns:host-1.0)
|
12
|
+
|
13
|
+
# Default connection options
|
14
|
+
DEFAULTS = { :port => 700, :compatibility => false, :lang => 'en', :version => '1.0',
|
15
|
+
:extensions => [], :services => DEFAULT_SERVICES }
|
16
|
+
|
17
|
+
# Receive frame header length
|
18
|
+
# @private
|
19
|
+
HEADER_LEN = 4
|
20
|
+
|
21
|
+
# @param [String] tag EPP Tag
|
22
|
+
# @param [String] passwd EPP Tag password
|
23
|
+
# @param [String] host EPP Server address
|
24
|
+
# @param [Hash] options configuration options
|
25
|
+
# @option options [Integer] :port EPP Port number, default 700
|
26
|
+
# @option options [Boolean] :compatibility Compatibility mode, default false
|
27
|
+
# @option options [String] :lang EPP Language code, default 'en'
|
28
|
+
# @option options [String] :version EPP protocol version, default '1.0'
|
29
|
+
# @option options [Array<String>] :extensions EPP Extension URNs
|
30
|
+
# @option options [Array<String>] :services EPP Service URNs
|
31
|
+
def initialize(tag, passwd, host, options = {})
|
32
|
+
@tag, @passwd, @host = tag, passwd, host
|
33
|
+
@options = DEFAULTS.merge(options)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sends a Hello Request to the server
|
37
|
+
# @return [Boolean] True if greeting was returned
|
38
|
+
def hello
|
39
|
+
send_frame(HelloRequest.new.to_s)
|
40
|
+
return true if recv_frame =~ /<greeting>/
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
# Send request to server
|
45
|
+
#
|
46
|
+
# @overload request(command, payload)
|
47
|
+
# @param [String, #to_s] command EPP Command to call
|
48
|
+
# @param [XML::Node, XML::Document, String] payload EPP XML Payload
|
49
|
+
# @overload request(command)
|
50
|
+
# @param [String, #to_s] command EPP Command to call
|
51
|
+
# @yield [xml] block to construct payload
|
52
|
+
# @yieldparam [XML::Node] xml XML Node of the command
|
53
|
+
# for the payload to be added into
|
54
|
+
# @return [Response] EPP Response object
|
55
|
+
def request(command, payload = nil, &block)
|
56
|
+
req = if payload.nil? && block_given?
|
57
|
+
Request.new(command, next_tid, &block)
|
58
|
+
else
|
59
|
+
Request.new(command, payload, next_tid)
|
60
|
+
end
|
61
|
+
|
62
|
+
send_recv_frame(req.to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Runs a block while logged into the receiver
|
66
|
+
#
|
67
|
+
# @yield logged in EPP server session
|
68
|
+
# @example typical usage
|
69
|
+
# with_login do
|
70
|
+
# # .. do stuff with logged in session ..
|
71
|
+
# end
|
72
|
+
def with_login
|
73
|
+
login!
|
74
|
+
|
75
|
+
begin
|
76
|
+
yield
|
77
|
+
ensure
|
78
|
+
logout!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# EPP Server Connection
|
83
|
+
#
|
84
|
+
# @yield connected session
|
85
|
+
# @example typical usage
|
86
|
+
# connection do
|
87
|
+
# # .. do stuff with logged in session ..
|
88
|
+
# end
|
89
|
+
# @example usage with with_login
|
90
|
+
# connection do
|
91
|
+
# with_login do
|
92
|
+
# # .. do stuff with logged in session ..
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
def connection
|
96
|
+
@conn = TCPSocket.new(@host, @options[:port])
|
97
|
+
@sock = OpenSSL::SSL::SSLSocket.new(@conn)
|
98
|
+
@sock.sync_close
|
99
|
+
|
100
|
+
begin
|
101
|
+
@sock.connect
|
102
|
+
recv_frame # Perform initial recv
|
103
|
+
|
104
|
+
yield
|
105
|
+
ensure
|
106
|
+
@sock.close
|
107
|
+
@conn.close
|
108
|
+
conn = sock = nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
private
|
112
|
+
# @return [String] next transaction id
|
113
|
+
def next_tid
|
114
|
+
@tid ||= 0
|
115
|
+
@tid += 1
|
116
|
+
"%s-%06d" % [@tag, @tid]
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [Request] Login Request Payload
|
120
|
+
def login_request
|
121
|
+
Request.new('login', next_tid) do |login|
|
122
|
+
login << XML::Node.new('clID', @tag)
|
123
|
+
login << XML::Node.new('pw', @passwd)
|
124
|
+
|
125
|
+
options = XML::Node.new('options')
|
126
|
+
options << XML::Node.new('version', @options[:version])
|
127
|
+
options << XML::Node.new('lang', @options[:lang])
|
128
|
+
login << options
|
129
|
+
|
130
|
+
svcs = XML::Node.new('svcs')
|
131
|
+
@options[:services].each { |uri| svcs << XML::Node.new('objURI', uri) }
|
132
|
+
login << svcs
|
133
|
+
|
134
|
+
unless @options[:extensions].empty?
|
135
|
+
ext = XML::Node.new('svcExtension')
|
136
|
+
@options[:extensions].each do |uri|
|
137
|
+
ext << XML::Node.new('extURI', uri)
|
138
|
+
end
|
139
|
+
svcs << ext
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [Request] Logout Request Payload
|
145
|
+
def logout_request
|
146
|
+
Request.new('logout')
|
147
|
+
end
|
148
|
+
|
149
|
+
# Perform login
|
150
|
+
#
|
151
|
+
# @return [true] login successful
|
152
|
+
# @raise [ResponseError] login failed
|
153
|
+
# @see login_request
|
154
|
+
def login!
|
155
|
+
response = send_recv_frame(login_request.to_s)
|
156
|
+
|
157
|
+
return true if response.code == 1000
|
158
|
+
raise ResponseError.new(response.code, response.message, response.to_xml)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Perform logout
|
162
|
+
#
|
163
|
+
# @return [true] logout successful
|
164
|
+
# @raise [ResponseError] logout failed
|
165
|
+
# @see logout_request
|
166
|
+
def logout!
|
167
|
+
response = send_recv_frame(logout_request.to_s)
|
168
|
+
|
169
|
+
return true if response.code == 1500
|
170
|
+
raise ResponseError.new(response.code, response.message, response.to_xml)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Send a frame and receive its response
|
174
|
+
#
|
175
|
+
# @param [String] xml XML Payload to send
|
176
|
+
# @return [Response] EPP Response
|
177
|
+
# @see send_frame
|
178
|
+
# @see recv_frame
|
179
|
+
def send_recv_frame(xml)
|
180
|
+
send_frame(xml)
|
181
|
+
Response.new(recv_frame)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Send XML frame
|
185
|
+
# @return [Integer] number of bytes written
|
186
|
+
def send_frame(xml)
|
187
|
+
@sock.write([xml.size + 4].pack("N") + xml)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Receive XML frame
|
191
|
+
# @return [String] XML response
|
192
|
+
def recv_frame
|
193
|
+
header = @sock.read(HEADER_LEN)
|
194
|
+
|
195
|
+
if header.nil? && @sock.eof?
|
196
|
+
raise ServerError, "Connection terminated by remote host"
|
197
|
+
elsif header.nil?
|
198
|
+
raise ServerError, "Failed to read header from remote host"
|
199
|
+
else
|
200
|
+
len = header.unpack('N')[0]
|
201
|
+
|
202
|
+
raise ServerError, "Bad frame header from server, should be greater than #{HEADER_LEN}" unless len > HEADER_LEN
|
203
|
+
response = @sock.read(len - HEADER_LEN)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epp-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Geoff Garside
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-20 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: yard
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: libxml-ruby
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Client for communicating with EPP services
|
64
|
+
email: geoff@geoffgarside.co.uk
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files:
|
70
|
+
- LICENSE
|
71
|
+
- README.rdoc
|
72
|
+
files:
|
73
|
+
- .document
|
74
|
+
- LICENSE
|
75
|
+
- README.rdoc
|
76
|
+
- Rakefile
|
77
|
+
- VERSION
|
78
|
+
- epp-client.gemspec
|
79
|
+
- lib/epp-client.rb
|
80
|
+
- lib/epp-client/client.rb
|
81
|
+
- lib/epp-client/hello_request.rb
|
82
|
+
- lib/epp-client/old_server.rb
|
83
|
+
- lib/epp-client/request.rb
|
84
|
+
- lib/epp-client/response.rb
|
85
|
+
- lib/epp-client/response_error.rb
|
86
|
+
- lib/epp-client/server.rb
|
87
|
+
- test/helper.rb
|
88
|
+
- test/test_epp-client.rb
|
89
|
+
has_rdoc: true
|
90
|
+
homepage: http://github.com/geoffgarside/epp-client
|
91
|
+
licenses: []
|
92
|
+
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
requirements: []
|
117
|
+
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.3.7
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: EPP (Extensible Provisioning Protocol) Client
|
123
|
+
test_files:
|
124
|
+
- test/helper.rb
|
125
|
+
- test/test_epp-client.rb
|