kono_epp_client 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c73e951c49f6a622590103b3359eb73b94f7534
4
- data.tar.gz: 987a223e10b4e7c57c325e66c559d64780502971
3
+ metadata.gz: 80640a05d680c3d25c1371495913b4816266e4c2
4
+ data.tar.gz: 1e2fa73d4c1e1d51ef7a487c275a9a8a08226acb
5
5
  SHA512:
6
- metadata.gz: 31994158d73ee50067dc702f35f9fb3a2519023f3dcbf84dfd471ad9af84bc9d83277005ab98c2a5ff5601d847b31159e6cc4ad5feec6bf06727e3a3113d330e
7
- data.tar.gz: de2d01f1a0a142d05059ebfcdda1855d7095ba2f5fa5cd349d94493a55e0c315315e01d024dbc47992c1c3f7909ae64564992885e93671a8d1f2afb40e4d2723
6
+ metadata.gz: 3bdcb5660089b22ec5ec0f026a35517d04c4c75006b863154db2a04cc0c8809348f0eda14496cd3ed105941c195666ee7a78b8b0eace40db8f44b22efeb1b418
7
+ data.tar.gz: 4f1eca44ce49bee99b1ededec75b3e30c058de3ffd1a5cf186e3579355767bbbb25b9f7d83930595b8a7e721bc7b271ca295c23c5642f1353d870fdd7a6fd90b
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # kono_epp_client
2
+ A simple EPP Client
@@ -0,0 +1,15 @@
1
+ include REXML
2
+
3
+ class KonoEppCommand < REXML::Document
4
+ def initialize( source = nil, context = nil )
5
+ super( source, context )
6
+
7
+ add( XMLDecl.new( "1.0", "UTF-8", "no" ) )
8
+
9
+ epp = add_element( "epp", { "xmlns" => "urn:ietf:params:xml:ns:epp-1.0",
10
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
11
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd" } )
12
+
13
+ epp.add_element( "command" )
14
+ end
15
+ end
@@ -0,0 +1,87 @@
1
+ class KonoEppCreateContact < KonoEppCommand
2
+ def initialize( options )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+
7
+ create = command.add_element "create"
8
+
9
+ contact_create = create.add_element( "contact:create", { "xmlns:contact" => "urn:ietf:params:xml:ns:contact-1.0",
10
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd" } )
11
+
12
+ puts options
13
+ id = contact_create.add_element "contact:id"
14
+ id.text = options[:id]
15
+
16
+ postal_info = contact_create.add_element "contact:postalInfo", { "type" => "loc" }
17
+
18
+ name = postal_info.add_element "contact:name"
19
+ name.text = options[:name]
20
+
21
+ unless options[:organization].blank?
22
+ organization = postal_info.add_element "contact:org"
23
+ organization.text = options[:organization]
24
+ end
25
+
26
+ addr = postal_info.add_element "contact:addr"
27
+
28
+ unless options[:street].blank?
29
+ street = addr.add_element "contact:street"
30
+ street.text = options[:street]
31
+ end
32
+
33
+ city = addr.add_element "contact:city"
34
+ city.text = options[:city]
35
+
36
+ unless options[:state].blank?
37
+ state = addr.add_element "contact:sp"
38
+ state.text = options[:state]
39
+ end
40
+
41
+ unless options[:postal_code].blank?
42
+ postal_code = addr.add_element "contact:pc"
43
+ postal_code.text = options[:postal_code]
44
+ end
45
+
46
+ country_code = addr.add_element "contact:cc"
47
+ country_code.text = options[:country_code]
48
+
49
+ if options[:voice]
50
+ voice = contact_create.add_element "contact:voice"
51
+ voice.text = options[:voice]
52
+ end
53
+
54
+ if options[:fax]
55
+ fax = contact_create.add_element "contact:fax"
56
+ fax.text = options[:fax]
57
+ end
58
+
59
+ email = contact_create.add_element "contact:email"
60
+ email.text = options[:email]
61
+
62
+ auth_info = contact_create.add_element "contact:authInfo"
63
+ pw = auth_info.add_element "contact:pw"
64
+ pw.text = options[:auth_info]
65
+
66
+ # FIXME
67
+ extension = command.add_element "extension"
68
+ extension_create = extension.add_element "extcon:create", { "xmlns:extcon" => 'http://www.nic.it/ITNIC-EPP/extcon-1.0',
69
+ "xsi:schemaLocation" => 'http://www.nic.it/ITNIC-EPP/extcon-1.0 extcon-1.0.xsd' }
70
+
71
+ publish = extension_create.add_element "extcon:consentForPublishing"
72
+ publish.text = options[:publish] == "1" ? "true" : "false"
73
+
74
+ if options[:is_registrant]
75
+ extcon_registrant = extension_create.add_element "extcon:registrant"
76
+
77
+ extcon_nationality = extcon_registrant.add_element "extcon:nationalityCode"
78
+ extcon_nationality.text = options[:nationality]
79
+
80
+ extcon_entity = extcon_registrant.add_element "extcon:entityType"
81
+ extcon_entity.text = options[:entity_type]
82
+
83
+ extcon_regcode = extcon_registrant.add_element "extcon:regCode"
84
+ extcon_regcode.text = options[:reg_code]
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,42 @@
1
+ class KonoEppCreateDomain < KonoEppCommand
2
+ def initialize( options )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ create = command.add_element( "create" )
7
+
8
+
9
+ domain_create = create.add_element( "domain:create", { "xmlns:domain" => "urn:ietf:params:xml:ns:domain-1.0",
10
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd" } )
11
+
12
+ name = domain_create.add_element "domain:name"
13
+ name.text = options[:name]
14
+
15
+ domain_ns = domain_create.add_element "domain:ns"
16
+
17
+ options[:nameservers].each do |ns|
18
+ host_attr = domain_ns.add_element "domain:hostAttr"
19
+ host_name = host_attr.add_element "domain:hostName"
20
+
21
+ host_name.text = ns[0]
22
+
23
+ # FIXME IPv6
24
+ host_addr = host_attr.add_element "domain:hostAddr", { "ip" => "v4" }
25
+ host_addr.text = ns[1]
26
+ end
27
+
28
+ domain_registrant = domain_create.add_element "domain:registrant"
29
+ domain_registrant.text = options[:registrant]
30
+
31
+ domain_contact = domain_create.add_element "domain:contact", { "type" => "admin" }
32
+ domain_contact.text = options[:admin]
33
+
34
+ domain_contact = domain_create.add_element "domain:contact", { "type" => "tech" }
35
+ domain_contact.text = options[:tech]
36
+
37
+ domain_authinfo = domain_create.add_element "domain:authInfo"
38
+
39
+ domain_pw = domain_authinfo.add_element "domain:pw"
40
+ domain_pw.text = options[:authinfo]
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ class KonoEppDeleteContact < KonoEppCommand
2
+ def initialize( id )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+
7
+ delete = command.add_element "delete"
8
+
9
+ contact_delete = delete.add_element( "contact:delete", { "xmlns:contact" => "urn:ietf:params:xml:ns:contact-1.0",
10
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd" } )
11
+
12
+ contact_id = contact_delete.add_element "contact:id"
13
+ contact_id.text = id
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ class KonoEppDeleteDomain < KonoEppCommand
2
+ def initialize( name )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ delete = command.add_element( "delete" )
7
+
8
+ domain_delete = delete.add_element( "domain:delete", { "xmlns:domain" => "urn:ietf:params:xml:ns:domain-1.0",
9
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd" } )
10
+
11
+ domain_name = domain_delete.add_element "domain:name"
12
+ domain_name.text = name
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class KonoEppHello < REXML::Document
2
+ def initialize
3
+ super
4
+
5
+ add( XMLDecl.new( "1.0", "UTF-8", "no" ) )
6
+
7
+ epp = add_element( "epp", { "xmlns" => "urn:ietf:params:xml:ns:epp-1.0",
8
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
9
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd" } )
10
+
11
+ epp.add_element( "hello" )
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ class KonoEppInfoContact < KonoEppCommand
2
+ def initialize( id )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+
7
+ info = command.add_element "info"
8
+
9
+ contact_info = info.add_element( "contact:info", { "xmlns:contact" => "urn:ietf:params:xml:ns:contact-1.0",
10
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd" } )
11
+
12
+ contact_id = contact_info.add_element "contact:id"
13
+ contact_id.text = id
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ class KonoEppInfoDomain < KonoEppCommand
2
+ def initialize( name )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ info = command.add_element( "info" )
7
+
8
+ domain_info = info.add_element( "domain:info", { "xmlns:domain" => "urn:ietf:params:xml:ns:domain-1.0",
9
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd" } )
10
+
11
+ domain_name = domain_info.add_element "domain:name", { "hosts" => "all" }
12
+ domain_name.text = name
13
+ end
14
+ end
@@ -0,0 +1,79 @@
1
+ class KonoEppLogin < KonoEppCommand
2
+ def initialize( id = nil, password = nil )
3
+ super( nil, nil )
4
+
5
+ @command = root.elements['command']
6
+ @login = @command.add_element( "login" )
7
+
8
+ @login.add_element( "clID" ).text = id if id
9
+ @login.add_element( "pw" ).text = password if password
10
+
11
+ @command.add_element( "clTRID" ).text = "ABC-12345"
12
+ end
13
+
14
+ def version=( value )
15
+ @options = @login.add_element( "options" ) unless @options
16
+ @options.add_element( "version") unless @options.elements['version']
17
+
18
+ @options.elements['version'].text = value
19
+ end
20
+
21
+ def version
22
+ version = @options.elements['version']
23
+
24
+ version.text if version
25
+ end
26
+
27
+ def lang=( value )
28
+ @options = @login.add_element( "options" ) unless @options
29
+ @options.add_element( "lang") unless @options.elements['lang']
30
+
31
+ @options.elements['lang'].text = value
32
+ end
33
+
34
+ def lang
35
+ lang = @options.elements['lang']
36
+
37
+ lang.text if version
38
+ end
39
+
40
+ def new_password=( value )
41
+ newpw = @login.add_element( "newPW" )
42
+ newpw.text = value
43
+ end
44
+
45
+ def services=( services )
46
+ svcs = @login.add_element( "svcs" ) unless @login.elements['svcs']
47
+ services.each {|service| svcs.add_element("objURI").text = service }
48
+ end
49
+
50
+ def services
51
+ svcs = @login.elements['svcs']
52
+
53
+ res = []
54
+ svcs.elements.each( "objURI" ) { |obj| res << obj.text } if svcs
55
+
56
+ return res
57
+ end
58
+
59
+ def extensions=( extensions )
60
+ svcs = @login.elements['svcs']
61
+
62
+ svcs = @login.add_element( "svcs" ) unless svcs
63
+
64
+ # Include schema extensions for registrars which require it
65
+ extensions_container = svcs.add_element("svcExtension") unless extensions.empty?
66
+
67
+ for uri in extensions
68
+ extensions_container.add_element("extURI").text = uri
69
+ end
70
+ end
71
+
72
+ def extensions
73
+ svc_extension = @login.elements['svcs/svcExtension']
74
+
75
+ svc_extension.elements.each( "extURI" ) { |obj| res << obj.text } if svcs_extension
76
+
77
+ return res
78
+ end
79
+ end
@@ -0,0 +1,10 @@
1
+ class KonoEppLogout < KonoEppCommand
2
+ def initialize( id = nil, password = nil )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ command.add_element( "logout" )
7
+
8
+ command.add_element( "clTRID" ).text = "ABC-12345"
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ class KonoEppPoll < KonoEppCommand
2
+ def initialize( op = :req )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ poll = command.add_element( "poll" )
7
+
8
+ poll.add_attribute( "op", op.to_s )
9
+ end
10
+
11
+ def ack_id=( id )
12
+ poll = root.elements['command/poll']
13
+
14
+ poll.add_attribute( "msgID", id )
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ class KonoEppTransferDomain < KonoEppCommand
2
+ def initialize( name, authinfo, op )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ transfer = command.add_element( "transfer", { "op" => op } )
7
+
8
+ domain_transfer = transfer.add_element( "domain:transfer", { "xmlns:domain" => "urn:ietf:params:xml:ns:domain-1.0",
9
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd" } )
10
+
11
+ domain_name = domain_transfer.add_element "domain:name"
12
+ domain_name.text = name
13
+
14
+ domain_authinfo = domain_transfer.add_element "domain:authInfo"
15
+ domain_pw = domain_authinfo.add_element "domain:pw"
16
+
17
+ domain_pw.text = authinfo
18
+ end
19
+ end
@@ -0,0 +1,115 @@
1
+ class KonoEppUpdateContact < KonoEppCommand
2
+
3
+ # TODO: Add and remove fields
4
+ def initialize( options )
5
+ super( nil, nil )
6
+
7
+ command = root.elements['command']
8
+
9
+ update = command.add_element "update"
10
+
11
+ contact_update = update.add_element( "contact:update", { "xmlns:contact" => "urn:ietf:params:xml:ns:contact-1.0",
12
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd" } )
13
+
14
+ id = contact_update.add_element "contact:id"
15
+ id.text = options[:id]
16
+
17
+ contact_chg = contact_update.add_element "contact:chg"
18
+
19
+ unless options[:name].blank? \
20
+ and options[:organization].blank? \
21
+ and options[:address].blank? \
22
+ and options[:city].blank? \
23
+ and options[:state].blank? \
24
+ and options[:postal_code].blank? \
25
+ and options[:country].blank?
26
+
27
+ postal_info = contact_chg.add_element "contact:postalInfo", { "type" => "loc" }
28
+
29
+ unless options[:name].blank?
30
+ name = postal_info.add_element "contact:name"
31
+ name.text = options[:name]
32
+ end
33
+
34
+ unless options[:organization].blank?
35
+ organization = postal_info.add_element "contact:org"
36
+ organization.text = options[:organization]
37
+ end
38
+
39
+ # NOTE: city and country are REQUIRED
40
+ unless options[:address].blank? \
41
+ and options[:city].blank? \
42
+ and options[:state].blank? \
43
+ and options[:postal_code].blank? \
44
+ and options[:country].blank?
45
+
46
+ addr = postal_info.add_element "contact:addr"
47
+
48
+ unless options[:address].blank?
49
+ street = addr.add_element "contact:street"
50
+ street.text = options[:address]
51
+ end
52
+
53
+ city = addr.add_element "contact:city"
54
+ city.text = options[:city]
55
+
56
+ unless options[:state].blank?
57
+ state = addr.add_element "contact:sp"
58
+ state.text = options[:state]
59
+ end
60
+
61
+ unless options[:postal_code].blank?
62
+ postal_code = addr.add_element "contact:pc"
63
+ postal_code.text = options[:postal_code]
64
+ end
65
+
66
+ country_code = addr.add_element "contact:cc"
67
+ country_code.text = options[:country]
68
+ end
69
+ end
70
+
71
+ if options[:voice]
72
+ voice = contact_chg.add_element "contact:voice"
73
+ voice.text = options[:voice]
74
+ end
75
+
76
+ if options[:fax]
77
+ fax = contact_chg.add_element "contact:fax"
78
+ fax.text = options[:fax]
79
+ end
80
+
81
+ if options[:email]
82
+ email = contact_chg.add_element "contact:email"
83
+ email.text = options[:email]
84
+ end
85
+
86
+
87
+ unless not options.has_key?( :publish ) \
88
+ and options[:nationality].blank? \
89
+ and options[:entity_type].blank? \
90
+ and options[:reg_code].blank?
91
+
92
+ # FIXME
93
+ extension = command.add_element "extension"
94
+ extension_update = extension.add_element "extcon:update", { "xmlns:extcon" => 'http://www.nic.it/ITNIC-EPP/extcon-1.0',
95
+ "xsi:schemaLocation" => 'http://www.nic.it/ITNIC-EPP/extcon-1.0 extcon-1.0.xsd' }
96
+ if options.has_key?( :publish )
97
+ publish = extension_update.add_element "extcon:consentForPublishing"
98
+ publish.text = options[:publish]
99
+ end
100
+
101
+ if options[:becomes_registrant]
102
+ extcon_registrant = extension_update.add_element "extcon:registrant"
103
+
104
+ extcon_nationality = extcon_registrant.add_element "extcon:nationalityCode"
105
+ extcon_nationality.text = options[:nationality]
106
+
107
+ extcon_entity = extcon_registrant.add_element "extcon:entityType"
108
+ extcon_entity.text = options[:entity_type]
109
+
110
+ extcon_regcode = extcon_registrant.add_element "extcon:regCode"
111
+ extcon_regcode.text = options[:reg_code]
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,88 @@
1
+ class KonoEppUpdateDomain < KonoEppCommand
2
+ def initialize( options )
3
+ super( nil, nil )
4
+
5
+ command = root.elements['command']
6
+ update = command.add_element( "update" )
7
+
8
+
9
+ domain_update = update.add_element( "domain:update", { "xmlns:domain" => "urn:ietf:params:xml:ns:domain-1.0",
10
+ "xsi:schemaLocation" => "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd" } )
11
+
12
+ name = domain_update.add_element "domain:name"
13
+ name.text = options[:name]
14
+
15
+ if not options[:add_nameservers].blank? or options[:add_admin] or options[:add_tech]
16
+ domain_add = domain_update.add_element "domain:add"
17
+ end
18
+
19
+ if not options[:remove_nameservers].blank? or options[:remove_admin] or options[:remove_tech]
20
+ domain_remove = domain_update.add_element "domain:rem"
21
+ end
22
+
23
+ # <domain:add>
24
+ unless options[:add_nameservers].blank?
25
+ domain_add_ns = domain_add.add_element "domain:ns"
26
+
27
+ options[:add_nameservers].each do |ns|
28
+ host_attr = domain_add_ns.add_element "domain:hostAttr"
29
+ host_name = host_attr.add_element "domain:hostName"
30
+
31
+ host_name.text = ns[0]
32
+
33
+ # FIXME IPv6
34
+ host_addr = host_attr.add_element "domain:hostAddr", { "ip" => "v4" }
35
+ host_addr.text = ns[1]
36
+ end
37
+ end
38
+
39
+ if options[:add_admin]
40
+ domain_contact = domain_add.add_element "domain:contact", { "type" => "admin" }
41
+ domain_contact.text = options[:add_admin]
42
+ end
43
+
44
+ if options[:add_tech]
45
+ domain_contact = domain_add.add_element "domain:contact", { "type" => "tech" }
46
+ domain_contact.text = options[:add_tech]
47
+ end
48
+
49
+ # <domain:rem>
50
+ unless options[:remove_nameservers].blank?
51
+ domain_remove_ns = domain_remove.add_element "domain:ns"
52
+
53
+ options[:remove_nameservers].each do |name|
54
+ host_attr = domain_remove_ns.add_element "domain:hostAttr"
55
+ host_name = host_attr.add_element "domain:hostName"
56
+
57
+ host_name.text = name
58
+ end
59
+ end
60
+
61
+ if options[:remove_admin]
62
+ domain_contact = domain_remove.add_element "domain:contact", { "type" => "admin" }
63
+ domain_contact.text = options[:remove_admin]
64
+ end
65
+
66
+ if options[:remove_tech]
67
+ domain_contact = domain_remove.add_element "domain:contact", { "type" => "tech" }
68
+ domain_contact.text = options[:remove_tech]
69
+ end
70
+
71
+ # <domain:chg>
72
+ if options[:auth_info]
73
+ domain_change = domain_update.add_element "domain:chg"
74
+
75
+ if options[:registrant]
76
+ domain_registrant = domain_change.add_element "domain:registrant"
77
+ domain_registrant.text = options[:registrant]
78
+ end
79
+
80
+ domain_authinfo = domain_change.add_element "domain:authInfo"
81
+
82
+ domain_pw = domain_authinfo.add_element "domain:pw"
83
+ domain_pw.text = options[:auth_info]
84
+ end
85
+
86
+ # TODO: Registrant
87
+ end
88
+ end
@@ -0,0 +1,18 @@
1
+ class KonoEppErrorResponse < StandardError #:nodoc:
2
+ attr_accessor :response_xml, :response_code, :reason_code, :message
3
+
4
+ # Generic EPP exception. Accepts a response code and a message
5
+ def initialize(attributes = {})
6
+ @response_xml = attributes[:xml]
7
+ @response_code = attributes[:response_code]
8
+ @reason_code = attributes[:reason_code]
9
+ @message = attributes[:message]
10
+ end
11
+
12
+ def to_s
13
+ "#{@message} (reason: #{@reason_code} code: #{@response_code})"
14
+ end
15
+ end
16
+
17
+ class KonoEppAuthenticationPasswordExpired < KonoEppErrorResponse ; end
18
+ class KonoEppLoginNeeded < KonoEppErrorResponse ; end
data/lib/epp/server.rb ADDED
@@ -0,0 +1,271 @@
1
+ module KonoEppClient #:nodoc:
2
+ class Server
3
+ include REXML
4
+ include RequiresParameters
5
+
6
+ require 'nokogiri'
7
+
8
+ attr_accessor :tag, :password, :server, :port, :old_server, :services, :lang, :extensions, :version, :credit, :timeout
9
+
10
+ # ==== Required Attrbiutes
11
+ #
12
+ # * <tt>:server</tt> - The EPP server to connect to
13
+ # * <tt>:tag</tt> - The tag or username used with <tt><login></tt> requests.
14
+ # * <tt>:password</tt> - The password used with <tt><login></tt> requests.
15
+ #
16
+ # ==== Optional Attributes
17
+ #
18
+ # * <tt>:port</tt> - The EPP standard port is 700. However, you can choose a different port to use.
19
+ # * <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"
20
+ # * <tt>:old_server</tt> - Set to true to read and write frames in a way that is compatible with older EPP servers. Default is false.
21
+ # * <tt>:lang</tt> - Set custom language attribute. Default is 'en'.
22
+ # * <tt>:services</tt> - Use custom EPP services in the <login> frame. The defaults use the EPP standard domain, contact and host 1.0 services.
23
+ # * <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.
24
+ # * <tt>:version</tt> - Set the EPP version. Defaults to "1.0".
25
+ def initialize(attributes = {})
26
+ requires!(attributes, :tag, :password, :server)
27
+
28
+ @tag = attributes[:tag]
29
+ @password = attributes[:password]
30
+ @server = attributes[:server]
31
+ @port = attributes[:port] || 700
32
+ @old_server = attributes[:old_server] || false
33
+ @lang = attributes[:lang] || "en"
34
+ @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"]
35
+ @extensions = attributes[:extensions] || []
36
+ @version = attributes[:version] || "1.0"
37
+ @transport = attributes[:transport] || :tcp
38
+ @timeout = attributes[:timeout] || 30
39
+
40
+ @logged_in = false
41
+ end
42
+
43
+ def connect_and_hello
44
+ open_connection
45
+
46
+ hello
47
+ end
48
+
49
+ # Closes the connection to the EPP server.
50
+ def close_connection
51
+ @connection.close
52
+ end
53
+
54
+ # Sends an XML request to the EPP server, and receives an XML response.
55
+ # <tt><login></tt> and <tt><logout></tt> requests are also wrapped
56
+ # around the request, so we can close the socket immediately after
57
+ # the request is made.
58
+ def request( xml )
59
+ # open_connection
60
+
61
+ # @logged_in = true if login
62
+
63
+ begin
64
+ @response = send_request( xml )
65
+ ensure
66
+ if @logged_in && !old_server
67
+ @logged_in = false if logout
68
+ end
69
+ end
70
+
71
+ return @response
72
+ end
73
+
74
+ # Sends a standard login request to the EPP server.
75
+ def login
76
+ login = KonoEppLogin.new( tag, password )
77
+
78
+ # FIXME: Order matters
79
+ login.version = version
80
+ login.lang = lang
81
+
82
+ login.services = services
83
+ login.extensions = extensions
84
+
85
+ send_command( login )
86
+ end
87
+
88
+ def change_password( new_password )
89
+ login = KonoEppLogin.new( tag, password )
90
+
91
+ # FIXME: Order matters
92
+ login.new_password = new_password
93
+
94
+ login.version = version
95
+ login.lang = lang
96
+
97
+ login.services = services
98
+ login.extensions = extensions
99
+
100
+ send_command( login )
101
+ end
102
+
103
+ def logged_in?
104
+ begin
105
+ poll
106
+ rescue
107
+ return false
108
+ end
109
+
110
+ return true
111
+ end
112
+
113
+ # FIXME: Remove command wrappers?
114
+ def hello
115
+ response = Hpricot.XML( send_request( KonoEppHello.new.to_s ) )
116
+ end
117
+
118
+ def poll( id = nil )
119
+ poll = KonoEppPoll.new( id ? :ack : :req )
120
+
121
+ poll.ack_id = id if id
122
+
123
+ send_command( poll )
124
+ end
125
+
126
+ def create_contact( options )
127
+ contact = KonoEppCreateContact.new options
128
+ send_command( contact )
129
+ end
130
+
131
+ def delete_contact( id )
132
+ contact = KonoEppDeleteContact.new id
133
+ send_command( contact )
134
+ end
135
+
136
+ def update_contact( options )
137
+ contact = KonoEppUpdateContact.new options
138
+ send_command( contact )
139
+ end
140
+
141
+ def create_domain( options )
142
+ domain = KonoEppCreateDomain.new options
143
+ send_command( domain )
144
+ end
145
+
146
+ def update_domain( options )
147
+ domain = KonoEppUpdateDomain.new options
148
+ send_command( domain )
149
+ end
150
+
151
+ def delete_domain( name )
152
+ domain = KonoEppDeleteDomain.new name
153
+ send_command( domain )
154
+ end
155
+
156
+ def info_contact( id )
157
+ contact = KonoEppInfoContact.new id
158
+ send_command( contact )
159
+ end
160
+
161
+ def info_domain( name )
162
+ info = KonoEppInfoDomain.new name
163
+ send_command( info )
164
+ end
165
+
166
+ def transfer_domain( name, authinfo, op )
167
+ transfer = KonoEppTransferDomain.new name, authinfo, op
168
+ send_command( transfer )
169
+ end
170
+
171
+ # Sends a standard logout request to the EPP server.
172
+ def logout
173
+ send_command( KonoEppLogout.new, 1500 )
174
+ end
175
+
176
+ # private
177
+ # Wrapper which sends XML to the server, and receives
178
+ # the response in return.
179
+ def send_request( xml )
180
+ write( xml )
181
+ read
182
+ end
183
+
184
+ def send_command( command, expected_result = 1000..1999 )
185
+ namespaces = { 'extepp' => 'http://www.nic.it/ITNIC-EPP/extepp-2.0',
186
+ 'xmlns' => "urn:ietf:params:xml:ns:epp-1.0" }
187
+
188
+ xml = Nokogiri.XML( send_request( command.to_s ) )
189
+
190
+ # TODO: multiple <response> RFC 3730 §2.6
191
+ result = xml.at_xpath( "/xmlns:epp/xmlns:response[1]/xmlns:result",
192
+ namespaces )
193
+ raise KonoEppErrorResponse.new( :message => 'Malformed response' ) if result.nil?
194
+
195
+ xmlns_code = result.at_xpath( "@code" )
196
+ raise KonoEppErrorResponse.new( :message => 'Malformed response' ) if xmlns_code.nil?
197
+
198
+ response_code = xmlns_code.value.to_i
199
+
200
+ xmlns_msg = result.xpath( "xmlns:msg/text ()",
201
+ namespaces )
202
+ raise KonoEppErrorResponse.new( :message => 'Malformed response' ) if xmlns_msg.empty?
203
+
204
+ result_message = xmlns_msg.text.strip
205
+
206
+ # TODO: value
207
+
208
+ xmlns_ext_reason = result.xpath( "xmlns:extValue/xmlns:reason",
209
+ namespaces)
210
+ result_message += ": #{xmlns_ext_reason.text.strip}" unless xmlns_ext_reason.empty?
211
+
212
+ xmlns_reason_code = result.xpath( "xmlns:extValue/xmlns:value/extepp:reasonCode",
213
+ namespaces )
214
+ reason_code = xmlns_reason_code.text.strip.to_i unless xmlns_reason_code.empty?
215
+
216
+ credit_msg = xml.xpath( "//extepp:credit/text ()",
217
+ namespaces )
218
+ @credit = credit_msg.text.to_f unless credit_msg.empty?
219
+
220
+ if expected_result === response_code
221
+ return xml
222
+ end
223
+
224
+ args = { :xml => xml,
225
+ :response_code => response_code,
226
+ :reason_code => reason_code,
227
+ :message => result_message }
228
+
229
+ case [ response_code, reason_code ]
230
+ when [2200, 6004]
231
+ raise KonoEppAuthenticationPasswordExpired.new( args )
232
+ when [2002, 4015]
233
+ raise KonoEppLoginNeeded.new( args )
234
+ else
235
+ raise KonoEppErrorResponse.new( args )
236
+ end
237
+ end
238
+
239
+ # Establishes the connection to the server. If the connection is
240
+ # established, then this method will call read and return
241
+ # the EPP <tt><greeting></tt> which is sent by the
242
+ # server upon connection.
243
+ def open_connection
244
+ Timeout.timeout @timeout do
245
+ @connection = case @transport
246
+ when :tcp then KonoEppClient::Transport::TcpTransport.new( server, port )
247
+ when :http then KonoEppClient::Transport::HttpTransport.new( server, port )
248
+ end
249
+ end
250
+ end
251
+
252
+
253
+ # Receive an EPP response from the server. Since the connection is blocking,
254
+ # this method will wait until the connection becomes available for use. If
255
+ # the connection is broken, a SocketError will be raised. Otherwise,
256
+ # it will return a string containing the XML from the server.
257
+ def read
258
+ Timeout.timeout @timeout do
259
+ @connection.read
260
+ end
261
+ end
262
+
263
+ # Send XML to the server. If the socket returns EOF,
264
+ # the connection has closed and a SocketError is raised.
265
+ def write( xml )
266
+ Timeout.timeout @timeout do
267
+ @connection.write( xml )
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,13 @@
1
+ module KonoEppClient::Transport
2
+ def read
3
+ raise NotImplemented.new
4
+ end
5
+
6
+ def write
7
+ raise NotImplemented.new
8
+ end
9
+
10
+ def close
11
+ raise NotImplemented.new
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ require 'net/http'
2
+
3
+ module KonoEppClient::Transport
4
+ class HttpTransport
5
+ include KonoEppClient::Transport
6
+
7
+ require 'pstore'
8
+
9
+ def initialize( server, port )
10
+ @net_http = Net::HTTP.new( server, port )
11
+
12
+ @net_http.use_ssl = true
13
+ @net_http.ssl_version = :TLSv1
14
+ @net_http.verify_mode = OpenSSL::SSL::VERIFY_PEER
15
+
16
+ #FIXME: Commented because not work on MacOS (dev machine), is necessary for Linux machine?
17
+ #@net_http.ca_path = '/etc/ssl/certs'
18
+
19
+ # @net_http.set_debug_output $stderr
20
+ #@net_http.set_debug_output File.open( "/tmp/net.log", "a")
21
+
22
+ @store = PStore.new( "cookies.pstore" )
23
+ end
24
+
25
+ def read
26
+ #puts @response.body
27
+ @response.body
28
+ end
29
+
30
+ def write( xml )
31
+ @store.transaction do
32
+ cookie_str = '$Version="1"'
33
+ @store[:cookies].each do |c|
34
+ cookie_str += "; #{c[:name]}=#{c[:value]}"
35
+ end if @store[:cookies]
36
+
37
+ header = { "Cookie" => cookie_str }
38
+
39
+ @response = @net_http.post( "/", xml, header )
40
+
41
+ @store[:cookies] = parse_set_cookie( @response["Set-Cookie"] ) if @response["Set-Cookie"]
42
+ end
43
+ end
44
+
45
+ def close
46
+ end
47
+
48
+ private
49
+ def parse_set_cookie( string )
50
+ cookies = []
51
+
52
+ string.split( "," ).each do |cookie|
53
+ tokens = cookie.split ";"
54
+
55
+ name, value = tokens[0].split( "=", 2 )
56
+ # TODO: complete parsing. Maybe encapsulate the cookie as well.
57
+ cookies << { :name => name, :value => value }
58
+ end
59
+
60
+ return cookies
61
+ end
62
+
63
+ def http_post
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,93 @@
1
+ module KonoEppClient::Transport
2
+ class TcpTransport
3
+ include KonoEppClient::Transport
4
+
5
+ def initialize( server, port )
6
+ @connection = TCPSocket.new( server, port )
7
+ @socket = OpenSSL::SSL::SSLSocket.new( @connection )
8
+
9
+ # Synchronously close the connection & socket
10
+ @socket.sync_close
11
+
12
+ # Connect
13
+ @socket.connect
14
+
15
+ # Get the initial frame
16
+ read
17
+ end
18
+
19
+ def read
20
+ if old_server
21
+ data = ""
22
+ first_char = @socket.read(1)
23
+
24
+ if first_char.nil? and @socket.eof?
25
+ raise SocketError.new("Connection closed by remote server")
26
+ elsif first_char.nil?
27
+ raise SocketError.new("Error reading frame from remote server")
28
+ else
29
+ data << first_char
30
+
31
+ while char = @socket.read(1)
32
+ data << char
33
+
34
+ return data if data =~ %r|<\/epp>\n$|mi # at end
35
+ end
36
+ end
37
+ else
38
+ header = @socket.read(4)
39
+
40
+ if header.nil? and @socket.eof?
41
+ raise SocketError.new("Connection closed by remote server")
42
+ elsif header.nil?
43
+ raise SocketError.new("Error reading frame from remote server")
44
+ else
45
+ unpacked_header = header.unpack("N")
46
+ length = unpacked_header[0]
47
+
48
+ if length < 5
49
+ raise SocketError.new("Got bad frame header length of #{length} bytes from the server")
50
+ else
51
+ response = @socket.read(length - 4)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def write
58
+ if defined?( @socket ) and @socket.is_a?( OpenSSL::SSL::SSLSocket )
59
+ @socket.close
60
+ @socket = nil
61
+ end
62
+
63
+ if defined?( @connection ) and @connection.is_a?( TCPSocket )
64
+ @connection.close
65
+ @connection = nil
66
+ end
67
+
68
+ return true if @connection.nil? and @socket.nil?
69
+ end
70
+
71
+ def close
72
+ if defined?( @socket ) and @socket.is_a?( OpenSSL::SSL::SSLSocket )
73
+ @socket.close
74
+ @socket = nil
75
+ end
76
+
77
+ if defined?( @connection ) and @connection.is_a?( TCPSocket )
78
+ @connection.close
79
+ @connection = nil
80
+ end
81
+
82
+ return true if @connection.nil? and @socket.nil?
83
+ end
84
+ private
85
+ # Receive an EPP frame from the server. Since the connection is blocking,
86
+ # this method will wait until the connection becomes available for use. If
87
+ # the connection is broken, a SocketError will be raised. Otherwise,
88
+ # it will return a string containing the XML from the server.
89
+ def get_frame
90
+ end
91
+
92
+ end
93
+ 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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kono_epp_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fabio Bonelli
@@ -17,7 +17,28 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - README.md
21
+ - lib/epp/epp_command.rb
22
+ - lib/epp/epp_command/create_contact.rb
23
+ - lib/epp/epp_command/create_domain.rb
24
+ - lib/epp/epp_command/delete_contact.rb
25
+ - lib/epp/epp_command/delete_domain.rb
26
+ - lib/epp/epp_command/hello.rb
27
+ - lib/epp/epp_command/info_contact.rb
28
+ - lib/epp/epp_command/info_domain.rb
29
+ - lib/epp/epp_command/login.rb
30
+ - lib/epp/epp_command/logout.rb
31
+ - lib/epp/epp_command/poll.rb
32
+ - lib/epp/epp_command/transfer_domain.rb
33
+ - lib/epp/epp_command/update_contact.rb
34
+ - lib/epp/epp_command/update_domain.rb
35
+ - lib/epp/exceptions.rb
36
+ - lib/epp/server.rb
37
+ - lib/epp/transport.rb
38
+ - lib/epp/transport/http.rb
39
+ - lib/epp/transport/tcp.rb
20
40
  - lib/kono_epp_client.rb
41
+ - lib/require_parameters.rb
21
42
  homepage: https://github.com/ArchimediaZerogroup/kono_epp_client
22
43
  licenses:
23
44
  - MIT