transip 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/Gemfile +3 -2
  2. data/Gemfile.lock +26 -14
  3. data/README.rdoc +18 -25
  4. data/VERSION.yml +2 -2
  5. data/lib/transip.rb +239 -133
  6. metadata +68 -49
  7. data/init.rb +0 -1
data/Gemfile CHANGED
@@ -2,5 +2,6 @@ source "http://rubygems.org"
2
2
  source 'http://gemcutter.org'
3
3
  source 'http://gems.github.com'
4
4
 
5
- gem 'savon', '>= 0.9.1'
6
- gem 'curb', '>= 0.7.15' # For Savon, see https://github.com/rubiii/savon/issues/167
5
+ gem 'savon', '>= 2.3.0'
6
+ gem 'curb', '>= 0.8.4'
7
+ gem 'facets', '>= 2.9.3'
data/Gemfile.lock CHANGED
@@ -3,25 +3,37 @@ GEM
3
3
  remote: http://gemcutter.org/
4
4
  remote: http://gems.github.com/
5
5
  specs:
6
- builder (3.0.0)
7
- crack (0.1.8)
8
- curb (0.7.15)
9
- gyoku (0.4.2)
6
+ akami (1.2.0)
7
+ gyoku (>= 0.4.0)
8
+ nokogiri (>= 1.4.0)
9
+ builder (3.2.2)
10
+ curb (0.8.4)
11
+ facets (2.9.3)
12
+ gyoku (1.1.0)
10
13
  builder (>= 2.1.2)
11
- httpi (0.9.2)
12
- ntlm-http (>= 0.1.1)
14
+ httpi (2.1.0)
13
15
  rack
14
- ntlm-http (0.1.1)
15
- rack (1.2.2)
16
- savon (0.9.1)
16
+ rubyntlm (~> 0.3.2)
17
+ nokogiri (1.5.10)
18
+ nori (2.3.0)
19
+ rack (1.5.2)
20
+ rubyntlm (0.3.4)
21
+ savon (2.3.0)
22
+ akami (~> 1.2.0)
17
23
  builder (>= 2.1.2)
18
- crack (~> 0.1.8)
19
- gyoku (>= 0.4.0)
20
- httpi (>= 0.7.8)
24
+ gyoku (~> 1.1.0)
25
+ httpi (~> 2.1.0)
26
+ nokogiri (>= 1.4.0, < 1.6)
27
+ nori (~> 2.3.0)
28
+ wasabi (~> 3.2.0)
29
+ wasabi (3.2.0)
30
+ httpi (~> 2.0)
31
+ nokogiri (>= 1.4.0, < 1.6)
21
32
 
22
33
  PLATFORMS
23
34
  ruby
24
35
 
25
36
  DEPENDENCIES
26
- curb
27
- savon
37
+ curb (>= 0.8.4)
38
+ facets (>= 2.9.3)
39
+ savon (>= 2.3.0)
data/README.rdoc CHANGED
@@ -1,48 +1,41 @@
1
1
  = TransIP API
2
2
 
3
- Ruby gem to use the full TransIP API (v2).
3
+ Ruby gem to use the full TransIP API (v4.2). This fork uses an updated version of savon, and implements the new request signing method that the guys at TransIP have introduced into their api. So far, i have only tested the :get_domain_names, :get_info and :set_dns_entries calls.
4
+
5
+
6
+ The transip API makes use of public/private key encryption. You need to use the TransIP
7
+ control panel to give your server access to the api, and to generate a key. You can then
8
+ use the key together with your username to gain access to the api
4
9
 
5
10
  For more info see:
6
11
 
7
- * <b>The origin of this code:</b> https://github.com/joost/transip-api
12
+ * <b>The origin of this code:</b> https://github.com/joost/transip
8
13
  * <b>TransIP API Docs:</b> https://www.transip.nl/g/api
9
14
 
10
- Credits:
11
-
12
- * <b>Savon Gem:</b> See: http://savonrb.com. Wouldn't be so simple without it!
15
+ Credits for full rewrite to work with new TransIP API version go to Richard Bronkhorst (https://github.com/richmans).
13
16
 
14
17
  == Install
15
18
 
16
- Use the gem.
17
-
18
- gem install transip
19
-
20
19
  For the latest version: Download / clone the repository. Bundle install the needed gems and require the lib.
21
20
 
22
- git clone git://github.com/joost/transip-api.git
21
+ git clone git://github.com/joost/transip.git
22
+ cd transip
23
23
  bundle install
24
- irb # and require './transip'
24
+ irb # and require './lib/transip'
25
25
 
26
26
  == Usage
27
27
 
28
28
  For the most up-to-date documentation see the source files. Use as follows:
29
29
 
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
+ transip = Transip.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
31
+ 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]
34
32
  transip.request(:get_domain_names)
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')]))
41
-
42
- == TODO
33
+ transip.request(:get_info, :domain_name => 'example.com')
34
+ transip.request(:get_whois, :domain_name => 'example.com')
35
+ transip.request(:set_dns_entries, :domain_name => 'example.com', :dns_entries => [Transip::DnsEntry.new('test', 5.inutes, 'A', '74.125.77.147')])
36
+ transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
43
37
 
