lumberg 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.gitignore +9 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +98 -0
  6. data/LICENSE +20 -0
  7. data/README.md +178 -0
  8. data/Rakefile +49 -0
  9. data/lib/cacert.pem +3910 -0
  10. data/lib/lumberg.rb +23 -0
  11. data/lib/lumberg/exceptions.rb +6 -0
  12. data/lib/lumberg/version.rb +3 -0
  13. data/lib/lumberg/whm.rb +70 -0
  14. data/lib/lumberg/whm/account.rb +329 -0
  15. data/lib/lumberg/whm/args.rb +113 -0
  16. data/lib/lumberg/whm/base.rb +28 -0
  17. data/lib/lumberg/whm/dns.rb +205 -0
  18. data/lib/lumberg/whm/reseller.rb +268 -0
  19. data/lib/lumberg/whm/server.rb +290 -0
  20. data/lib/net_http_hacked.rb +42 -0
  21. data/lumberg.gemspec +28 -0
  22. data/spec/lumberg_spec.rb +9 -0
  23. data/spec/spec_helper.rb +33 -0
  24. data/spec/vcr_cassettes/whm/account/accountsummary.yml +43 -0
  25. data/spec/vcr_cassettes/whm/account/changepackage.yml +64 -0
  26. data/spec/vcr_cassettes/whm/account/createacct.yml +43 -0
  27. data/spec/vcr_cassettes/whm/account/domainuserdata.yml +43 -0
  28. data/spec/vcr_cassettes/whm/account/editquota.yml +85 -0
  29. data/spec/vcr_cassettes/whm/account/limitbw.yml +85 -0
  30. data/spec/vcr_cassettes/whm/account/listaccts.yml +147 -0
  31. data/spec/vcr_cassettes/whm/account/listsuspended.yml +43 -0
  32. data/spec/vcr_cassettes/whm/account/modifyacct.yml +63 -0
  33. data/spec/vcr_cassettes/whm/account/myprivs.yml +43 -0
  34. data/spec/vcr_cassettes/whm/account/passwd.yml +43 -0
  35. data/spec/vcr_cassettes/whm/account/removeacct.yml +85 -0
  36. data/spec/vcr_cassettes/whm/account/restoreaccount.yml +198 -0
  37. data/spec/vcr_cassettes/whm/account/setsiteip.yml +190 -0
  38. data/spec/vcr_cassettes/whm/account/suspend.yml +64 -0
  39. data/spec/vcr_cassettes/whm/account/unsuspend.yml +43 -0
  40. data/spec/vcr_cassettes/whm/dns/adddns.yml +64 -0
  41. data/spec/vcr_cassettes/whm/dns/addzonerecord.yml +43 -0
  42. data/spec/vcr_cassettes/whm/dns/dumpzone.yml +43 -0
  43. data/spec/vcr_cassettes/whm/dns/editzonerecord.yml +64 -0
  44. data/spec/vcr_cassettes/whm/dns/getzonerecord.yml +64 -0
  45. data/spec/vcr_cassettes/whm/dns/killdns.yml +43 -0
  46. data/spec/vcr_cassettes/whm/dns/listmxs.yml +22 -0
  47. data/spec/vcr_cassettes/whm/dns/listzones.yml +22 -0
  48. data/spec/vcr_cassettes/whm/dns/lookupnsip.yml +43 -0
  49. data/spec/vcr_cassettes/whm/dns/removezonerecord.yml +64 -0
  50. data/spec/vcr_cassettes/whm/dns/resetzone.yml +43 -0
  51. data/spec/vcr_cassettes/whm/dns/resolvedomainname.yml +43 -0
  52. data/spec/vcr_cassettes/whm/dns/savemxs.yml +22 -0
  53. data/spec/vcr_cassettes/whm/reseller/acctcounts.yml +43 -0
  54. data/spec/vcr_cassettes/whm/reseller/listacls.yml +22 -0
  55. data/spec/vcr_cassettes/whm/reseller/listresellers.yml +22 -0
  56. data/spec/vcr_cassettes/whm/reseller/resellerstats.yml +43 -0
  57. data/spec/vcr_cassettes/whm/reseller/saveacllist.yml +85 -0
  58. data/spec/vcr_cassettes/whm/reseller/setacls.yml +43 -0
  59. data/spec/vcr_cassettes/whm/reseller/setresellerips.yml +42 -0
  60. data/spec/vcr_cassettes/whm/reseller/setresellerlimits.yml +43 -0
  61. data/spec/vcr_cassettes/whm/reseller/setresellermainip.yml +64 -0
  62. data/spec/vcr_cassettes/whm/reseller/setresellernameservers.yml +64 -0
  63. data/spec/vcr_cassettes/whm/reseller/setresellerpackagelimit.yml +64 -0
  64. data/spec/vcr_cassettes/whm/reseller/setupreseller.yml +43 -0
  65. data/spec/vcr_cassettes/whm/reseller/suspendreseller.yml +64 -0
  66. data/spec/vcr_cassettes/whm/reseller/terminatereseller.yml +64 -0
  67. data/spec/vcr_cassettes/whm/reseller/unsetupreseller.yml +43 -0
  68. data/spec/vcr_cassettes/whm/reseller/unsuspendreseller.yml +43 -0
  69. data/spec/vcr_cassettes/whm/server/applist.yml +22 -0
  70. data/spec/vcr_cassettes/whm/server/gethostname.yml +22 -0
  71. data/spec/vcr_cassettes/whm/server/getlanglist.yml +22 -0
  72. data/spec/vcr_cassettes/whm/server/loadavg.yml +28 -0
  73. data/spec/vcr_cassettes/whm/server/my_function.yml +64 -0
  74. data/spec/vcr_cassettes/whm/server/response_type.yml +85 -0
  75. data/spec/vcr_cassettes/whm/server/systemloadavg.yml +43 -0
  76. data/spec/vcr_cassettes/whm/server/version.yml +22 -0
  77. data/spec/whm/account_spec.rb +577 -0
  78. data/spec/whm/args_spec.rb +179 -0
  79. data/spec/whm/base_spec.rb +28 -0
  80. data/spec/whm/dns_spec.rb +352 -0
  81. data/spec/whm/reseller_spec.rb +359 -0
  82. data/spec/whm/server_spec.rb +288 -0
  83. data/spec/whm/whm_spec.rb +41 -0
  84. metadata +265 -0
