epp-fork 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +88 -0
- data/Rakefile +15 -0
- data/lib/epp.rb +16 -0
- data/lib/epp/exceptions.rb +13 -0
- data/lib/epp/server.rb +203 -0
- data/lib/require_parameters.rb +14 -0
- data/test/test_epp.rb +11 -0
- data/test/test_helper.rb +3 -0
- metadata +78 -0
data/README.rdoc
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
= EPP (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. Currently, major providers Centralnic and Nominet have been tested.
|
4
|
+
|
5
|
+
* {Nominet Standard EPP Documentation}[http://www.nominet.org.uk/registrars/systems/standardepp/]
|
6
|
+
* {Centralnic Labs EPP Documentation}[http://labs.centralnic.com/epp/]
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
|
10
|
+
You can install this gem with:
|
11
|
+
|
12
|
+
$ sudo gem sources -a http://gems.github.com
|
13
|
+
$ sudo gem install ultraspeed-epp
|
14
|
+
|
15
|
+
Then, you can require it in your Ruby app:
|
16
|
+
|
17
|
+
require "epp"
|
18
|
+
|
19
|
+
If you're using Rails, add the following line to your Rails <tt>config/environment.rb</tt>:
|
20
|
+
|
21
|
+
config.gem "ultraspeed-epp", :lib => "epp", :source => "http://gems.github.com"
|
22
|
+
|
23
|
+
Once you do that, you can install the gem by typing <tt>sudo rake gems:install</tt>.
|
24
|
+
|
25
|
+
== Example Usage
|
26
|
+
|
27
|
+
First, you must initialize an Epp::Server object to use. This requires the EPP server address, tag/username and password:
|
28
|
+
|
29
|
+
server = Epp::Server.new(
|
30
|
+
:server => "testbed-epp.nominet.org.uk",
|
31
|
+
:tag => "TESTING",
|
32
|
+
:password => "testing"
|
33
|
+
)
|
34
|
+
|
35
|
+
If no port is specified, it will be assumed that you will be using port 700.
|
36
|
+
|
37
|
+
You would then make an XML request to the server.
|
38
|
+
|
39
|
+
xml = "<?xml ... </epp>"
|
40
|
+
response = server.request(xml)
|
41
|
+
|
42
|
+
You can build this however you'd like. The process is as follows:
|
43
|
+
|
44
|
+
* Connect to EPP server, get the <greeting> frame
|
45
|
+
* Send a standard <login> request
|
46
|
+
* Send your request
|
47
|
+
* Send a standard <logout> request
|
48
|
+
* Disconnect the socket from the server
|
49
|
+
|
50
|
+
The EPP server 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.
|
51
|
+
|
52
|
+
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.
|
53
|
+
|
54
|
+
== Bugs/Issues
|
55
|
+
|
56
|
+
Please report all issues using the GitHub issue tracker at:
|
57
|
+
|
58
|
+
http://github.com/ultraspeed/epp/issues
|
59
|
+
|
60
|
+
== Credit
|
61
|
+
|
62
|
+
Author:: Josh Delsman at Ultraspeed (http://twitter.com/voxxit)
|
63
|
+
Inspired from:: http://labs.centralnic.com/Net_EPP_Client.php
|
64
|
+
|
65
|
+
== License
|
66
|
+
|
67
|
+
The MIT License
|
68
|
+
|
69
|
+
Copyright (c) 2009 Josh Delsman (Ultraspeed)
|
70
|
+
|
71
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
72
|
+
a copy of this software and associated documentation files (the
|
73
|
+
'Software'), to deal in the Software without restriction, including
|
74
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
75
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
76
|
+
permit persons to whom the Software is furnished to do so, subject to
|
77
|
+
the following conditions:
|
78
|
+
|
79
|
+
The above copyright notice and this permission notice shall be
|
80
|
+
included in all copies or substantial portions of the Software.
|
81
|
+
|
82
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
83
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
84
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
85
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
86
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
87
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
88
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'hanna/rdoctask'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/lib/epp'
|
8
|
+
|
9
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
10
|
+
|
11
|
+
# Rdoc
|
12
|
+
Rake::RDocTask.new do |rd|
|
13
|
+
rd.main = "README.rdoc"
|
14
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
15
|
+
end
|
data/lib/epp.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Gem and other dependencies
|
2
|
+
require 'rubygems'
|
3
|
+
require 'openssl'
|
4
|
+
require 'socket'
|
5
|
+
require 'active_support'
|
6
|
+
require 'rexml/document'
|
7
|
+
require 'hpricot'
|
8
|
+
|
9
|
+
# Package files
|
10
|
+
require File.dirname(__FILE__) + '/require_parameters.rb'
|
11
|
+
require File.dirname(__FILE__) + '/epp/server.rb'
|
12
|
+
require File.dirname(__FILE__) + '/epp/exceptions.rb'
|
13
|
+
|
14
|
+
module Epp #:nodoc:
|
15
|
+
VERSION = '1.0.8'
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class EppErrorResponse < StandardError #:nodoc:
|
2
|
+
attr_accessor :response
|
3
|
+
|
4
|
+
# Generic EPP exception. Accepts a response code and a message
|
5
|
+
def initialize(attributes = {})
|
6
|
+
@response_code = attributes[:code]
|
7
|
+
@message = attributes[:message]
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{@message} (code #{@response_code})"
|
12
|
+
end
|
13
|
+
end
|
data/lib/epp/server.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
module Epp #:nodoc:
|
2
|
+
class Server
|
3
|
+
include RequiresParameters
|
4
|
+
|
5
|
+
attr_accessor :tag, :password, :server, :port, :clTRID, :old_server
|
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>:clTRID</tt> - The client transaction identifier is an element that EPP specifies MAY be used to uniquely identify the command to the server. You are responsible for maintaining your own transaction identifier space to ensure uniqueness. Defaults to "ABC-12345"
|
17
|
+
# * <tt>:old_server</tt> - Set to true to read and write frames in a way that is compatible with old EPP servers. Default is false.
|
18
|
+
# * <tt>:lang</tt> - Set custom language attribute. Default is 'en'.
|
19
|
+
def initialize(attributes = {})
|
20
|
+
requires!(attributes, :tag, :password, :server)
|
21
|
+
|
22
|
+
@tag = attributes[:tag]
|
23
|
+
@password = attributes[:password]
|
24
|
+
@server = attributes[:server]
|
25
|
+
@port = attributes[:port] || 700
|
26
|
+
@clTRID = attributes[:clTRID] || "ABC-12345"
|
27
|
+
@old_server = attributes[:old_server] || false
|
28
|
+
@lang = attributes[:lang] || 'en'
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sends an XML request to the EPP server, and receives an XML response.
|
32
|
+
# <tt><login></tt> and <tt><logout></tt> requests are also wrapped
|
33
|
+
# around the request, so we can close the socket immediately after
|
34
|
+
# the request is made.
|
35
|
+
def request(xml)
|
36
|
+
open_connection
|
37
|
+
|
38
|
+
begin
|
39
|
+
login
|
40
|
+
@response = send_request(xml)
|
41
|
+
ensure
|
42
|
+
logout unless @old_server
|
43
|
+
close_connection
|
44
|
+
end
|
45
|
+
|
46
|
+
return @response
|
47
|
+
end
|
48
|
+
|
49
|
+
# private
|
50
|
+
|
51
|
+
# Wrapper which sends an XML frame to the server, and receives
|
52
|
+
# the response frame in return.
|
53
|
+
def send_request(xml)
|
54
|
+
send_frame(xml)
|
55
|
+
response = get_frame
|
56
|
+
end
|
57
|
+
|
58
|
+
def login
|
59
|
+
xml = REXML::Document.new
|
60
|
+
xml << REXML::XMLDecl.new("1.0", "UTF-8", "no")
|
61
|
+
|
62
|
+
xml.add_element("epp", {
|
63
|
+
"xmlns" => "urn:ietf:params:xml:ns:epp-1.0",
|
64
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
65
|
+
"xsi:schemaLocation" => "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd"
|
66
|
+
})
|
67
|
+
|
68
|
+
command = xml.root.add_element("command")
|
69
|
+
login = command.add_element("login")
|
70
|
+
|
71
|
+
login.add_element("clID").text = @tag
|
72
|
+
login.add_element("pw").text = @password
|
73
|
+
|
74
|
+
options = login.add_element("options")
|
75
|
+
options.add_element("version").text = "1.0"
|
76
|
+
options.add_element("lang").text = @lang
|
77
|
+
|
78
|
+
services = login.add_element("svcs")
|
79
|
+
services.add_element("objURI").text = "urn:ietf:params:xml:ns:domain-1.0"
|
80
|
+
services.add_element("objURI").text = "urn:ietf:params:xml:ns:contact-1.0"
|
81
|
+
services.add_element("objURI").text = "urn:ietf:params:xml:ns:host-1.0"
|
82
|
+
|
83
|
+
command.add_element("clTRID").text = @clTRID
|
84
|
+
|
85
|
+
# Receive the login response
|
86
|
+
response = Hpricot.XML(send_request(xml.to_s))
|
87
|
+
|
88
|
+
result_message = (response/"epp"/"response"/"result"/"msg").text.strip
|
89
|
+
result_code = (response/"epp"/"response"/"result").attr("code").to_i
|
90
|
+
|
91
|
+
if result_code == 1000
|
92
|
+
return true
|
93
|
+
else
|
94
|
+
raise EppErrorResponse.new(:code => result_code, :message => result_message)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def logout
|
99
|
+
xml = REXML::Document.new
|
100
|
+
xml << REXML::XMLDecl.new("1.0", "UTF-8", "no")
|
101
|
+
|
102
|
+
xml.add_element('epp', {
|
103
|
+
'xmlns' => "urn:ietf:params:xml:ns:epp-1.0",
|
104
|
+
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
|
105
|
+
'xsi:schemaLocation' => "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd"
|
106
|
+
})
|
107
|
+
|
108
|
+
command = xml.root.add_element("command")
|
109
|
+
login = command.add_element("logout")
|
110
|
+
|
111
|
+
# Receive the logout response
|
112
|
+
response = Hpricot.XML(send_request(xml.to_s))
|
113
|
+
|
114
|
+
result_message = (response/"epp"/"response"/"result"/"msg").text.strip
|
115
|
+
result_code = (response/"epp"/"response"/"result").attr("code").to_i
|
116
|
+
|
117
|
+
if result_code == 1500
|
118
|
+
return true
|
119
|
+
else
|
120
|
+
raise EppErrorResponse.new(:code => result_code, :message => result_message)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Establishes the connection to the server. If the connection is
|
125
|
+
# established, then this method will call get_frame and return
|
126
|
+
# the EPP <tt><greeting></tt> frame which is sent by the
|
127
|
+
# server upon connection.
|
128
|
+
def open_connection
|
129
|
+
@connection = TCPSocket.new(@server, @port)
|
130
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@connection)
|
131
|
+
|
132
|
+
# Synchronously close the connection & socket
|
133
|
+
@socket.sync_close
|
134
|
+
|
135
|
+
# Connect
|
136
|
+
@socket.connect
|
137
|
+
|
138
|
+
# Get the initial frame
|
139
|
+
get_frame
|
140
|
+
end
|
141
|
+
|
142
|
+
# Closes the connection to the EPP server.
|
143
|
+
def close_connection
|
144
|
+
if defined?(@socket) and @socket.is_a?(OpenSSL::SSL::SSLSocket)
|
145
|
+
@socket.close
|
146
|
+
@socket = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
if defined?(@connection) and @connection.is_a?(TCPSocket)
|
150
|
+
@connection.close
|
151
|
+
@connection = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
return true if @connection.nil? and @socket.nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Receive an EPP frame from the server. Since the connection is blocking,
|
158
|
+
# this method will wait until the connection becomes available for use. If
|
159
|
+
# the connection is broken, a SocketError will be raised. Otherwise,
|
160
|
+
# it will return a string containing the XML from the server.
|
161
|
+
def get_frame
|
162
|
+
if @old_server
|
163
|
+
data = ''
|
164
|
+
first_char = @socket.read(1)
|
165
|
+
if first_char.nil? and @socket.eof?
|
166
|
+
raise SocketError.new("Connection closed by remote server")
|
167
|
+
elsif first_char.nil?
|
168
|
+
raise SocketError.new("Error reading frame from remote server")
|
169
|
+
else
|
170
|
+
data << first_char
|
171
|
+
while char = @socket.read(1)
|
172
|
+
data << char
|
173
|
+
return data if data =~ %r|<\/epp>\n$|mi # at end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
else
|
177
|
+
header = @socket.read(4)
|
178
|
+
|
179
|
+
if header.nil? and @socket.eof?
|
180
|
+
raise SocketError.new("Connection closed by remote server")
|
181
|
+
elsif header.nil?
|
182
|
+
raise SocketError.new("Error reading frame from remote server")
|
183
|
+
else
|
184
|
+
unpacked_header = header.unpack("N")
|
185
|
+
length = unpacked_header[0]
|
186
|
+
|
187
|
+
if length < 5
|
188
|
+
raise SocketError.new("Got bad frame header length of #{length} bytes from the server")
|
189
|
+
else
|
190
|
+
response = @socket.read(length - 4)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Send an XML frame to the server. Should return the total byte
|
197
|
+
# size of the frame sent to the server. If the socket returns EOF,
|
198
|
+
# the connection has closed and a SocketError is raised.
|
199
|
+
def send_frame(xml)
|
200
|
+
@socket.write( @old_server ? (xml + "\r\n") : ([xml.size + 4].pack("N") + xml) )
|
201
|
+
end
|
202
|
+
end
|
203
|
+
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
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epp-fork
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Blaz Grilc
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-17 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70114712986360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70114712986360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hpricot
|
27
|
+
requirement: &70114712985900 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.8.1
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70114712985900
|
36
|
+
description: Basic functionality for connecting and making requests on EPP (Extensible
|
37
|
+
Provisioning Protocol) servers.
|
38
|
+
email: blaz.grilc@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- lib/epp/exceptions.rb
|
44
|
+
- lib/epp/server.rb
|
45
|
+
- lib/epp.rb
|
46
|
+
- lib/require_parameters.rb
|
47
|
+
- Rakefile
|
48
|
+
- README.rdoc
|
49
|
+
- test/test_epp.rb
|
50
|
+
- test/test_helper.rb
|
51
|
+
homepage: http://github.com/blaz/epp
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --inline-source
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.8.6
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Basic functionality for connecting and making requests on EPP (Extensible
|
77
|
+
Provisioning Protocol) servers.
|
78
|
+
test_files: []
|