transip 0.3.7 → 0.4.1

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/.gitignore CHANGED
@@ -1 +1,5 @@
1
- *.gem
1
+ *.gem
2
+ .DS_Store
3
+ .bundle/
4
+ vendor/
5
+ tmp/
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
- source "http://rubygems.org"
2
- source 'http://gemcutter.org'
3
- source 'http://gems.github.com'
1
+ source 'https://rubygems.org'
4
2
 
5
3
  gem 'savon', '>= 2.3.0'
6
4
  gem 'curb', '>= 0.8.4'
@@ -1,19 +1,17 @@
1
1
  = TransIP API
2
2
 
3
- Ruby gem to use the full TransIP API (v4.2).
4
- 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.
3
+ Ruby gem to use the full TransIP (www.transip.nl) API (v5.0).
4
+ Uses an updated version of savon and implements the new request signing method that the guys at TransIP have introduced into their API.
5
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
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.
9
9
 
10
10
  For more info see:
11
11
 
12
12
  * <b>The origin of this code:</b> https://github.com/joost/transip
13
13
  * <b>TransIP API Docs:</b> https://www.transip.nl/g/api
14
14
 
15
- Credits for full rewrite to work with new TransIP API version go to Richard Bronkhorst (https://github.com/richmans).
16
-
17
15
  == Install
18
16
 
19
17
  Install from rubygems (rubygems.org/gems/transip):
@@ -26,7 +24,7 @@ or add in your Bundle Gemfile:
26
24
 
27
25
  For the latest version: Download / clone the repository. Bundle install the needed gems and require the lib.
28
26
 
29
- git clone git://github.com/joost/transip.git
27
+ git clone https://github.com/joost/transip.git
30
28
  cd transip
31
29
  bundle install
32
30
  irb # and require './lib/transip'
@@ -46,19 +44,47 @@ For the most up-to-date documentation see the source files.
46
44
  Setup the API client:
47
45
 
48
46
  # use this in production
49
- transip = Transip.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
47
+ transip = Transip::DomainClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
48
+
49
+ You can use Transip::DomainClient, Transip::VpsClient, Transip::ColocationClient, Transip::WebhostingClient and Transip::ForwardClient.
50
50
 
51
51
  In development you can leave out the ip. To test request use :readonly mode.
52
52
 
53
+ If you store your private key in a seperate file you can do:
54
+
55
+ transip = Transip::DomainClient.new(username: 'your_username', key_file: 'path_to_your_private_key_file', ip: '12.34.12.3', mode: :readwrite)
56
+
57
+ === DomainClient
58
+
59
+ transip = Transip::DomainClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
60
+
53
61
  transip.actions # => [:batch_check_availability, :check_availability, :get_whois, :get_domain_names, :get_info, :batch_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, :get_all_tld_infos, :get_tld_info, :get_current_domain_action, :retry_current_domain_action_with_new_data, :retry_transfer_with_different_auth_code, :cancel_domain_action]
54
62
 
55
63
  transip.request(:get_domain_names)
56
64
  transip.request(:get_info, :domain_name => 'example.com')
57
65
  transip.request(:get_whois, :domain_name => 'example.com')
58
66
  transip.request(:set_dns_entries, :domain_name => 'example.com', :dns_entries => [Transip::DnsEntry.new('test', 5.inutes, 'A', '74.125.77.147')])
59
- transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
67
+ transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 300, 'A', '74.125.77.147')]))
60
68
  transip.request(:set_contacts, :domain_name => 'example.com', :contacts => [Transip::WhoisContact.new('type', 'first', 'middle', 'last', 'company', 'kvk', 'companyType', 'street', 'number', 'postalCode', 'city', 'phoneNumber', 'faxNumber', 'email', 'country')])
61
69
 