44
- * Tests
45
38
 
46
39
  Please feel free to contribute and send me a pull request via Github!
47
40
 
48
- Copyright (c) 2011 Joost Hietbrink, released under the MIT license
41
+ Copyright (c) 2013 Richard Bronkhorst, released under the MIT license
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 2
4
- :patch: 0
3
+ :minor: 3
4
+ :patch: 0
data/lib/transip.rb CHANGED
@@ -1,56 +1,37 @@
1
1
  require "rubygems"
2
2
  require "bundler/setup"
3
-
3
+ require 'securerandom'
4
4
  require 'savon'
5
5
  require 'curb'
6
- require 'digest/md5'
6
+ require 'facets'
7
+ require 'digest/sha2'
8
+ require 'base64'
9
+ require 'ipaddr'
7
10
  #
8
- # Implements the www.transip.nl API (v2). For more info see: https://www.transip.nl/g/api/
11
+ # Implements the www.transip.nl API (v4.2). For more info see: https://www.transip.nl/g/api/
9
12
  #
13
+ # The transip API makes use of public/private key encryption. You need to use the TransIP
14
+ # control panel to give your server access to the api, and to generate a key. You can then
15
+ # use the key together with your username to gain access to the api
10
16
  # Usage:
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
17
+ # transip = Transip.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
13
18
  # 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]
14
19
  # transip.request(:get_domain_names)
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!
20
+ # transip.request(:get_info, :domain_name => 'example.com')
21
+ # transip.request(:get_whois, :domain_name => 'example.com')
22
+ # transip.request(:set_dns_entries, :domain_name => 'example.com', :dns_entries => [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')])
23
+ # transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
26
24
  #
27
- # Credits:
28
- # Savon Gem - See: http://savonrb.com/. Wouldn't be so simple without it!
29
25
  class Transip
30
-
26
+ SERVICE = 'DomainService'
31
27
  WSDL = 'https://api.transip.nl/wsdl/?service=DomainService'
28
+ API_VERSION = '4.2'
32
29
 
33
30
  attr_accessor :username, :password, :ip, :mode, :hash
34
31
  attr_reader :response
35
32
 
36
33
  # Following Error needs to be catched in your code!
37
34
  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
35
  end
55
36
 
56
37
  # Following subclasses are actually not needed (as you can also
@@ -72,9 +53,9 @@ class Transip
72
53
  end
73
54
 
74
55
  # 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
56
+ # It calls to_s on unknown Objects. We use it to convert
76
57
  def to_s
77
- Gyoku.xml(self.to_hash)
58
+ Gyoku.xml(self.members_to_hash)
78
59
  end
79
60
 
80
61
  # See what happens here: http://snippets.dzone.com/posts/show/302
@@ -86,6 +67,50 @@ class Transip
86
67
  { self.class_name_to_sym => self.members_to_hash }
87
68
  end
88
69
 
70
+ def self.get_type(hash)
71
+ type = hash[:'@xsi:type'].split(":").last
72
+ raise "No type definition found in hash" if type.nil?
73
+ klass = Transip.const_get(type) rescue nil
74
+ raise "Invalid transipStruct #{type}" unless klass < TransipStruct
75
+ klass
76
+ end
77
+
78
+ def self.from_hash(hash)
79
+ begin
80
+ result = get_type(hash).new
81
+ rescue
82
+ return hash
83
+ end
84
+ hash.each do |key, value|
85
+ next if key[0] == '@'
86
+ result.send(:"#{key}=", from_soap(value))
87
+ end
88
+ result
89
+ end
90
+
91
+ def self.from_soap(input)
92
+ if input.is_a? Array
93
+ result = input.map {|value| from_soap(value)}
94
+ elsif input.is_a? Hash
95
+
96
+ if input.keys.first == :item
97
+ result = from_soap(input[:item])
98
+ elsif input[:'@xsi:type'] == 'xsd:string'
99
+ result = ''
100
+ else
101
+ result = TransipStruct.from_hash(input)
102
+ end
103
+ # this is not a transip struct
104
+ if result.is_a? Hash
105
+ result.each do |key, value|
106
+ result[key] = from_soap(value)
107
+ end
108
+ end
109
+ else
110
+ result = input
111
+ end
112
+ result
113
+ end
89
114
  end
