lumberg 0.9.5

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.
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"