70
+ === VpsClient
71
+
72
+ transip_vps = Transip::VpsClient.new(username: 'your_username', key: 'your_private_rsa_key', ip: '12.34.12.3', mode: :readwrite)
73
+
74
+ transip_vps.actions # => [:get_available_products, :get_available_addons, :get_active_addons_for_vps, :get_available_upgrades, :get_available_addons_for_vps, :get_cancellable_addons_for_vps, :order_vps, :order_addon, :order_private_network, :upgrade_vps, :cancel_vps, :cancel_addon, :cancel_private_network, :get_private_networks_by_vps, :get_all_private_networks, :add_vps_to_private_network, :remove_vps_from_private_network, :get_amount_of_traffic_used, :start, :stop, :reset, :create_snapshot, :revert_snapshot, :remove_snapshot, :get_vps, :get_vpses, :get_snapshots_by_vps, :get_operating_systems, :install_operating_system, :get_ips_for_vps, :get_all_ips, :add_ipv6_to_vps, :update_ptr_record]
75
+
76
+ transip_vps.request(:get_available_products)
77
+ transip_vps.request(:get_operating_systems)
78
+ transip_vps.request(:order_vps, product_name: 'vps-bladevps-x1', addons: nil, operating_system_name: 'ubuntu-12.04.1-transip', hostname: 'my.hostname.com')
79
+
80
+ === ColocationClient, WebhostingClient and ForwardClient
81
+
82
+ Same as above.
83
+
84
+ Find out full methods and parameters by going throught the PHP sources of the TransIP API. You can find them via www.transip.nl.
85
+
86
+ == Contribute and licence
87
+
62
88
  Please feel free to contribute and send me a pull request via Github!
63
89
 
64
- Copyright (c) 2013 Richard Bronkhorst, released under the MIT license
90
+ (C)opyright 2014 Joost Hietbrink / Richard Bronkhorst, released under the MIT license.
@@ -9,14 +9,17 @@ require 'base64'
9
9
  require 'ipaddr'
10
10
 
11
11
  require File.expand_path '../transip/version', __FILE__
12
+ require File.expand_path '../transip/client', __FILE__
13
+ require File.expand_path '../transip/api_error', __FILE__
14
+
12
15
  #
13
- # Implements the www.transip.nl API (v4.2). For more info see: https://www.transip.nl/g/api/
16
+ # Implements the www.transip.nl API (v5.0). For more info see: https://www.transip.nl/g/api/
14
17
  #
15
18
  # The transip API makes use of public/private key encryption. You need to use the TransIP
16
19
  # control panel to give your server access to the api, and to generate a key. You can then
17
20
  # use the key together with your username to gain access to the api
18
21
  # Usage:
19
- # transip = Transip.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
22
+ # transip = Transip::DomainClient.new(:username => 'api_username', :key => private_key, :ip => '12.34.12.3', :mode => 'readwrite') # use this in production
20
23
  # 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]
21
24
  # transip.request(:get_domain_names)
22
25
  # transip.request(:get_info, :domain_name => 'example.com')
@@ -25,16 +28,13 @@ require File.expand_path '../transip/version', __FILE__
25
28
  # transip.request(:set_contacts, :domain_name => 'example.com', :contacts => [Transip::WhoisContact.new('type', 'first', 'middle', 'last', 'company', 'kvk', 'companyType', 'street','number','postalCode','city','phoneNumber','faxNumber','email','country')])
26
29
  # transip.request(:register, Transip::Domain.new('example.com', nil, nil, [Transip::DnsEntry.new('test', 5.minutes, 'A', '74.125.77.147')]))
27
30
  #
28
- class Transip
29
- SERVICE = 'DomainService'
30
- WSDL = 'https://api.transip.nl/wsdl/?service=DomainService'
31
- API_VERSION = '4.2'
32
-
33
- attr_accessor :username, :password, :ip, :mode, :hash
34
- attr_reader :response
31
+ module Transip
35
32
 
36
- # Following Error needs to be catched in your code!
37
- class ApiError < RuntimeError
33
+ # Backwards compatibility with v3.x of the gem.
34
+ # TODO: Remove
35
+ def self.new(*args)
36
+ puts "Transip.new is deprecated. Use Transip::DomainClient.new instead!"
37
+ Client.new(*args)
38
38
  end
39
39
 
