epp-nokogiri 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +9 -0
- data/README.rdoc +70 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/epp.gemspec +26 -0
- data/lib/epp/exceptions.rb +18 -0
- data/lib/epp/server.rb +207 -0
- data/lib/epp/version.rb +3 -0
- data/lib/epp.rb +14 -0
- data/lib/require_parameters.rb +14 -0
- data/test/test_epp.rb +244 -0
- data/test/test_helper.rb +12 -0
- data/test/xml/error.xml +14 -0
- data/test/xml/login_request.xml +1 -0
- data/test/xml/login_response.xml +12 -0
- data/test/xml/login_with_extensions_request.xml +1 -0
- data/test/xml/logout_request.xml +1 -0
- data/test/xml/logout_response.xml +12 -0
- data/test/xml/new_request.xml +2 -0
- data/test/xml/socket_preparation.xml +27 -0
- data/test/xml/test_request.xml +4 -0
- data/test/xml/test_response.xml +26 -0
- metadata +163 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Josh Delsman (Ultraspeed)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
= EPP v1.3.1 (by {Ultraspeed}[http://ultraspeed.co.uk])
|
2
|
+
|
3
|
+
The EPP gem provides basic functionality for connecting and making requests on EPP (Extensible Provisioning Protocol) servers. It has been fully tested against the RFC 5730 & 5734 specification.
|
4
|
+
|
5
|
+
Currently, major providers Centralnic and Nominet have been integrated using this gem. CoCCA is currently in testing.
|
6
|
+
|
7
|
+
* {Nominet Standard EPP Documentation}[http://www.nominet.org.uk/registrars/systems/standardepp/]
|
8
|
+
* {Centralnic Labs EPP Documentation}[http://labs.centralnic.com/epp/]
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
You can install this gem with:
|
13
|
+
|
14
|
+
$ gem install epp
|
15
|
+
|
16
|
+
Then, you can require it in your Ruby/Rails app:
|
17
|
+
|
18
|
+
require "epp"
|
19
|
+
|
20
|
+
If you're using Rails, configure your gem in the following file:
|
21
|
+
|
22
|
+
# Rails 2.x - config/environment.rb
|
23
|
+
config.gem "epp"
|
24
|
+
|
25
|
+
# Rails 3 - Gemfile
|
26
|
+
gem "epp"
|
27
|
+
|
28
|
+
== Example Usage
|
29
|
+
|
30
|
+
First, you must initialize an Epp::Server object to use. This requires the EPP server address, tag/username and password:
|
31
|
+
|
32
|
+
server = Epp::Server.new(
|
33
|
+
:server => "testbed-epp.nominet.org.uk",
|
34
|
+
:tag => "TESTING",
|
35
|
+
:password => "testing"
|
36
|
+
)
|
37
|
+
|
38
|
+
If no port is specified, it will be assumed that you will be using port 700.
|
39
|
+
|
40
|
+
You would then make an XML request to the server.
|
41
|
+
|
42
|
+
xml = "<?xml ... </epp>"
|
43
|
+
response = server.request(xml)
|
44
|
+
|
45
|
+
You can build this however you'd like, although Nokogiri is already present. The process is as follows:
|
46
|
+
|
47
|
+
* Connect to EPP server and receive the <greeting> frame
|
48
|
+
* Send a standard <login> request
|
49
|
+
* Send your request
|
50
|
+
* Send a standard <logout> request
|
51
|
+
* Disconnect the socket from the server
|
52
|
+
|
53
|
+
The EPP module would then return the XML response as a string. In this example, the response XML would be set equal to <tt>response</tt> for your usage.
|
54
|
+
|
55
|
+
Once the request is complete, it will automatically close the connection. For simplicity purposes, this plug-in will *not* use a persistent connection to the EPP server. This may change in future releases, as some registries require that persistent connections be supported. Please feel free to email me to collaborate on making this possible.
|
56
|
+
|
57
|
+
== Bugs/Issues
|
58
|
+
|
59
|
+
Please report all issues using the GitHub issue tracker at:
|
60
|
+
|
61
|
+
http://github.com/ultraspeed/epp/issues
|
62
|
+
|
63
|
+
== Credit
|
64
|
+
|
65
|
+
Author: Josh Delsman (http://twitter.com/voxxit)
|
66
|
+
Inspired from: http://labs.centralnic.com/Net_EPP_Client.php
|
67
|
+
|
68
|
+
== License
|
69
|
+
|
70
|
+
See the LICENSE file.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
Rake::TestTask.new(:test) do |test|
|
5
|
+
test.libs << 'lib' << 'test'
|
6
|
+
test.pattern = 'test/**/test_*.rb'
|
7
|
+
test.verbose = true
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'rcov/rcovtask'
|
12
|
+
|
13
|
+
Rcov::RcovTask.new do |test|
|
14
|
+
test.libs << 'test'
|
15
|
+
test.pattern = 'test/**/test_*.rb'
|
16
|
+
test.verbose = true
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
task :rcov do
|
20
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.4.0
|
data/epp.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "epp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "epp-nokogiri"
|
7
|
+
s.version = Epp::VERSION
|
8
|
+
s.authors = ["Josh Delsman", "Delwyn de Villiers", "Priit Haamer"]
|
9
|
+
s.email = ["jdelsman@ultraspeed.com", "delwyn.d@gmail.com", "priit@edicy.com"]
|
10
|
+
s.homepage = "http://github.com/delwyn/epp"
|
11
|
+
s.summary = %q{EPP (Extensible Provisioning Protocol) for Ruby}
|
12
|
+
s.description = %q{Basic functionality for connecting and making requests on EPP (Extensible Provisioning Protocol) servers}
|
13
|
+
|
14
|
+
s.rubyforge_project = "epp"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency("nokogiri", [">= 1.4.1"])
|
22
|
+
s.add_runtime_dependency("uuidtools", [">= 0"])
|
23
|
+
|
24
|
+
s.add_development_dependency("shoulda", [">= 0"])
|
25
|
+
s.add_development_dependency("mocha", [">= 0"])
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class EppErrorResponse < StandardError #:nodoc:
|
2
|
+
include RequiresParameters
|
3
|
+
|
4
|
+
attr_accessor :response_xml, :response_code, :message
|
5
|
+
|
6
|
+
# Generic EPP exception. Accepts a response code and a message
|
7
|
+
def initialize(attributes = {})
|
8
|
+
requires!(attributes, :xml, :code, :message)
|
9
|
+
|
10
|
+
@response_xml = attributes[:xml]
|
11
|
+
@response_code = attributes[:code]
|
12
|
+
@message = attributes[:message]
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{@message} (code #{@response_code})"
|
17
|
+
end
|
18
|
+
end
|
data/lib/epp/server.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
module Epp #:nodoc:
|
2
|
+
class Server
|
3
|
+
include RequiresParameters
|
4
|
+
|
5
|
+
attr_accessor :tag, :password, :server, :port, :lang, :services, :extensions, :version, :key, :cert
|
6
|
+
|
7
|
+
# ==== Required Attrbiutes
|
8
|
+
#
|
9
|
+
# * <tt>:server</tt> - The EPP server to connect to
|
10
|
+
# * <tt>:tag</tt> - The tag or username used with <tt><login></tt> requests.
|
11
|
+
# * <tt>:password</tt> - The password used with <tt><login></tt> requests.
|
12
|
+
#
|
13
|
+
# ==== Optional Attributes
|
14
|
+
#
|
15
|
+
# * <tt>:port</tt> - The EPP standard port is 700. However, you can choose a different port to use.
|
16
|
+
# * <tt>:lang</tt> - Set custom language attribute. Default is 'en'.
|
17
|
+
# * <tt>:services</tt> - Use custom EPP services in the <login> frame. The defaults use the EPP standard domain, contact and host 1.0 services.
|
18
|
+
# * <tt>:extensions</tt> - URLs to custom extensions to standard EPP. Use these to extend the standard EPP (e.g., Nominet uses extensions). Defaults to none.
|
19
|
+
# * <tt>:version</tt> - Set the EPP version. Defaults to "1.0".
|
20
|
+
# * <tt>:cert</tt> - SSL Certificate.
|
21
|
+
# * <tt>:key</tt> - SSL Key.
|
22
|
+
def initialize(attributes = {})
|
23
|
+
requires!(attributes, :tag, :password, :server)
|
24
|
+
|
25
|
+
@tag = attributes[:tag]
|
26
|
+
@password = attributes[:password]
|
27
|
+
@server = attributes[:server]
|
28
|
+
@port = attributes[:port] || 700
|
29
|
+
@lang = attributes[:lang] || "en"
|
30
|
+
@services = attributes[:services] || ["urn:ietf:params:xml:ns:domain-1.0", "urn:ietf:params:xml:ns:contact-1.0", "urn:ietf:params:xml:ns:host-1.0"]
|
31
|
+
@extensions = attributes[:extensions] || []
|
32
|
+
@version = attributes[:version] || "1.0"
|
33
|
+
@cert = attributes[:cert] || nil
|
34
|
+
@key = attributes[:key] || nil
|
35
|
+
|
36
|
+
@logged_in = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_epp_request(&block)
|
40
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
41
|
+
xml.epp(
|
42
|
+
'xmlns' => 'urn:ietf:params:xml:ns:epp-1.0',
|
43
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
44
|
+
'xsi:schemaLocation' => 'urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd'
|
45
|
+
) do
|
46
|
+
yield xml if block_given?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sends an XML request to the EPP server, and receives an XML response.
|
52
|
+
# <tt><login></tt> and <tt><logout></tt> requests are also wrapped
|
53
|
+
# around the request, so we can close the socket immediately after
|
54
|
+
# the request is made.
|
55
|
+
def request(xml)
|
56
|
+
open_connection
|
57
|
+
|
58
|
+
@logged_in = true if login
|
59
|
+
|
60
|
+
begin
|
61
|
+
@response = send_request(xml)
|
62
|
+
ensure
|
63
|
+
@logged_in = false if @logged_in && logout
|
64
|
+
|
65
|
+
close_connection
|
66
|
+
end
|
67
|
+
|
68
|
+
return @response
|
69
|
+
end
|
70
|
+
|
71
|
+
# Wrapper which sends an XML frame to the server, and receives
|
72
|
+
# the response frame in return.
|
73
|
+
def send_request(xml)
|
74
|
+
send_frame(xml)
|
75
|
+
get_frame
|
76
|
+
end
|
77
|
+
|
78
|
+
# Establishes the connection to the server. If the connection is
|
79
|
+
# established, then this method will call get_frame and return
|
80
|
+
# the EPP <tt><greeting></tt> frame which is sent by the
|
81
|
+
# server upon connection.
|
82
|
+
def open_connection
|
83
|
+
@connection = TCPSocket.new(server, port)
|
84
|
+
@context = OpenSSL::SSL::SSLContext.new
|
85
|
+
@context.cert = @cert
|
86
|
+
@context.key = @key
|
87
|
+
|
88
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@connection, @context) if @connection
|
89
|
+
|
90
|
+
@socket.sync_close = true
|
91
|
+
@socket.connect
|
92
|
+
|
93
|
+
get_frame
|
94
|
+
end
|
95
|
+
|
96
|
+
# Closes the connection to the EPP server.
|
97
|
+
def close_connection
|
98
|
+
@socket.close if @socket and not @socket.closed?
|
99
|
+
@connection.close if @connection and not @connection.closed?
|
100
|
+
|
101
|
+
@socket = @connection = nil
|
102
|
+
|
103
|
+
return true
|
104
|
+
end
|
105
|
+
|
106
|
+
# Receive an EPP frame from the server. Since the connection is blocking,
|
107
|
+
# this method will wait until the connection becomes available for use. If
|
108
|
+
# the connection is broken, a SocketError will be raised. Otherwise,
|
109
|
+
# it will return a string containing the XML from the server.
|
110
|
+
def get_frame
|
111
|
+
raise SocketError.new("Connection closed by remote server") if !@socket or @socket.eof?
|
112
|
+
|
113
|
+
header = @socket.read(4)
|
114
|
+
|
115
|
+
raise SocketError.new("Error reading frame from remote server") if header.nil?
|
116
|
+
|
117
|
+
length = header_size(header)
|
118
|
+
|
119
|
+
raise SocketError.new("Got bad frame header length of #{length} bytes from the server") if length < 5
|
120
|
+
|
121
|
+
return @socket.read(length - 4)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Send an XML frame to the server. Should return the total byte
|
125
|
+
# size of the frame sent to the server. If the socket returns EOF,
|
126
|
+
# the connection has closed and a SocketError is raised.
|
127
|
+
def send_frame(xml)
|
128
|
+
@socket.write(packed(xml) + xml)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Pack the XML as a header for the EPP server.
|
132
|
+
def packed(xml)
|
133
|
+
[xml.size + 4].pack("N")
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns size of header of response from the EPP server.
|
137
|
+
def header_size(header)
|
138
|
+
header.unpack("N").first
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Sends a standard login request to the EPP server.
|
144
|
+
def login
|
145
|
+
raise SocketError, "Socket must be opened before logging in" if !@socket or @socket.closed?
|
146
|
+
|
147
|
+
builder = build_epp_request do |xml|
|
148
|
+
xml.command {
|
149
|
+
xml.login {
|
150
|
+
xml.clID tag
|
151
|
+
xml.pw password
|
152
|
+
xml.options {
|
153
|
+
xml.version version
|
154
|
+
xml.lang lang
|
155
|
+
}
|
156
|
+
xml.svcs {
|
157
|
+
xml.objURI "urn:ietf:params:xml:ns:domain-1.0"
|
158
|
+
xml.objURI "urn:ietf:params:xml:ns:contact-1.0"
|
159
|
+
xml.objURI "urn:ietf:params:xml:ns:host-1.0"
|
160
|
+
|
161
|
+
unless extensions.empty?
|
162
|
+
xml.svcExtension {
|
163
|
+
for uri in extensions
|
164
|
+
xml.extURI uri
|
165
|
+
end
|
166
|
+
}
|
167
|
+
end
|
168
|
+
}
|
169
|
+
}
|
170
|
+
xml.clTRID UUIDTools::UUID.timestamp_create.to_s
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
response = Nokogiri::XML(send_request(builder.to_xml))
|
175
|
+
|
176
|
+
handle_response(response)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Sends a standard logout request to the EPP server.
|
180
|
+
def logout
|
181
|
+
raise SocketError, "Socket must be opened before logging out" if !@socket or @socket.closed?
|
182
|
+
|
183
|
+
builder = build_epp_request do |xml|
|
184
|
+
xml.command {
|
185
|
+
xml.logout
|
186
|
+
xml.clTRID UUIDTools::UUID.timestamp_create.to_s
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
response = Nokogiri::XML(send_request(builder.to_xml))
|
191
|
+
|
192
|
+
handle_response(response, 1500)
|
193
|
+
end
|
194
|
+
|
195
|
+
def handle_response(response, acceptable_response = 1000)
|
196
|
+
result_code = response.css('epp response result').first['code'].to_i
|
197
|
+
|
198
|
+
if result_code == acceptable_response
|
199
|
+
return true
|
200
|
+
else
|
201
|
+
result_message = doc.css('epp response result msg').first.text.strip
|
202
|
+
|
203
|
+
raise EppErrorResponse.new(:xml => response, :code => result_code, :message => result_message)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/lib/epp/version.rb
ADDED
data/lib/epp.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/epp/version"
|
2
|
+
# require 'rubygems'
|
3
|
+
require 'openssl'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'uuidtools'
|
6
|
+
require 'socket'
|
7
|
+
|
8
|
+
# Package files
|
9
|
+
require File.dirname(__FILE__) + '/require_parameters.rb'
|
10
|
+
require File.dirname(__FILE__) + '/epp/server.rb'
|
11
|
+
require File.dirname(__FILE__) + '/epp/exceptions.rb'
|
12
|
+
|
13
|
+
module Epp #:nodoc:
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RequiresParameters #:nodoc:
|
2
|
+
def requires!(hash, *params)
|
3
|
+
params.each do |param|
|
4
|
+
if param.is_a?(Array)
|
5
|
+
raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
|
6
|
+
|
7
|
+
valid_options = param[1..-1]
|
8
|
+
raise ArgumentError.new("Parameter: #{param.first} must be one of #{valid_options.to_sentence(:connector => 'or')}") unless valid_options.include?(hash[param.first])
|
9
|
+
else
|
10
|
+
raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/test/test_epp.rb
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EppTest < Test::Unit::TestCase
|
4
|
+
context "EPP" do
|
5
|
+
context "server" do
|
6
|
+
setup do
|
7
|
+
@epp = Epp::Server.new(
|
8
|
+
:server => "test-epp.nominet.org.uk",
|
9
|
+
:tag => "TEST",
|
10
|
+
:password => "test"
|
11
|
+
)
|
12
|
+
|
13
|
+
@tcp_sock = mock('TCPSocket')
|
14
|
+
@ssl_sock = mock('OpenSSL::SSL::SSLSocket')
|
15
|
+
end
|
16
|
+
|
17
|
+
should "verify class name is Epp::Server" do
|
18
|
+
assert @epp.is_a?(Epp::Server)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "require server, tag and password attributes" do
|
22
|
+
assert_raises ArgumentError do
|
23
|
+
epp = Epp::Server.new(:tag => "a", :password => "a")
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_raises ArgumentError do
|
27
|
+
epp = Epp::Server.new(:server => "a", :password => "a")
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_raises ArgumentError do
|
31
|
+
epp = Epp::Server.new(:server => "a", :tag => "a")
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_nothing_raised do
|
35
|
+
epp = Epp::Server.new(:server => "a", :tag => "a", :password => "a")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should "set instance variables for attributes" do
|
40
|
+
epp = Epp::Server.new(
|
41
|
+
:tag => "TAG",
|
42
|
+
:password => "f00bar",
|
43
|
+
:server => "nominet-epp.server.org.uk",
|
44
|
+
:port => 8700,
|
45
|
+
:lang => "es",
|
46
|
+
:services => ["urn:ietf:params:xml:ns:domain-nom-ext-1.1.xsd"],
|
47
|
+
:extensions => ["domain-nom-ext-1.1.xsd"],
|
48
|
+
:version => "90.0",
|
49
|
+
:key => "ssl_key",
|
50
|
+
:cert => "ssl_cert"
|
51
|
+
)
|
52
|
+
|
53
|
+
assert_equal "TAG", epp.tag
|
54
|
+
assert_equal "f00bar", epp.password
|
55
|
+
assert_equal "nominet-epp.server.org.uk", epp.server
|
56
|
+
assert_equal 8700, epp.port
|
57
|
+
assert_equal "es", epp.lang
|
58
|
+
assert_equal ["urn:ietf:params:xml:ns:domain-nom-ext-1.1.xsd"], epp.services
|
59
|
+
assert_equal ["domain-nom-ext-1.1.xsd"], epp.extensions
|
60
|
+
assert_equal "90.0", epp.version
|
61
|
+
assert_equal "ssl_key", epp.key
|
62
|
+
assert_equal "ssl_cert", epp.cert
|
63
|
+
end
|
64
|
+
|
65
|
+
should "build a new XML request" do
|
66
|
+
xml = xml_file("new_request.xml")
|
67
|
+
|
68
|
+
assert @epp.build_epp_request.is_a?(Nokogiri::XML::Builder)
|
69
|
+
assert_equal xml, @epp.build_epp_request.to_xml
|
70
|
+
end
|
71
|
+
|
72
|
+
should "open connection and receive a greeting" do
|
73
|
+
prepare_socket!
|
74
|
+
|
75
|
+
assert @epp.open_connection
|
76
|
+
end
|
77
|
+
|
78
|
+
should "return true if connection closed" do
|
79
|
+
prepare_socket!
|
80
|
+
|
81
|
+
@epp.open_connection
|
82
|
+
|
83
|
+
@tcp_sock.stubs(:close).returns(nil)
|
84
|
+
@ssl_sock.stubs(:close).returns(nil)
|
85
|
+
@tcp_sock.stubs(:closed?).returns(true)
|
86
|
+
@ssl_sock.stubs(:closed?).returns(true)
|
87
|
+
|
88
|
+
assert @epp.close_connection
|
89
|
+
end
|
90
|
+
|
91
|
+
should "get frame from new EPP servers with a header of four bytes" do
|
92
|
+
prepare_socket!
|
93
|
+
|
94
|
+
@epp.open_connection
|
95
|
+
|
96
|
+
response = xml_file("test_response.xml")
|
97
|
+
|
98
|
+
@ssl_sock.expects(:read).with(4).returns("\000\000\003\"")
|
99
|
+
@ssl_sock.expects(:read).with(798).returns(response)
|
100
|
+
|
101
|
+
assert response, @epp.get_frame
|
102
|
+
end
|
103
|
+
|
104
|
+
should "raise exception if socket closed unexpectedly while getting frame" do
|
105
|
+
prepare_socket!
|
106
|
+
simulate_close!
|
107
|
+
|
108
|
+
@epp.open_connection
|
109
|
+
@epp.close_connection
|
110
|
+
|
111
|
+
assert_raises SocketError do
|
112
|
+
@epp.get_frame
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
should "raise exception if header cannot be read when getting frame" do
|
117
|
+
prepare_socket!
|
118
|
+
|
119
|
+
@epp.open_connection
|
120
|
+
|
121
|
+
@ssl_sock.expects(:read).with(4).returns(nil)
|
122
|
+
@ssl_sock.stubs(:eof?).returns(false)
|
123
|
+
|
124
|
+
assert_raises SocketError do
|
125
|
+
@epp.get_frame
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
should "send frame to an EPP server" do
|
130
|
+
prepare_socket!
|
131
|
+
|
132
|
+
@epp.open_connection
|
133
|
+
|
134
|
+
send = xml_file("test_request.xml")
|
135
|
+
|
136
|
+
@ssl_sock.expects(:write).with(@epp.packed(send) + send).returns(121)
|
137
|
+
|
138
|
+
assert_equal 121, @epp.send_frame(send)
|
139
|
+
end
|
140
|
+
|
141
|
+
should "create a packed header for EPP request" do
|
142
|
+
xml_to_send = "<xml><test/></xml>"
|
143
|
+
assert_equal "\000\000\000\026", @epp.packed(xml_to_send)
|
144
|
+
end
|
145
|
+
|
146
|
+
should "return size of header from EPP response" do
|
147
|
+
assert_equal [22], "\000\000\000\026".unpack("N")
|
148
|
+
assert_equal 22, @epp.header_size("\000\000\000\026")
|
149
|
+
end
|
150
|
+
|
151
|
+
should "send frame, and get response from server" do
|
152
|
+
prepare_socket!
|
153
|
+
|
154
|
+
@epp.open_connection
|
155
|
+
|
156
|
+
send = xml_file("test_request.xml")
|
157
|
+
receive = xml_file("test_response.xml")
|
158
|
+
|
159
|
+
@ssl_sock.expects(:write).with(@epp.packed(send) + send).returns(121)
|
160
|
+
@ssl_sock.expects(:read).with(4).returns("\000\000\003\"")
|
161
|
+
@ssl_sock.expects(:read).with(798).returns(receive)
|
162
|
+
|
163
|
+
assert receive, @epp.send_request(send)
|
164
|
+
end
|
165
|
+
|
166
|
+
should "wrap a request around a logging in and logging out request" do
|
167
|
+
prepare_socket!
|
168
|
+
simulate_close!
|
169
|
+
check_socket!
|
170
|
+
|
171
|
+
test_request = xml_file("test_request.xml")
|
172
|
+
test_response = xml_file("test_response.xml")
|
173
|
+
|
174
|
+
@epp.expects(:login).returns(true)
|
175
|
+
@epp.expects(:logout).returns(true)
|
176
|
+
@epp.expects(:send_request).with(test_request).returns(test_response)
|
177
|
+
|
178
|
+
@response = @epp.request(test_request)
|
179
|
+
|
180
|
+
assert_equal test_response, @response
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "exceptions" do
|
185
|
+
should "require XML, code and message attributes" do
|
186
|
+
assert_raises ArgumentError do
|
187
|
+
e = EppErrorResponse.new(:code => "a", :message => "a")
|
188
|
+
end
|
189
|
+
|
190
|
+
assert_raises ArgumentError do
|
191
|
+
e = EppErrorResponse.new(:xml => "a", :message => "a")
|
192
|
+
end
|
193
|
+
|
194
|
+
assert_raises ArgumentError do
|
195
|
+
e = EppErrorResponse.new(:xml => "a", :code => "a")
|
196
|
+
end
|
197
|
+
|
198
|
+
assert_nothing_raised do
|
199
|
+
e = EppErrorResponse.new(:xml => "a", :code => "a", :message => "a")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
should "print error message to string" do
|
204
|
+
e = EppErrorResponse.new(
|
205
|
+
:xml => "<xml></xml>",
|
206
|
+
:code => 400,
|
207
|
+
:message => "Test error message"
|
208
|
+
)
|
209
|
+
|
210
|
+
assert_equal "Test error message (code 400)", e.to_s
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def prepare_socket!
|
218
|
+
@response = xml_file("test_response.xml")
|
219
|
+
|
220
|
+
TCPSocket.expects(:new).returns(@tcp_sock)
|
221
|
+
OpenSSL::SSL::SSLSocket.expects(:new).returns(@ssl_sock)
|
222
|
+
|
223
|
+
@ssl_sock.expects(:sync_close=).with(true)
|
224
|
+
@ssl_sock.expects(:connect).returns(@ssl_sock)
|
225
|
+
@ssl_sock.expects(:read).with(4).returns("\000\000\003\r")
|
226
|
+
@ssl_sock.expects(:read).with(777).returns(@response)
|
227
|
+
@ssl_sock.stubs(:eof?)
|
228
|
+
end
|
229
|
+
|
230
|
+
def check_socket!
|
231
|
+
@ssl_sock.stubs(:closed?)
|
232
|
+
end
|
233
|
+
|
234
|
+
def simulate_close!
|
235
|
+
@ssl_sock.stubs(:close).returns(nil)
|
236
|
+
@tcp_sock.stubs(:close).returns(nil)
|
237
|
+
@ssl_sock.stubs(:closed?).returns(true)
|
238
|
+
@tcp_sock.stubs(:closed?).returns(true)
|
239
|
+
end
|
240
|
+
|
241
|
+
def xml_file(name)
|
242
|
+
File.read(File.dirname(__FILE__) + "/xml/#{name}")
|
243
|
+
end
|
244
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/xml/error.xml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
|
3
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
4
|
+
xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd">
|
5
|
+
<response>
|
6
|
+
<result code="2200">
|
7
|
+
<msg>Authentication error</msg>
|
8
|
+
</result>
|
9
|
+
<trID>
|
10
|
+
<clTRID>ABC-12345</clTRID>
|
11
|
+
<svTRID>54321-XYZ</svTRID>
|
12
|
+
</trID>
|
13
|
+
</response>
|
14
|
+
</epp>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8' standalone='no'?><epp xsi:schemaLocation='urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='urn:ietf:params:xml:ns:epp-1.0'><command><login><clID>TEST</clID><pw>test</pw><options><version>1.0</version><lang>en</lang></options><svcs><objURI>urn:ietf:params:xml:ns:domain-1.0</objURI><objURI>urn:ietf:params:xml:ns:contact-1.0</objURI><objURI>urn:ietf:params:xml:ns:host-1.0</objURI></svcs></login><clTRID>ABC-12345</clTRID></command></epp>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
3
|
+
<response>
|
4
|
+
<result code="1000">
|
5
|
+
<msg>Command completed successfully</msg>
|
6
|
+
</result>
|
7
|
+
<trID>
|
8
|
+
<clTRID>ABC-12345</clTRID>
|
9
|
+
<svTRID>54321-XYZ</svTRID>
|
10
|
+
</trID>
|
11
|
+
</response>
|
12
|
+
</epp>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8' standalone='no'?><epp xsi:schemaLocation='urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='urn:ietf:params:xml:ns:epp-1.0'><command><login><clID>TEST</clID><pw>test</pw><options><version>1.0</version><lang>en</lang></options><svcs><objURI>urn:ietf:params:xml:ns:domain-1.0</objURI><objURI>urn:ietf:params:xml:ns:contact-1.0</objURI><objURI>urn:ietf:params:xml:ns:host-1.0</objURI><svcExtension><extURI>urn:ietf:params:xml:ns:rgp-1.0</extURI></svcExtension></svcs></login><clTRID>ABC-12345</clTRID></command></epp>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8' standalone='no'?><epp xsi:schemaLocation='urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='urn:ietf:params:xml:ns:epp-1.0'><command><logout/></command></epp>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
3
|
+
<response>
|
4
|
+
<result code="1500">
|
5
|
+
<msg>Command completed successfully; ending session</msg>
|
6
|
+
</result>
|
7
|
+
<trID>
|
8
|
+
<clTRID>ABC-12345</clTRID>
|
9
|
+
<svTRID>54321-XYZ</svTRID>
|
10
|
+
</trID>
|
11
|
+
</response>
|
12
|
+
</epp>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
|
3
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
4
|
+
xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd">
|
5
|
+
<greeting>
|
6
|
+
<svID>CoCCA EPP Server - epp.cocca.iors.cx</svID>
|
7
|
+
<svDate>2010-07-07T12:46:33.0536Z</svDate>
|
8
|
+
<svcMenu>
|
9
|
+
<version>1.0</version>
|
10
|
+
<lang>en</lang>
|
11
|
+
<objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
|
12
|
+
<objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
|
13
|
+
<objURI>urn:ietf:params:xml:ns:host-1.0</objURI>
|
14
|
+
<svcExtension>
|
15
|
+
<extURI>urn:ietf:params:xml:ns:rgp-1.0</extURI>
|
16
|
+
</svcExtension>
|
17
|
+
</svcMenu>
|
18
|
+
<dcp>
|
19
|
+
<access><all/></access>
|
20
|
+
<statement>
|
21
|
+
<purpose><admin/><prov/></purpose>
|
22
|
+
<recipient><ours/><public/></recipient>
|
23
|
+
<retention><stated/></retention>
|
24
|
+
</statement>
|
25
|
+
</dcp>
|
26
|
+
</greeting>
|
27
|
+
</epp>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
3
|
+
<greeting>
|
4
|
+
<svID>Example EPP server epp.example.com</svID>
|
5
|
+
<svDate>2000-06-08T22:00:00.0Z</svDate>
|
6
|
+
<svcMenu>
|
7
|
+
<version>1.0</version>
|
8
|
+
<lang>en</lang>
|
9
|
+
<lang>fr</lang>
|
10
|
+
<objURI>urn:ietf:params:xml:ns:obj1</objURI>
|
11
|
+
<objURI>urn:ietf:params:xml:ns:obj2</objURI>
|
12
|
+
<objURI>urn:ietf:params:xml:ns:obj3</objURI>
|
13
|
+
<svcExtension>
|
14
|
+
<extURI>http://custom/obj1ext-1.0</extURI>
|
15
|
+
</svcExtension>
|
16
|
+
</svcMenu>
|
17
|
+
<dcp>
|
18
|
+
<access><all/></access>
|
19
|
+
<statement>
|
20
|
+
<purpose><admin/><prov/></purpose>
|
21
|
+
<recipient><ours/><public/></recipient>
|
22
|
+
<retention><stated/></retention>
|
23
|
+
</statement>
|
24
|
+
</dcp>
|
25
|
+
</greeting>
|
26
|
+
</epp>
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epp-nokogiri
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Josh Delsman
|
14
|
+
- Delwyn de Villiers
|
15
|
+
- Priit Haamer
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2013-04-09 00:00:00 +03:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: nokogiri
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 5
|
32
|
+
segments:
|
33
|
+
- 1
|
34
|
+
- 4
|
35
|
+
- 1
|
36
|
+
version: 1.4.1
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: uuidtools
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 3
|
48
|
+
segments:
|
49
|
+
- 0
|
50
|
+
version: "0"
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: shoulda
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: mocha
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
description: Basic functionality for connecting and making requests on EPP (Extensible Provisioning Protocol) servers
|
82
|
+
email:
|
83
|
+
- jdelsman@ultraspeed.com
|
84
|
+
- delwyn.d@gmail.com
|
85
|
+
- priit@edicy.com
|
86
|
+
executables: []
|
87
|
+
|
88
|
+
extensions: []
|
89
|
+
|
90
|
+
extra_rdoc_files: []
|
91
|
+
|
92
|
+
files:
|
93
|
+
- .gitignore
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE
|
96
|
+
- README.rdoc
|
97
|
+
- Rakefile
|
98
|
+
- VERSION
|
99
|
+
- epp.gemspec
|
100
|
+
- lib/epp.rb
|
101
|
+
- lib/epp/exceptions.rb
|
102
|
+
- lib/epp/server.rb
|
103
|
+
- lib/epp/version.rb
|
104
|
+
- lib/require_parameters.rb
|
105
|
+
- test/test_epp.rb
|
106
|
+
- test/test_helper.rb
|
107
|
+
- test/xml/error.xml
|
108
|
+
- test/xml/login_request.xml
|
109
|
+
- test/xml/login_response.xml
|
110
|
+
- test/xml/login_with_extensions_request.xml
|
111
|
+
- test/xml/logout_request.xml
|
112
|
+
- test/xml/logout_response.xml
|
113
|
+
- test/xml/new_request.xml
|
114
|
+
- test/xml/socket_preparation.xml
|
115
|
+
- test/xml/test_request.xml
|
116
|
+
- test/xml/test_response.xml
|
117
|
+
has_rdoc: true
|
118
|
+
homepage: http://github.com/delwyn/epp
|
119
|
+
licenses: []
|
120
|
+
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 3
|
141
|
+
segments:
|
142
|
+
- 0
|
143
|
+
version: "0"
|
144
|
+
requirements: []
|
145
|
+
|
146
|
+
rubyforge_project: epp
|
147
|
+
rubygems_version: 1.4.2
|
148
|
+
signing_key:
|
149
|
+
specification_version: 3
|
150
|
+
summary: EPP (Extensible Provisioning Protocol) for Ruby
|
151
|
+
test_files:
|
152
|
+
- test/test_epp.rb
|
153
|
+
- test/test_helper.rb
|
154
|
+
- test/xml/error.xml
|
155
|
+
- test/xml/login_request.xml
|
156
|
+
- test/xml/login_response.xml
|
157
|
+
- test/xml/login_with_extensions_request.xml
|
158
|
+
- test/xml/logout_request.xml
|
159
|
+
- test/xml/logout_response.xml
|
160
|
+
- test/xml/new_request.xml
|
161
|
+
- test/xml/socket_preparation.xml
|
162
|
+
- test/xml/test_request.xml
|
163
|
+
- test/xml/test_response.xml
|