@@ -0,0 +1,290 @@
1
+ require 'cgi'
2
+
3
+ module Lumberg
4
+ module Whm
5
+ class Server < Base
6
+ # Server
7
+ attr_accessor :host
8
+
9
+ # Remote access hash
10
+ attr_accessor :hash
11
+
12
+ # Base URL to the WHM API
13
+ attr_accessor :base_url
14
+
15
+ # API username - :default => root
16
+ attr_accessor :user
17
+
18
+ # Raw HTTP response from WHM
19
+ attr_accessor :raw_response
20
+
21
+ # WHM parsed response
22
+ attr_reader :response
23
+
24
+ # HTTP Params used for API requests
25
+ attr_accessor :params
26
+
27
+ # WHM API function name
28
+ attr_reader :function
29
+
30
+ # Use ssl?
31
+ attr_accessor :ssl
32
+
33
+ # HTTP SSL verify mode
34
+ attr_accessor :ssl_verify
35
+
36
+ # Returned params to transfor to booleans
37
+ attr_accessor :boolean_params
38
+
39
+ # Force response type...ARG!
40
+ attr_accessor :force_response_type
41
+
42
+ #
43
+ # ==== Required
44
+ # * <tt>:host</tt> - PENDING
45
+ # * <tt>:hash</tt> - PENDING
46
+ #
47
+ # ==== Optional
48
+ # * <tt>:user</tt> - PENDING
49
+ # * <tt>:ssl</tt> - PENDING
50
+ def initialize(options)
51
+ Args.new(options) do |c|
52
+ c.requires :host, :hash
53
+ c.optionals :user, :ssl
54
+ end
55
+
56
+ @ssl_verify ||= false
57
+ @ssl = options.delete(:ssl)
58
+ @host = options.delete(:host)
59
+ @hash = format_hash(options.delete(:hash))
60
+ @user = (options.has_key?(:user) ? options.delete(:user) : 'root')
61
+
62
+ @base_url = format_url(options)
63
+ end
64
+
65
+ def perform_request(function, options = {})
66
+ @function = function
67
+
68
+ # WHM sometime uses different keys for the result hash
69
+ @key = options.delete(:key) || 'result'
70
+
71
+ @params = format_query(options)
72
+ uri = URI.parse("#{@base_url}#{function}?#{@params}")
73
+
74
+ yield self if block_given?
75
+
76
+ req = prepare_request(uri)
77
+
78
+ # Do the request
79
+ res = do_request(uri, req)
80
+
81
+ @raw_response = res
82
+ @response = JSON.parse(@raw_response.body)
83
+ format_response
84
+ end
85
+
86
+ def get_hostname
87
+ perform_request('gethostname', {:key => 'hostname'})
88
+ end
89
+
90
+ def version
91
+ perform_request('version', {:key => 'version'})
92
+ end
93
+
94
+ def load_average
95
+ @force_response_type = :query
96
+ result = perform_request('loadavg')
97
+ result[:success] = result[:params].has_key?(:one)
98
+ result
99
+ end
100
+
101
+ def system_load_average(options = {})
102
+ Args.new(options) do |c|
103
+ c.requires "api.version".to_sym
104
+ end
105
+
106
+ perform_request('systemloadavg', options.merge(:key => 'data'))
107
+ end
108
+
109
+ def languages
110
+ perform_request('getlanglist', {:key => 'lang'})
111
+ end
112
+
113
+ protected
114
+ def response_type
115
+ if !@force_response_type.nil?
116
+ @force_response_type
117
+ elsif !@response.respond_to?(:has_key?)
118
+ :unknown
119
+ elsif @response.has_key?('error')
120
+ :error
121
+ elsif @response.has_key?(@key)
122
+ :action
123
+ elsif @response.has_key?('status') && @response.has_key?('statusmsg')
124
+ :query
125
+ else
126
+ :unknown
127
+ end
128
+ end
129
+
130
+ def format_response
131
+ success, message, params = false, nil, {}
132
+
133
+ case response_type
134
+ when :action
135
+ success, message, params = format_action_response
136
+ when :query
137
+ success, message, params = format_query_response
138
+ when :error
139
+ message = @response['error']
140
+ when :unknown
141
+ message = "Unknown error occurred #{@response.inspect}"
142
+ end
143
+
144
+ params = Whm::to_bool(params, @boolean_params) unless @boolean_params.nil?
145
+
146
+ # Reset this for subsequent requests
147
+ @force_response_type = nil
148
+ {:success => success, :message => message, :params => Whm::symbolize_keys(params)}
149
+ end
150
+
151
+ def format_url(options = {})
152
+ @ssl = true if @ssl.nil?
153
+ port = (@ssl ? 2087 : 2086)
154
+ proto = (@ssl ? 'https' : 'http')
155
+
156
+ "#{proto}://#{@host}:#{port}/json-api/"
157
+ end
158
+
159
+ def format_hash(hash)
160
+ raise Lumberg::WhmArgumentError.new("Missing WHM hash") unless hash.is_a?(String)
161
+ hash.gsub(/\n|\s/, '')
162
+ end
163
+
164
+ def format_query(hash)
165
+ elements = []
166
+ hash.each do |key, value|
167
+ value = 1 if value === true
168
+ value = 0 if value === false
169
+ elements << "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"
170
+ end
171
+ elements.sort.join('&')
172
+ end
173
+
174
+ private
175
+
176
+ def do_request(uri, req)
177
+ begin
178
+ Net::HTTP.skip_bad_headers = true
179
+ http = Net::HTTP.new(uri.host, uri.port)
180
+ http.set_debug_output($stderr) if ENV['LUMBERG_DEBUG']
181
+
182
+ enable_ssl(http) if uri.port == 2087
183
+
184
+ http.start do |h|
185
+ h.request(req)
186
+ end
187
+ rescue Exception => e
188
+ puts "Error when sending the request.
189
+ Enable debug output by setting the environment variable LUMBERG_DEBUG and try again."
190
+ raise e
191
+ ensure
192
+ Net::HTTP.skip_bad_headers = false
193
+ end
194
+ end
195
+
196
+ def format_action_response
197
+ # Some API methods ALSO return a 'status' as
198
+ # part of a result. We only use this value if it's
199
+ # part of the results hash
200
+ item = @response[@key]
201
+
202
+ unless item.is_a?(Array) || item.is_a?(Hash)
203
+ res = {@key => item}
204
+ success, message = true, ""
205
+ else
206
+ result = nil
207
+ if item.first.is_a?(Hash)
208
+ result = item.first
209
+ res = (item.size > 1 ? item.dup : item.first.dup)
210
+ else
211
+ res = item.dup
212
+
213
+ # more hacks for WHM silly API
214
+ if @response.has_key?('result')
215
+ result_node = @response['result']
216
+ node_with_key_status = result_node.is_a?(Hash) && result_node.has_key?('status')
217
+ result = (node_with_key_status ? result_node : result_node.first)
218
+ else
219
+ res.delete('status')
220
+ res.delete('statusmsg')
221
+ end
222
+ end
223
+
224
+ unless result.nil?
225
+ success = result['status'].to_i == 1
226
+ message = result['statusmsg']
227
+ end
228
+ end
229
+
230
+ return success, message, res
231
+ end
232
+
233
+ def format_query_response
234
+ success = @response['status'].to_i == 1
235
+ message = @response['statusmsg']
236
+
237
+ # returns the rest as a params arg
238
+ res = @response.dup
239
+ res.delete('status')
240
+ res.delete('statusmsg')
241
+
242
+ return success, message, res
243
+ end
244
+
245
+ # Creates WHM::Whatever.new(:server => @server)
246
+ # automagically
247
+ def auto_accessors
248
+ [:account, :dns, :reseller]
249
+ end
250
+
251
+ def method_missing(meth, *args, &block)
252
+ if auto_accessors.include?(meth.to_sym)
253
+ ivar = instance_variable_get("@#{meth}")
254
+ if ivar.nil?
255
+ constant = Whm.const_get(meth.to_s.capitalize)
256
+ return instance_variable_set("@#{meth}", constant.new(:server => self))
257
+ else
258
+ return ivar
259
+ end
260
+ else
261
+ super
262
+ end
263
+ end
264
+
265
+ def prepare_request(uri)
266
+ # Setup request URL
267
+ url = uri.path
268
+ query = uri.query
269
+ url << "?" + query unless query.nil? || query.empty?
270
+
271
+ # Add Auth Header
272
+ req = Net::HTTP::Get.new(url)
273
+ req.add_field("Authorization", "WHM #{@user}:#{@hash}")
274
+
275
+ req
276
+ end
277
+
278
+ def enable_ssl(http)
279
+ if @ssl_verify
280
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
281
+ http.ca_file = File.join(Lumberg::base_path, "cacert.pem")
282
+ else
283
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
284
+ end
285
+ http.use_ssl = true
286
+ end
287
+
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,42 @@
1
+ module Net
2
+ class HTTP
3
+ @skip_bad_headers = false
4
+
5
+ class << self
6
+ # Accessor for @skip_bad_headers
7
+ def skip_bad_headers
8
+ @skip_bad_headers
9
+ end
10
+
11
+ # Mutator for @skip_bad_headers
12
+ def skip_bad_headers=(value)
13
+ @skip_bad_headers = value
14
+ end
15
+ end
16
+ end
17
+
18
+ class << HTTPResponse
19
+ # Parses each response header and strips out bad headers if skip_bad_headers is set
20
+ def each_response_header(sock)
21
+ key = value = nil
22
+ while true
23
+ line = sock.readuntil("\n", true).sub(/\s+\z/, '')
24
+ break if line.empty?
25
+ if line[0] == ?\s or line[0] == ?\t and value
26
+ value << ' ' unless value.empty?
27
+ value << line.strip
28
+ else
29
+ tmp_key, tmp_value = line.strip.split(/\s*:\s*/, 2)
30
+ next if tmp_value.nil? && Net::HTTP.skip_bad_headers
31
+
32
+ yield key, value if key
33
+
34
+ key = tmp_key
35
+ value = tmp_value
36
+ raise HTTPBadResponse, 'wrong header line format' if value.nil?
37
+ end
38
+ end
39
+ yield key, value if key
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "lumberg/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lumberg"
7
+ s.version = Lumberg::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Justin Mazzi"]
10
+ s.email = ["jmazzi@site5.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Ruby library for the WHM & cPanel API}
13
+ s.description = %q{Ruby library for the WHM & cPanel API; It's not a half day or anything like that}
14
+
15
+ s.rubyforge_project = "lumberg"
16
+
17
+ s.add_dependency 'json'
18
+ s.add_development_dependency 'rspec'
19
+ s.add_development_dependency 'fakeweb'
20
+ s.add_development_dependency 'vcr'
21
+ s.add_development_dependency 'rcov'
22
+ s.add_development_dependency 'metric_fu'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module Lumberg
4
+ describe "Lumberg::VERSION" do
5
+ it "has a valid format" do
6
+ VERSION.should match /\d+/
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ #$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'rspec'
3
+ require 'lumberg'
4
+ require 'lumberg/exceptions'
5
+ require 'vcr'
6
+ require 'timeout'
7
+
8
+ VCR.config do |c|
9
+ c.cassette_library_dir = 'spec/vcr_cassettes'
10
+ c.stub_with :fakeweb
11
+ c.default_cassette_options = {:record => :new_episodes}
12
+ end
13
+
14
+ def live_test?
15
+ !ENV['WHM_REAL'].nil?
16
+ end
17
+
18
+ def requires_attr(attr, &block)
19
+ expect { block.call }.to raise_error(Lumberg::WhmArgumentError, /Missing required parameter: #{attr}/i)
20
+ end
21
+
22
+ RSpec.configure do |c|
23
+ c.extend VCR::RSpec::Macros
24
+ c.before(:each) do
25
+ if live_test?
26
+ @whm_hash = ENV['WHM_HASH'].dup
27
+ @whm_host = ENV['WHM_HOST'].dup
28
+ else
29
+ @whm_hash = 'iscool'
30
+ @whm_host = 'myhost.com'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: https://myhost.com:2087/json-api/accountsummary?user=summary
6
+ body:
7
+ headers:
8
+ authorization:
9
+ - WHM root:iscool
10
+ response: !ruby/struct:VCR::Response
11
+ status: !ruby/struct:VCR::ResponseStatus
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ server:
16
+ - cpsrvd/11.28.64
17
+ transfer-encoding:
18
+ - chunked
19
+ content-type:
20
+ - text/plain
21
+ body: "{\"status\":1,\"statusmsg\":\"Ok\",\"acct\":[{\"startdate\":\"10 Dec 16 10:24\",\"plan\":\"default\",\"suspended\":0,\"theme\":\"na4\",\"shell\":\"/usr/local/cpanel/bin/jailshell\",\"maxpop\":\"unlimited\",\"maxlst\":\"unlimited\",\"maxaddons\":\"9999\",\"suspendtime\":null,\"ip\":\"192.1.2.3\",\"maxsub\":\"unlimited\",\"domain\":\"summary.com\",\"maxsql\":\"unlimited\",\"partition\":\"home\",\"maxftp\":\"unlimited\",\"user\":\"summary\",\"suspendreason\":\"not suspended\",\"unix_startdate\":\"1292516679\",\"diskused\":\"0M\",\"maxparked\":\"9999\",\"email\":\"timtesting@timtesting.com\",\"disklimit\":\"unlimited\",\"owner\":\"root\"}]}"
22
+ http_version: "1.1"
23
+ - !ruby/struct:VCR::HTTPInteraction
24
+ request: !ruby/struct:VCR::Request
25
+ method: :get
26
+ uri: https://myhost.com:2087/json-api/accountsummary?user=notexists
27
+ body:
28
+ headers:
29
+ authorization:
30
+ - WHM root:iscool
31
+ response: !ruby/struct:VCR::Response
32
+ status: !ruby/struct:VCR::ResponseStatus
33
+ code: 200
34
+ message: OK
35
+ headers:
36
+ server:
37
+ - cpsrvd/11.28.64
38
+ transfer-encoding:
39
+ - chunked
40
+ content-type:
41
+ - text/plain
42
+ body: "{\"status\":0,\"statusmsg\":\"Account does not exist\",\"acct\":null}"
43
+ http_version: "1.1"