40
40
  # Following subclasses are actually not needed (as you can also
@@ -70,7 +70,7 @@ class Transip
70
70
 
71
71
  # See what happens here: http://snippets.dzone.com/posts/show/302
72
72
  def members_to_hash
73
- Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten]
73
+ Hash[*members.collect {|m| [member_name_to_camel(m), self.send(m)]}.flatten(1)]
74
74
  end
75
75
 
76
76
  def to_hash
@@ -186,231 +186,13 @@ class Transip
186
186
  class Tld < TransipStruct.new(:name, :price, :renewal_price, :capabilities, :registration_period_length, :cancel_time_frame)
187
187
  end
188
188
 
189
- # Options:
190
- # * username
191
- # * ip
192
- # * key
193
- # * mode
194
- #
195
- # Example:
196
- # transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production
197
- def initialize(options = {})
198
- @key = options[:key]
199
- @username = options[:username]
200
- @ip = options[:ip]
201
- raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?
202
-
203
- @mode = options[:mode] || :readonly
204
- @endpoint = options[:endpoint] || 'api.transip.nl'
205
- if options[:password]
206
- @password = options[:password]
207
- end
208
- @savon_options = {
209
- :wsdl => WSDL
210
- }
211
- # By default we don't want to debug!
212
- self.turn_off_debugging!
213
- end
214
-
215
- # By default we don't want to debug!
216
- # Changing might impact other Savon usages.
217
- def turn_off_debugging!
218
- @savon_options[:log] = false # disable logging
219
- @savon_options[:log_level] = :info # changing the log level
220
- end
221
-
222
-
223
- # Make Savon log to Rails.logger and turn_off_debugging!
224
- def use_with_rails!
225
- if Rails.env.production?
226
- self.turn_off_debugging!
227
- end
228
- @savon_options[:logger] = Rails.logger # using the Rails logger
229
- end
230
-
231
- # yes, i know, it smells bad
232
- def convert_array_to_hash(array)
233
- result = {}
234
- array.each_with_index do |value, index|
235
- result[index] = value
236
- end
237
- result
238
- end
239
-
240
- def urlencode(input)
241
- output = URI.encode_www_form_component(input)
242
- output.gsub!('+', '%20')
243
- output.gsub!('%7E', '~')
244
- output
245
- end
246
-
247
- def serialize_parameters(parameters, key_prefix=nil)
248
- parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
249
- parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
250
- if not parameters.is_a? Hash
251
- return urlencode(parameters)
252
- end
253
-
254
- encoded_parameters = []
255
- parameters.each do |key, value|
256
- next if key.to_s == '@xsi:type'
257
- encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
258
- if value.is_a? Hash or value.is_a? Array or value.is_a? TransipStruct
259
- encoded_parameters << serialize_parameters(value, encoded_key)
260
- else
261
- encoded_value = urlencode(value)
262
- encoded_parameters << "#{encoded_key}=#{encoded_value}"
263
- end
264
- end
265
-
266
- encoded_parameters = encoded_parameters.join("&")
267
- #puts encoded_parameters.split('&').join("\n")
268
- encoded_parameters
269
- end
270
-
271
-
272
- # does all the techy stuff to calculate transip's sick authentication scheme:
273
- # a hash with all the request information is subsequently:
274
- # serialized like a www form
275
- # SHA512 digested
276
- # asn1 header added
277
- # private key encrypted
278
- # Base64 encoded
279
- # URL encoded
280
- # I think the guys at transip were trying to use their entire crypto-toolbox!
281
- def signature(method, parameters, time, nonce)
282
- formatted_method = method.to_s.lower_camelcase
283
- parameters ||= {}
284
- input = convert_array_to_hash(parameters.values)
285
- options = {
286
- '__method' => formatted_method,
287
- '__service' => SERVICE,
288
- '__hostname' => @endpoint,
289
- '__timestamp' => time,
290
- '__nonce' => nonce
291
-
292
- }
293
- input.merge!(options)
294
- raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim
295
- serialized_input = serialize_parameters(input)
296
-
297
- digest = Digest::SHA512.new.digest(serialized_input)
298
- asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
299
-
300
- # convert asn_header literal to ASCII-8BIT
301
- if RUBY_VERSION.split('.')[0] == "2"
302
- asn = asn_header.b + digest
303
- else
304
- asn = asn_header + digest
305
- end
306
- private_key = OpenSSL::PKey::RSA.new(@key)
307
- encrypted_asn = private_key.private_encrypt(asn)
308
- readable_encrypted_asn = Base64.encode64(encrypted_asn)
309
- urlencode(readable_encrypted_asn)
310
- end
311
-
312
- def to_cookies(content)
313
- content.map do |item|
314
- HTTPI::Cookie.new item
315
- end
316
- end
317
-
318
-
319
- # Used for authentication
320
- def cookies(method, parameters)
321
- time = Time.new.to_i
322
- #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
323
- nonce = SecureRandom.uuid.gsub("-", '')
324
- result = to_cookies [ "login=#{self.username}",
325
- "mode=#{self.mode}",
326
- "timestamp=#{time}",
327
- "nonce=#{nonce}",
328
- "clientVersion=#{API_VERSION}",
329
- "signature=#{signature(method, parameters, time, nonce)}"
330
-
331
- ]
332
- #puts signature(method, parameters, time, nonce)
333
- result
334
- end
335
-
336
- # Same as client method but initializes a brand new fresh client.
337
- # You have to use this one when you want to re-set the mode (readwrite, readonly),
338
- # or authentication details of your client.
339
- def client!
340
- @client = Savon::Client.new(@savon_options) do
341
- namespaces(
342
- "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
343
- )
344
- end
345
- return @client
346
- end
189
+ # VPS related methods
190
+ # Available from TransIp v5.0.
347
191
 
