transip 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|