transip 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +15 -6
- data/VERSION.yml +1 -1
- data/lib/transip.rb +215 -16
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -13,8 +13,11 @@ Credits:
|
|
13
13
|
|
14
14
|
== Install
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
Use the gem.
|
17
|
+
|
18
|
+
gem install transip
|
19
|
+
|
20
|
+
For the latest version: Download / clone the repository. Bundle install the needed gems and require the lib.
|
18
21
|
|
19
22
|
git clone git://github.com/joost/transip-api.git
|
20
23
|
bundle install
|
@@ -24,11 +27,17 @@ Download / clone the repository. Bundle install the needed gems and require the
|
|
24
27
|
|
25
28
|
For the most up-to-date documentation see the source files. Use as follows:
|
26
29
|
|
27
|
-
|
28
|
-
transip.
|
29
|
-
transip.
|
30
|
+
require 'transip'
|
31
|
+
transip = Transip.new(:username => 'api_username') # will try to determine IP (will not work behind NAT) and uses readonly mode
|
32
|
+
transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
|
33
|
+
transip.actions # => [:check_availability, :get_whois, :get_domain_names, :get_info, :get_auth_code, :get_is_locked, :register, :cancel, :transfer_with_owner_change, :transfer_without_owner_change, :set_nameservers, :set_lock, :unset_lock, :set_dns_entries, :set_owner, :set_contacts]
|
30
34
|
transip.request(:get_domain_names)
|
31
|
-
transip.request(:get_info, :domain_name => '
|
35
|
+
transip.request(:get_info, :domain_name => 'yelloyello.be')
|
36
|
+
transip.request_with_ip4_fix(:check_availability, :domain_name => 'yelloyello.be') # Will fix your IP if not correct (voodoo magic)
|
37
|
+
transip.request_with_ip4_fix(:get_info, :domain_name => 'one_of_your_domains.com')
|
38
|
+
transip.request(:get_whois, :domain_name => 'google.com')
|
39
|
+
transip.request(:set_dns_entries, :domain_name => 'bdgg.nl', :dns_entries => [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')])
|
40
|
+
transip.request(:register, Transip::Domain.new('newdomain.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
|
32
41
|
|
33
42
|
== TODO
|
34
43
|
|
data/VERSION.yml
CHANGED
data/lib/transip.rb
CHANGED
@@ -8,13 +8,21 @@ require 'digest/md5'
|
|
8
8
|
# Implements the www.transip.nl API (v2). For more info see: https://www.transip.nl/g/api/
|
9
9
|
#
|
10
10
|
# Usage:
|
11
|
-
# transip = Transip.new(
|
12
|
-
# transip = Transip.new(
|
13
|
-
# transip.generate_hash('your_api_password') # Use this to generate a authentication hash
|
14
|
-
# transip.hash = 'your_hash' # Or use this to directly set the hash (so you don't have to use your password in your code)
|
11
|
+
# transip = Transip.new(:username => 'api_username') # will try to determine IP (will not work behind NAT) and uses readonly mode
|
12
|
+
# transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
|
15
13
|
# transip.actions # => [:check_availability, :get_whois, :get_domain_names, :get_info, :get_auth_code, :get_is_locked, :register, :cancel, :transfer_with_owner_change, :transfer_without_owner_change, :set_nameservers, :set_lock, :unset_lock, :set_dns_entries, :set_owner, :set_contacts]
|
16
14
|
# transip.request(:get_domain_names)
|
17
15
|
# transip.request(:get_info, :domain_name => 'yelloyello.be')
|
16
|
+
# transip.request_with_ip4_fix(:check_availability, :domain_name => 'yelloyello.be')
|
17
|
+
# transip.request_with_ip4_fix(:get_info, :domain_name => 'one_of_your_domains.com')
|
18
|
+
# transip.request(:get_whois, :domain_name => 'google.com')
|
19
|
+
# transip.request(:set_dns_entries, :domain_name => 'bdgg.nl', :dns_entries => [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')])
|
20
|
+
# transip.request(:register, Transip::Domain.new('newdomain.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
|
21
|
+
#
|
22
|
+
# Some other methods:
|
23
|
+
# transip.generate_hash # Use this to generate a authentication hash
|
24
|
+
# transip.hash = 'your_hash' # Or use this to directly set the hash (so you don't have to use your password in your code)
|
25
|
+
# transip.client! # This returns a new Savon::Client. It is cached in transip.client so when you update your username, password or hash call this method!
|
18
26
|
#
|
19
27
|
# Credits:
|
20
28
|
# Savon Gem - See: http://savonrb.com/. Wouldn't be so simple without it!
|
@@ -22,16 +30,164 @@ class Transip
|
|
22
30
|
|
23
31
|
WSDL = 'https://api.transip.nl/wsdl/?service=DomainService'
|
24
32
|
|
25
|
-
attr_accessor :
|
33
|
+
attr_accessor :username, :password, :ip, :mode, :hash
|
26
34
|
attr_reader :response
|
27
35
|
|
36
|
+
# Following Error needs to be catched in your code!
|
37
|
+
class ApiError < RuntimeError
|
38
|
+
|
39
|
+
IP4_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
|
40
|
+
|
41
|
+
# Returns true if we have a authentication error and gets ip from error msg.
|
42
|
+
# "Wrong API credentials (bad hash); called from IP 213.86.41.114"
|
43
|
+
def ip4_authentication_error?
|
44
|
+
self.message.to_s =~ /called from IP\s(#{IP4_REGEXP})/ # "Wrong API credentials (bad hash); called from IP 213.86.41.114"
|
45
|
+
@error_msg_ip = $1
|
46
|
+
!@error_msg_ip.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the ip coming from the error msg.
|
50
|
+
def error_msg_ip
|
51
|
+
@error_msg_ip || ip4_authentication_error? && @error_msg_ip
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# Following subclasses are actually not needed (as you can also
|
57
|
+
# do the same by just creating hashes..).
|
58
|
+
|
59
|
+
class TransipStruct < Struct
|
60
|
+
|
61
|
+
# See Rails' underscore method.
|
62
|
+
def underscore(string)
|
63
|
+
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
64
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
65
|
+
tr("-", "_").
|
66
|
+
downcase
|
67
|
+
end
|
68
|
+
|
69
|
+
# Converts Transip::DnsEntry into :dns_entry
|
70
|
+
def class_name_to_sym
|
71
|
+
self.underscore(self.class.name.split('::').last).to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
# Gyoku.xml (see: https://github.com/rubiii/gyoku) is used by Savon.
|
75
|
+
# It calls to_s on unknown Objects. We use it to convert
|
76
|
+
def to_s
|
77
|
+
Gyoku.xml(self.to_hash)
|
78
|
+
end
|
79
|
+
|
80
|
+
# See what happens here: http://snippets.dzone.com/posts/show/302
|
81
|
+
def members_to_hash
|
82
|
+
Hash[*members.collect {|m| [m, self.send(m)]}.flatten]
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_hash
|
86
|
+
{ self.class_name_to_sym => self.members_to_hash }
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
# name - String (Eg. '@' or 'www')
|
92
|
+
# expire - Integer (1.day)
|
93
|
+
# type - String (Eg. A, AAAA, CNAME, MX, NS, TXT, SRV)
|
94
|
+
# content - String (Eg. '10 mail', '127.0.0.1' or 'www')
|
95
|
+
class DnsEntry < TransipStruct.new(:name, :expire, :type, :content)
|
96
|
+
end
|
97
|
+
|
98
|
+
# hostname - string
|
99
|
+
# ipv4 - string
|
100
|
+
# ipv6 - string (optional)
|
101
|
+
class Nameserver < TransipStruct.new(:name, :ipv4, :ipv6)
|
102
|
+
end
|
103
|
+
|
104
|
+
# type - string
|
105
|
+
# first_name - string
|
106
|
+
# middle_name - string
|
107
|
+
# last_name - string
|
108
|
+
# company_name - string
|
109
|
+
# company_kvk - string
|
110
|
+
# company_type - string ('BV', 'BVI/O', 'COOP', 'CV'..) (see WhoisContact.php)
|
111
|
+
# street - string
|
112
|
+
# number - string (streetnumber)
|
113
|
+
# postal_code - string
|
114
|
+
# city - string
|
115
|
+
# phone_number - string
|
116
|
+
# fax_number - string
|
117
|
+
# email - string
|
118
|
+
# country - string (one of the ISO country abbrevs, must be lowercase) ('nl', 'de', ) (see WhoisContact.php)
|
119
|
+
class WhoisContact < TransipStruct.new(:type, :first_name, :middle_name, :last_name, :company_name, :company_kvk, :company_type, :street, :number, :postal_code, :city, :phone_number, :fax_number, :email, :country)
|
120
|
+
end
|
121
|
+
|
122
|
+
# company_name - string
|
123
|
+
# support_email - string
|
124
|
+
# company_url - string
|
125
|
+
# terms_of_usage_url - string
|
126
|
+
# banner_line1 - string
|
127
|
+
# banner_line2 - string
|
128
|
+
# banner_line3 - string
|
129
|
+
class DomainBranding < TransipStruct.new(:company_name, :support_email, :company_url, :terms_of_usage_url, :banner_line1, :banner_line2, :banner_line3)
|
130
|
+
end
|
131
|
+
|
132
|
+
# name - String
|
133
|
+
# nameservers - Array of Transip::Nameserver
|
134
|
+
# contacts - Array of Transip::WhoisContact
|
135
|
+
# dns_entries - Array of Transip::DnsEntry
|
136
|
+
# branding - Transip::DomainBranding
|
137
|
+
class Domain < TransipStruct.new(:name, :nameservers, :contacts, :dns_entries, :branding)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Options:
|
141
|
+
# * username
|
142
|
+
# * ip
|
143
|
+
# * password
|
144
|
+
# * mode
|
145
|
+
#
|
28
146
|
# Example:
|
29
|
-
# transip = Transip.new(
|
30
|
-
# transip = Transip.new(
|
31
|
-
def initialize(
|
32
|
-
@
|
33
|
-
|
34
|
-
@
|
147
|
+
# transip = Transip.new(:username => 'api_username') # will try to determine IP (will not work behind NAT) and uses readonly mode
|
148
|
+
# transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
|
149
|
+
def initialize(options = {})
|
150
|
+
@username = options[:username]
|
151
|
+
raise ArgumentError, "The :username options is required!" if @username.nil?
|
152
|
+
@ip = options[:ip] || self.class.local_ip
|
153
|
+
@mode = options[:mode] || :readonly
|
154
|
+
if options[:password]
|
155
|
+
@password = options[:password]
|
156
|
+
self.generate_hash
|
157
|
+
end
|
158
|
+
|
159
|
+
# By default we don't want to debug!
|
160
|
+
self.turn_off_debugging!
|
161
|
+
end
|
162
|
+
|
163
|
+
# By default we don't want to debug!
|
164
|
+
# Changing might impact other Savon usages.
|
165
|
+
def turn_off_debugging!
|
166
|
+
Savon.configure do |config|
|
167
|
+
config.log = false # disable logging
|
168
|
+
config.log_level = :info # changing the log level
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Make Savon log.
|
173
|
+
# Changing might impact other Savon usages.
|
174
|
+
def turn_on_debugging!
|
175
|
+
Savon.configure do |config|
|
176
|
+
config.log = true
|
177
|
+
config.log_level = :debug
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Make Savon log to Rails.logger and turn_off_debugging!
|
182
|
+
def use_with_rails!
|
183
|
+
Savon.configure do |config|
|
184
|
+
if Rails.env.production?
|
185
|
+
self.turn_off_debugging!
|
186
|
+
# else
|
187
|
+
# self.turn_on_debugging!
|
188
|
+
end
|
189
|
+
config.logger = Rails.logger # using the Rails logger
|
190
|
+
end
|
35
191
|
end
|
36
192
|
|
37
193
|
# Generates the needed authentication hash.
|
@@ -39,16 +195,17 @@ class Transip
|
|
39
195
|
# NOTE: The password is NOT your general TransIP password
|
40
196
|
# but one specially for the API. Configure it in the Control
|
41
197
|
# Panel.
|
42
|
-
def generate_hash
|
43
|
-
|
198
|
+
def generate_hash
|
199
|
+
raise StandardError, "Need username and password to (re)generate the authentication hash." if self.username.nil? || self.password.nil?
|
200
|
+
digest_string = "#{self.username}:#{self.password}@#{self.ip}"
|
44
201
|
digest = Digest::MD5.hexdigest(digest_string)
|
45
|
-
|
202
|
+
self.hash = digest
|
46
203
|
end
|
47
204
|
|
48
205
|
# Used as authentication
|
49
206
|
def cookie
|
50
|
-
raise StandardError, "Don't have an authentication hash yet. Please set a hash using generate_hash
|
51
|
-
"login=#{
|
207
|
+
raise StandardError, "Don't have an authentication hash yet. Please set a hash using generate_hash or hash= method." if hash.blank?
|
208
|
+
"login=#{self.username}; hash=#{self.hash}; mode=#{self.mode}; "
|
52
209
|
end
|
53
210
|
|
54
211
|
# Same as client method but initializes a brand new fresh client.
|
@@ -86,10 +243,52 @@ class Transip
|
|
86
243
|
@response = client.request(action) do
|
87
244
|
soap.body = options
|
88
245
|
end
|
246
|
+
elsif options.class < Transip::TransipStruct
|
247
|
+
# If we call request(:register, Transip::Domain.new('newdomain.com')) we turn the Transip::Domain into a Hash.
|
248
|
+
@response = client.request(action) do
|
249
|
+
soap.body = options.to_hash
|
250
|
+
end
|
89
251
|
else
|
90
252
|
raise ArgumentError, "Expected options to be nil or a Hash!"
|
91
253
|
end
|
92
254
|
@response.to_hash
|
255
|
+
rescue Savon::SOAP::Fault => e
|
256
|
+
raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
|
257
|
+
# TODO: Curl::Err::HostResolutionError, Couldn't resolve host name
|
258
|
+
end
|
259
|
+
|
260
|
+
# This is voodoo. Use it only if you know voodoo kung-fu.
|
261
|
+
#
|
262
|
+
# The method fixes the ip that is set. It uses the error from
|
263
|
+
# Transip to set the ip and re-request an authentication hash.
|
264
|
+
#
|
265
|
+
# It only works if you set password (via the password= method)!
|
266
|
+
def request_with_ip4_fix(*args)
|
267
|
+
self.request(*args)
|
268
|
+
rescue ApiError => e
|
269
|
+
if e.ip4_authentication_error?
|
270
|
+
if !(@ip == e.error_msg_ip) # If not the same IP we try it with this IP..
|
271
|
+
self.ip = e.error_msg_ip
|
272
|
+
self.generate_hash # Generate a new authentication hash.
|
273
|
+
self.client! # Update the client with the new authentication hash in the cookie!
|
274
|
+
return self.request(*args)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
raise # If we haven't returned anything.. we raise the ApiError again.
|
278
|
+
end
|
279
|
+
|
280
|
+
private
|
281
|
+
|
282
|
+
# Find my local_ip..
|
283
|
+
def self.local_ip
|
284
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
285
|
+
|
286
|
+
UDPSocket.open do |s|
|
287
|
+
s.connect('74.125.77.147', 1) # Connects to a Google IP '74.125.77.147'.
|
288
|
+
s.addr.last
|
289
|
+
end
|
290
|
+
ensure
|
291
|
+
Socket.do_not_reverse_lookup = orig
|
93
292
|
end
|
94
293
|
|
95
294
|
end
|