348
- # Returns a Savon::Client object to be used in the connection.
349
- # This object is re-used and cached as @client.
350
- def client
351
- @client ||= client!
192
+ class Vps < TransipStruct.new(:name, :description, :operating_system, :disk_size, :memory_size, :cpus, :status, :ip_address, :vnc_hostname, :vnc_port_number, :vnc_password, :is_blocked, :is_customer_locked)
352
193
  end
353
194
 
354
- # Returns Array with all possible SOAP WSDL actions.
355
- def actions
356
- client.operations
195
+ class VpsService < TransipStruct.new(:name, :description, :operating_system, :disk_size, :memory_size, :cpus, :status, :ip_address, :vnc_hostname, :vnc_port_number, :vnc_password, :is_blocked, :is_customer_locked)
357
196
  end
358
197
 
359
- # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
360
- def fix_array_definitions(options)
361
- result = {}
362
- options.each do |key, value|
363
- if value.is_a? Array and value.size > 0
364
- entry_name = value.first.class.name.split(":").last
365
- result[key] = {
366
- 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
367
- :'@xsi:type' => "tns:ArrayOf#{entry_name}",
368
- :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
369
- }
370
- else
371
- result[key] = value
372
- end
373
- end
374
- result
375
- end
376
-
377
-
378
- # converts the savon response object to something we can return to the caller
379
- # - A TransipStruct object
380
- # - An array of TransipStructs
381
- # - nil
382
- def process_response(response)
383
- response = response.to_hash.values.first[:return] rescue nil
384
- TransipStruct.from_soap(response)
385
-
386
- end
387
-
388
- # This is the main request function
389
- # throws ApiError
390
- # returns response object (can be TransipStruct or Array of TransipStruct)
391
- def request(action, options = nil)
392
- formatted_action = action.to_s.lower_camelcase
393
- parameters = {
394
- # for some reason, the transip server wants the body root tag to be
395
- # the name of the action.
396
- :message_tag => formatted_action
397
- }
398
- options = options.to_hash if options.is_a? Transip::TransipStruct
399
-
400
- if options.is_a? Hash
401
- xml_options = fix_array_definitions(options)
402
- elsif options.nil?
403
- xml_options = nil
404
- else
405
- raise "Invalid parameter format (should be nil, hash or TransipStruct"
406
- end
407
- parameters[:message] = xml_options
408
- parameters[:cookies] = cookies(action, options)
409
- #puts parameters.inspect
410
- response = client.call(action, parameters)
411
-
412
- process_response(response)
413
- rescue Savon::SOAPFault => e
414
- raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
415
- end
416
- end
198
+ end
@@ -0,0 +1,5 @@
1
+ module Transip
2
+ # Following Error needs to be catched in your code!
3
+ class ApiError < RuntimeError
4
+ end
5
+ end
@@ -0,0 +1,285 @@
1
+ module Transip
2
+
3
+ class Client
4
+
5
+ API_VERSION = '5.0'
6
+ API_SERVICE = 'DomainService'
7
+
8
+ attr_accessor :username, :password, :ip, :mode, :hash
9
+ attr_reader :response
10
+
11
+ def api_version
12
+ # We use self.class:: here to not use parentclass constant.
13
+ @api_version || self.class::API_VERSION
14
+ end
15
+
16
+ def api_service
17
+ @api_service || self.class::API_SERVICE
18
+ end
19
+
20
+ def wsdl
21
+ "https://api.transip.nl/wsdl/?service=#{api_service}"
22
+ end
23
+
24
+ attr_accessor :debug
25
+
26
+ # Options:
27
+ # * username - Your login name on the TransIP website.
28
+ # * ip - needed in production
29
+ # * key / key_file - key is one of your private keys (these can be requested via your Controlpanel). key_file is path to file containing key.
30
+ # * mode - :readonly, :readwrite
31
+ #
32
+ # Example:
33
+ # transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite') # use this in production
34
+ def initialize(options = {})
35
+ @key = options[:key] || (options[:key_file] && File.read(options[:key_file]))
36
+ @username = options[:username]
37
+ @ip = options[:ip]
38
+ @api_version = options[:api_version]
39
+ @api_service = options[:api_service]
40
+ raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil?
41
+
42
+ @mode = options[:mode] || :readonly
43
+ @endpoint = options[:endpoint] || 'api.transip.nl'
44
+ if options[:password]
45
+ @password = options[:password]
46
+ end
47
+ @savon_options = {
48
+ :wsdl => wsdl
49
+ }
50
+ # By default we don't want to debug!
51
+ self.turn_off_debugging!
52
+ end
53
+
54
+ # By default we don't want to debug!
55
+ # Changing might impact other Savon usages.
56
+ def turn_off_debugging!
57
+ @savon_options[:log] = false # disable logging
58
+ @savon_options[:log_level] = :info # changing the log level
59
+ end
60
+
61
+ # Make Savon log to Rails.logger and turn_off_debugging!
62
+ def use_with_rails!
63
+ if Rails.env.production?
64
+ self.turn_off_debugging!
65
+ end
66
+ @savon_options[:logger] = Rails.logger # using the Rails logger
67
+ end
68
+
69
+ # yes, i know, it smells bad
70
+ def convert_array_to_hash(array)
71
+ result = {}
72
+ array.each_with_index do |value, index|
73
+ result[index] = value
74
+ end
75
+ result
76
+ end
77
+
78
+ def urlencode(input)
79
+ output = URI.encode_www_form_component(input)
80
+ output.gsub!('+', '%20')
81
+ output.gsub!('%7E', '~')
82
+ output
83
+ end
84
+
85
+ def serialize_parameters(parameters, key_prefix=nil)
86
+ debug_log("serialize_parameters(#{parameters.inspect}, #{key_prefix.inspect}")
87
+
88
+ parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct
89
+ parameters = convert_array_to_hash(parameters) if parameters.is_a? Array
90
+ if not parameters.is_a? Hash
91
+ return urlencode(parameters)
92
+ end
93
+ return "#{key_prefix}=" if parameters.empty?
94
+
95
+ encoded_parameters = []
96
+ parameters.each do |key, value|
97
+ next if key.to_s == '@xsi:type'
98
+ encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]"
99
+ if value.is_a?(Hash) or value.is_a?(Array) or value.is_a?(TransipStruct)
100
+ encoded_parameters << serialize_parameters(value, encoded_key)
101
+ else
102
+ encoded_value = urlencode(value)
103
+ encoded_parameters << "#{encoded_key}=#{encoded_value}"
104
+ end
105
+ end
106
+
107
+ encoded_parameters = encoded_parameters.join("&")
108
+ debug_log("encoded_parameters:\n#{encoded_parameters.split('&').join("\n")}")
109
+ encoded_parameters
110
+ end
111
+
112
+ # does all the techy stuff to calculate transip's sick authentication scheme:
113
+ # a hash with all the request information is subsequently:
114
+ # serialized like a www form
115
+ # SHA512 digested
116
+ # asn1 header added
117
+ # private key encrypted
118
+ # Base64 encoded
119
+ # URL encoded
120
+ # I think the guys at transip were trying to use their entire crypto-toolbox!
121
+ def signature(method, parameters, time, nonce)
122
+ formatted_method = method.to_s.lower_camelcase
123
+ parameters ||= {}
124
+ input = convert_array_to_hash(parameters.values)
125
+ options = {
126
+ '__method' => formatted_method,
127
+ '__service' => api_service,
128
+ '__hostname' => @endpoint,
129
+ '__timestamp' => time,
130
+ '__nonce' => nonce
131
+
132
+ }
133
+ input.merge!(options)
134
+ raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim
135
+ serialized_input = serialize_parameters(input)
136
+
137
+ digest = Digest::SHA512.new.digest(serialized_input)
138
+ asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
139
+
140
+ # convert asn_header literal to ASCII-8BIT
141
+ if RUBY_VERSION.split('.')[0] == "2"
142
+ asn = asn_header.b + digest
143
+ else
144
+ asn = asn_header + digest
145
+ end
146
+ private_key = OpenSSL::PKey::RSA.new(@key)
147
+ encrypted_asn = private_key.private_encrypt(asn)
148
+ readable_encrypted_asn = Base64.encode64(encrypted_asn)
149
+ urlencode(readable_encrypted_asn)
150
+ end
151
+
152
+ def to_cookies(content)
153
+ content.map do |item|
154
+ HTTPI::Cookie.new item
155
+ end
156
+ end
157
+
158
+ # Used for authentication
159
+ def cookies(method, parameters)
160
+ time = Time.new.to_i
161
+ #strip out the -'s because transip requires the nonce to be between 6 and 32 chars
162
+ nonce = SecureRandom.uuid.gsub("-", '')
163
+ result = to_cookies [ "login=#{self.username}",
164
+ "mode=#{self.mode}",
165
+ "timestamp=#{time}",
166
+ "nonce=#{nonce}",
167
+ "clientVersion=#{api_version}",
168
+ "signature=#{signature(method, parameters, time, nonce)}"
169
+
170
+ ]
171
+ debug_log("signature:\n#{signature(method, parameters, time, nonce)}")
172
+ result
173
+ end
174
+
175
+ # Same as client method but initializes a brand new fresh client.
176
+ # You have to use this one when you want to re-set the mode (readwrite, readonly),
177
+ # or authentication details of your client.
178
+ def client!
179
+ @client = Savon::Client.new(@savon_options) do
180
+ namespaces(
181
+ "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/"
182
+ )
183
+ end
184
+ return @client
185
+ end
186
+
187
+ # Returns a Savon::Client object to be used in the connection.
188
+ # This object is re-used and cached as @client.
189
+ def client
190
+ @client ||= client!
191
+ end
192
+
193
+ # Returns Array with all possible SOAP WSDL actions.
194
+ def actions
195
+ client.operations
196
+ end
197
+
198
+ # This makes sure that arrays are properly encoded as soap-arrays by Gyoku
199
+ def fix_array_definitions(options)
200
+ result = {}
201
+ options.each do |key, value|
202
+ if value.is_a?(Array) and (value.size > 0)
203
+ entry_name = value.first.class.name.split(":").last
204
+ result[key] = {
205
+ 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"},
206
+ :'@xsi:type' => "tns:ArrayOf#{entry_name}",
207
+ :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]"
208
+ }
209
+ elsif value.is_a?(Hash)
210
+ result[key] = fix_array_definitions(value)
211
+ else
212
+ result[key] = value
213
+ end
214
+ end
215
+ result
216
+ end
217
+
218
+ # converts the savon response object to something we can return to the caller
219
+ # - A TransipStruct object
220
+ # - An array of TransipStructs
221
+ # - nil
222
+ def process_response(response)
223
+ response = response.to_hash.values.first[:return] rescue nil
224
+ TransipStruct.from_soap(response)
225
+ end
226
+
227
+ # This is the main request function
228
+ # throws ApiError
229
+ # returns response object (can be TransipStruct or Array of TransipStruct)
230
+ def request(action, options = nil)
231
+ formatted_action = action.to_s.lower_camelcase
232
+ parameters = {
233
+ # for some reason, the transip server wants the body root tag to be
234
+ # the name of the action.
235
+ :message_tag => formatted_action
236
+ }
237
+ options = options.to_hash if options.is_a?(Transip::TransipStruct)
238
+
239
+ if options.is_a?(Hash)
240
+ xml_options = fix_array_definitions(options)
241
+ elsif options.nil?
242
+ xml_options = nil
243
+ else
244
+ raise "Invalid parameter format (should be nil, hash or TransipStruct)"
245
+ end
246
+ parameters[:message] = xml_options
247
+ parameters[:cookies] = cookies(action, options)
248
+ debug_log("parameters:\n#{parameters.inspect}")
249
+ response = client.call(action, parameters)
250
+
251
+ process_response(response)
252
+ rescue Savon::SOAPFault => e
253
+ raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?).
254
+ end
255
+
256
+ private
257
+
258
+ def debug_log(msg)
259
+ puts msg if @debug
260
+ end
261
+
262
+ end
263
+
264
+ # 'Aliased' by Transip::Client.
265
+ class DomainClient < Client;end
266
+
267
+ # We name it VpsClient instead of VpsService since the latter is already in use by
268
+ # the TransipStruct.
269
+ class VpsClient < Client
270
+ API_SERVICE = 'VpsService'
271
+ end
272
+
273
+ class ColocationClient < Client
274
+ API_SERVICE = 'ColocationService'
275
+ end
276
+
277
+ class WebhostingClient < Client
278
+ API_SERVICE = 'WebhostingService'
279
+ end
280
+
281
+ class ForwardClient < Client
282
+ API_SERVICE = 'ForwardService'
283
+ end
284
+
285
+ end
@@ -1,3 +1,3 @@
1
- class Transip
2
- VERSION = "0.3.7"
1
+ module Transip
2
+ VERSION = "0.4.1"
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Transip::VERSION
9
9
  spec.authors = ["Joost Hietbrink", "Richard Bronkhorst"]