90
115
 
91
116
  # name - String (Eg. '@' or 'www')
@@ -98,7 +123,7 @@ class Transip
98
123
  # hostname - string
99
124
  # ipv4 - string
100
125
  # ipv6 - string (optional)
101
- class Nameserver < TransipStruct.new(:name, :ipv4, :ipv6)
126
+ class Nameserver < TransipStruct.new(:hostname, :ipv4, :ipv6)
102
127
  end
103
128
 
104
129
  # type - string
@@ -134,28 +159,45 @@ class Transip
134
159
  # contacts - Array of Transip::WhoisContact
135
160
  # dns_entries - Array of Transip::DnsEntry
136
161
  # branding - Transip::DomainBranding
137
- class Domain < TransipStruct.new(:name, :nameservers, :contacts, :dns_entries, :branding)
162
+ # auth_code - String
163
+ # is_locked - boolean
164
+ # registration_date - DateTime
165
+ # renewal_date - DateTime
166
+ class Domain < TransipStruct.new(:name, :nameservers, :contacts, :dns_entries, :branding, :auth_code, :is_locked, :registration_date, :renewal_date)
167
+ end
168
+
169
+
170
+ # name - String
171
+ # price - number
172
+ # renewal_price - number
173
+ # capabilities - Array of strings
174
+ # registration_period_length - number
175
+ # cancel_time_frame - number
176
+ class Tld < TransipStruct.new(:name, :price, :renewal_price, :capabilities, :registration_period_length, :cancel_time_frame)
138
177
  end
139
178
 
140
179
  # Options:
141
- # * username
180
+ # * username
142
181
  # * ip
143
- # * password
182
+ # * key
144
183
  # * mode
145
184
  #
146
185
  # Example:
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
186
+ # transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production
149
187
  def initialize(options = {})
188
+ @key = options[:key]
150
189
  @username = options[:username]
151
- raise ArgumentError, "The :username options is required!" if @username.nil?
152
- @ip = options[:ip] || self.class.local_ip
190
+ @ip = options[:ip]
191
+ raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?
192
+
153
193
  @mode = options[:mode] || :readonly
194
+ @endpoint = options[:endpoint] || 'api.transip.nl'
154
195
  if options[:password]
155
196
  @password = options[:password]
156
- self.generate_hash
157
197
  end
158
-
198
+ @savon_options = {
199
+ :wsdl => WSDL
200
+ }
159
201
  # By default we don't want to debug!
160
202
  self.turn_off_debugging!
161
203
  end
@@ -163,59 +205,127 @@ class Transip
163
205
  # By default we don't want to debug!
164
206
  # Changing might impact other Savon usages.
165
207
  def turn_off_debugging!
166
- Savon.configure do |config|
167
- config.log = false # disable logging
168
- config.log_level = :info # changing the log level
208
+ @savon_options[:log] = false # disable logging
209
+ @savon_options[:log_level] = :info # changing the log level
210
+ end
211
+
212
+
213
+ # Make Savon log to Rails.logger and turn_off_debugging!
214
+ def use_with_rails!
215
+ if Rails.env.production?
216
+ self.turn_off_debugging!
169
217
  end
218
+ @savon_options[:logger] = Rails.logger # using the Rails logger
170
219
  end
171
220
 
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
221
+ # yes, i know, it smells bad
222
+ def convert_array_to_hash(array)
223
+ result = {}
224
+ array.each_with_index do |value, index|
225
+ result[index] = value
178
226
  end
227
+ result
179
228
  end
180
229
 
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!
230
+ def urlencode(input)
231
+ output = URI.encode_www_form_component(input)
232
+ output.gsub!('+', '%20')
233
+ output.gsub!('%7E', '~')
234
+ output
235
+ end
236
+
237
+ def serialize_parameters(parameters, key_prefix=nil)
238
+ parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
239
+ parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
240
+ if not parameters.is_a? Hash
241
+ return urlencode(parameters)
242
+ end
243
+
244
+ encoded_parameters = []
245
+ parameters.each do |key, value|
246
+ next if key.to_s == '@xsi:type'
247
+ encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
248
+ if value.is_a? Hash or value.is_a? Array or value.is_a? TransipStruct
249
+ encoded_parameters << serialize_parameters(value, encoded_key)
250
+ else
251
+ encoded_value = urlencode(value)
252
+ encoded_parameters << "#{encoded_key}=#{encoded_value}"
188
253
  end
189
- config.logger = Rails.logger # using the Rails logger
190
254
  end
255
+
256
+ encoded_parameters = encoded_parameters.join("&")
257
+ #puts encoded_parameters.split('&').join("\n")
258
+ encoded_parameters
191
259
  end
192
260
 
193
- # Generates the needed authentication hash.
194
- #
195
- # NOTE: The password is NOT your general TransIP password
196
- # but one specially for the API. Configure it in the Control
197
- # Panel.
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}"
201
- digest = Digest::MD5.hexdigest(digest_string)
202
- self.hash = digest
261
+
262
+ # does all the techy stuff to calculate transip's sick authentication scheme:
263
+ # a hash with all the request information is subsequently:
264
+ # serialized like a www form
265
+ # SHA512 digested
266
+ # asn1 header added
267
+ # private key encrypted
268
+ # Base64 encoded
269
+ # URL encoded
270
+ # I think the guys at transip were trying to use their entire crypto-toolbox!
271
+ def signature(method, parameters, time, nonce)
272
+ formatted_method = method.to_s.lower_camelcase
273
+ parameters ||= {}
274
+ input = convert_array_to_hash(parameters.values)
275
+ options = {
276
+ '__method' => formatted_method,
277
+ '__service' => SERVICE,
278
+ '__hostname' => @endpoint,
279
+ '__timestamp' => time,
280
+ '__nonce' => nonce
281
+
282
+ }
283
+ input.merge!(options)
284
+ raise "Invalid RSA key" unless @key =~ /-----BEGIN RSA PRIVATE KEY-----(.*)-----END RSA PRIVATE KEY-----/sim
285
+ serialized_input = serialize_parameters(input)
286
+
287
+ digest = Digest::SHA512.new.digest(serialized_input)
288
+ asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
289
+ asn = asn_header + digest
290
+ private_key = OpenSSL::PKey::RSA.new(@key)
291
+ encrypted_asn = private_key.private_encrypt(asn)
292
+ readable_encrypted_asn = Base64.encode64(encrypted_asn)
293
+ urlencode(readable_encrypted_asn)
203
294
  end
204
295
 
205
- # Used as authentication
206
- def cookie
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}; "
296
+ def to_cookies(content)
297
+ content.map do |item|
298
+ HTTPI::Cookie.new item
299
+ end
300
+ end
301
+
302
+
303
+ # Used for authentication
304
+ def cookies(method, parameters)
305
+ time = Time.new.to_i
306
+ #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
307
+ nonce = SecureRandom.uuid.gsub("-", '')
308
+ result = to_cookies [ "login=#{self.username}",
309
+ "mode=#{self.mode}",
310
+ "timestamp=#{time}",
311
+ "nonce=#{nonce}",
312
+ "clientVersion=#{API_VERSION}",
313
+ "signature=#{signature(method, parameters, time, nonce)}"
314
+
315
+ ]
316
+ #puts signature(method, parameters, time, nonce)
317
+ result
209
318
  end
210
319
 
211
320
  # Same as client method but initializes a brand new fresh client.
212
321
  # You have to use this one when you want to re-set the mode (readwrite, readonly),
213
322
  # or authentication details of your client.
214
323
  def client!
215
- @client = Savon::Client.new do
216
- wsdl.document = WSDL
324
+ @client = Savon::Client.new(@savon_options) do
325
+ namespaces(
326
+ "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
327
+ )
217
328
  end
218
- @client.http.headers["Cookie"] = cookie
219
329
  return @client
220
330
  end
221
331
 
@@ -230,65 +340,61 @@ class Transip
230
340
  client.wsdl.soap_actions
231
341
  end
232
342
 
233
- # Returns the response.to_hash (raw Savon::SOAP::Response is also stored in @response).
234
- # Examples:
235
- # hash_response = transip.request(:get_domain_names)
236
- # hash_response[:get_domain_names_response][:return][:item] # => ["your.domain", "names.list"]
237
- # For more info see the Transip API docs.
238
- # Be sure to rescue all the errors.. since it is hardcore error throwing.
239
- def request(action, options = nil)
240
- if options.nil?
241
- @response = client.request(action)
242
- elsif options.is_a?(Hash)
243
- @response = client.request(action) do
244
- soap.body = options
343
+ # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
344
+ def fix_array_definitions(options)
345
+ result = {}
346
+ options.each do |key, value|
347
+ if value.is_a? Array and value.size > 0
348
+ entry_name = value.first.class.name.split(":").last
349
+ result[key] = {
350
+ 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
351
+ :'@xsi:type' => "tns:ArrayOf#{entry_name}",
352
+ :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
353
+ }
354
+ else
355
+ result[key] = value
245
356
  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