10
10
  spec.email = ["joost@joopp.com"]
11
- spec.description = %q{Ruby gem to use the full TransIP API (v4.2).}
12
- spec.summary = %q{Ruby gem to use the full TransIP API (v4.2).}
11
+ spec.description = %q{Ruby gem to use the full TransIP API (v5.0).}
12
+ spec.summary = %q{Ruby gem to use the full TransIP API (v5.0).}
13
13
  spec.homepage = %q{http://github.com/joost/transip}
14
14
  spec.license = "MIT"
15
15
 
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.1
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Joost Hietbrink
@@ -9,11 +10,12 @@ authors:
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-12-16 00:00:00.000000000 Z
13
+ date: 2014-02-25 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: savon
16
17
  requirement: !ruby/object:Gem::Requirement
18
+ none: false
17
19
  requirements:
18
20
  - - ! '>='
19
21
  - !ruby/object:Gem::Version
@@ -21,6 +23,7 @@ dependencies:
21
23
  type: :runtime
22
24
  prerelease: false
23
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
24
27
  requirements:
25
28
  - - ! '>='
26
29
  - !ruby/object:Gem::Version
@@ -28,6 +31,7 @@ dependencies:
28
31
  - !ruby/object:Gem::Dependency
29
32
  name: curb
30
33
  requirement: !ruby/object:Gem::Requirement
34
+ none: false
31
35
  requirements:
32
36
  - - ! '>='
33
37
  - !ruby/object:Gem::Version
@@ -35,6 +39,7 @@ dependencies:
35
39
  type: :runtime
36
40
  prerelease: false
37
41
  version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
38
43
  requirements:
39
44
  - - ! '>='
40
45
  - !ruby/object:Gem::Version
@@ -42,6 +47,7 @@ dependencies:
42
47
  - !ruby/object:Gem::Dependency
43
48
  name: facets
44
49
  requirement: !ruby/object:Gem::Requirement
50
+ none: false
45
51
  requirements:
46
52
  - - ! '>='
47
53
  - !ruby/object:Gem::Version
@@ -49,11 +55,12 @@ dependencies:
49
55
  type: :runtime
50
56
  prerelease: false
51
57
  version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
52
59
  requirements:
53
60
  - - ! '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: 2.9.3
56
- description: Ruby gem to use the full TransIP API (v4.2).
63
+ description: Ruby gem to use the full TransIP API (v5.0).
57
64
  email:
58
65
  - joost@joopp.com
59
66
  executables: []
@@ -68,31 +75,34 @@ files:
68
75
  - MIT-LICENSE
69
76
  - README.rdoc
70
77
  - lib/transip.rb
78
+ - lib/transip/api_error.rb
79
+ - lib/transip/client.rb
71
80
  - lib/transip/version.rb
72
81
  - transip.gemspec
73
82
  homepage: http://github.com/joost/transip
74
83
  licenses:
75
84
  - MIT
76
- metadata: {}
77
85
  post_install_message:
78
86
  rdoc_options:
79
87
  - --charset=UTF-8
80
88
  require_paths:
81
89
  - lib
82
90
  required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
83
92
  requirements:
84
93
  - - ! '>='
85
94
  - !ruby/object:Gem::Version
86
95
  version: '0'
87
96
  required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
88
98
  requirements:
89
99
  - - ! '>='
90
100
  - !ruby/object:Gem::Version
91
101
  version: '0'
92
102
  requirements: []
93
103
  rubyforge_project:
94
- rubygems_version: 2.1.1
104
+ rubygems_version: 1.8.23
95
105
  signing_key:
96
- specification_version: 4
97
- summary: Ruby gem to use the full TransIP API (v4.2).
106
+ specification_version: 3
107
+ summary: Ruby gem to use the full TransIP API (v5.0).
98
108
  test_files: []
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NjcxZDVlYWExZTQ0MjA2NGZmYWNjM2QzOTkwNjg0OTgzODgzNGRiNg==
5
- data.tar.gz: !binary |-
6
- NzM0ZjJlMzBjNDExYmVmMzE1ZDA1MjA0NTk1NGVkZTIwNDZmOTBlNQ==
7
- SHA512:
8
- metadata.gz: !binary |-
9
- OGNhMDg5ZjBiZTg2ZmZjZGUzZjZiYTkxZWI4MGMxMzYwZWFhNDM5OGIxZWVl
10
- ZDY1ZmY0YWYxZjZhYWIzZGU4NzM1YTYyZGZlOTBjMGYxMWIzN2FiNWUxMjBl
11
- NzA1OWE1Nzk4NTg0ZDBlNDM1MGFmZWQ2NzdjYzMyMTFiNTU3MzA=
12
- data.tar.gz: !binary |-
13
- NGFkYTQ4NWY5Y2U5ZDg4YTQ2ODdjYjY4ZGRiNzA0NmRiZGRiMWVlYzg2YjIx
14
- YzRlNTNlODM3Yjg5NThlNzBkZTRjMmU0MWU1NDVjZDUyNDZhMzVmY2RjMThh
15
- MmFkMGVmYWNhMmM1YWY1MzE4YjA4YWZiMDc4ZWNjODNlN2FhYzA=