251
- else
252
- raise ArgumentError, "Expected options to be nil or a Hash!"
253
357
  end
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
358
+ result
258
359
  end
259
360
 
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
361
 
280
- private
362
+ # converts the savon response object to something we can return to the caller
363
+ # - A TransipStruct object
364
+ # - An array of TransipStructs
365
+ # - nil
366
+ def process_response(response)
367
+ response = response.to_hash.values.first[:return] rescue nil
368
+ TransipStruct.from_soap(response)
281
369
 
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
370
+ end
285
371
 
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
372
+ # This is the main request function
373
+ # throws ApiError
374
+ # returns response object (can be TransipStruct or Array of TransipStruct)
375
+ def request(action, options = nil)
376
+ formatted_action = action.to_s.lower_camelcase
377
+ parameters = {
378
+ # for some reason, the transip server wants the body root tag to be
379
+ # the name of the action.
380
+ :message_tag => formatted_action
381
+ }
382
+ options = options.to_hash if options.is_a? Transip::TransipStruct
383
+
384
+ if options.is_a? Hash
385
+ xml_options = fix_array_definitions(options)
386
+ elsif options.nil?
387
+ xml_options = nil
388
+ else
389
+ raise "Invalid parameter format (should be nil, hash or TransipStruct"
289
390
  end
290
- ensure
291
- Socket.do_not_reverse_lookup = orig
292
- end
391
+ parameters[:message] = xml_options
392
+ parameters[:cookies] = cookies(action, options)
393
+ #puts parameters.inspect
394
+ response = client.call(action, parameters)
293
395
 
396
+ process_response(response)
397
+ rescue Savon::SOAPFault => e
398
+ raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
399
+ end
294
400
  end
metadata CHANGED
@@ -1,84 +1,103 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: transip
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
4
5
  prerelease:
5
- version: 0.2.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Joost Hietbrink
9
+ - Richard Bronkhorst
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
-
13
- date: 2011-03-13 00:00:00 +01:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
13
+ date: 2013-09-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
17
16
  name: savon
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
20
18
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: 0.9.1
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.3.0
25
23
  type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 2.3.0
31
+ - !ruby/object:Gem::Dependency
28
32
  name: curb
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.8.4
39
+ type: :runtime
29
40
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
41
+ version_requirements: !ruby/object:Gem::Requirement
31
42
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: 0.7.15
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.8.4
47
+ - !ruby/object:Gem::Dependency
48
+ name: facets
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.9.3
36
55
  type: :runtime
37
- version_requirements: *id002
38
- description: Ruby gem to use the full TransIP API (v2).
39
- email: joost@joopp.com
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: 2.9.3
63
+ description: Ruby gem to use the full TransIP API (v4.2).
64
+ email:
40
65
  executables: []
41
-
42
66
  extensions: []
43
-
44
- extra_rdoc_files:
67
+ extra_rdoc_files:
45
68
  - MIT-LICENSE
46
69
  - README.rdoc
47
- files:
70
+ files:
48
71
  - MIT-LICENSE
49
72
  - README.rdoc
50
73
  - VERSION.yml
51
74
  - Gemfile
52
75
  - Gemfile.lock
53
- - init.rb
54
76
  - lib/transip.rb
55
- has_rdoc: true
56
- homepage: http://github.com/joost/transip-api
77
+ homepage: http://github.com/richmans/transip-api
57
78
  licenses: []
58
-
59
79
  post_install_message:
60
- rdoc_options:
80
+ rdoc_options:
61
81
  - --charset=UTF-8
62
- require_paths:
82
+ require_paths:
63
83
  - lib
64
- required_ruby_version: !ruby/object:Gem::Requirement
84
+ required_ruby_version: !ruby/object:Gem::Requirement
65
85
  none: false
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: "0"
70
- required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
91
  none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: "0"
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
76
96
  requirements: []
77
-
78
97
  rubyforge_project:
79
- rubygems_version: 1.5.0
98
+ rubygems_version: 1.8.25
80
99
  signing_key:
81
100
  specification_version: 3
82
- summary: Ruby gem to use the full TransIP API (v2).
101
+ summary: Ruby gem to use the full TransIP API (v4.2).
83
102
  test_files: []
84
-
103
+ has_rdoc: true
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'transip-api'