deathbycaptcha 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in deathbycaptcha.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2011 by Infosimples. http://www.infosimples.com.br/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ == DeathByCaptcha
2
+
3
+ DeathByCaptcha is a Ruby API for acessing the http://www.deathbycaptcha.com services.
4
+
5
+ It supports HTTP and socket-based connections, with the latter being recommended for having faster responses and overall better performance.
6
+
7
+ When using socket connections, make sure that outgoing TCP traffic to <b>api.deathbycaptcha.com</b> to the ports range in <b>8123-8130</b> is not blocked by your firewall.
8
+
9
+ === Thread-safety note
10
+
11
+ The API is thread-safe, which means it is perfectly fine to share a client instance between multiple threads.
12
+
13
+ === Installation
14
+
15
+ You can install the latest DeathByCaptcha gem with:
16
+
17
+ gem install deathbycaptcha
18
+
19
+ You can add it to your Gemfile:
20
+
21
+ gem 'deathbycaptcha'
22
+
23
+ === Examples
24
+
25
+ ==== Create a client
26
+
27
+ ===== HTTP client
28
+
29
+ require 'deathbycaptcha'
30
+
31
+ client = DeathByCaptcha.http_client('myusername', 'mypassword')
32
+
33
+ ===== Socket client
34
+
35
+ require 'deathbycaptcha'
36
+
37
+ client = DeathByCaptcha.socket_client('myusername', 'mypassword')
38
+
39
+ ==== Verbose mode (for debugging purposes)
40
+
41
+ client.is_verbose = true
42
+
43
+ ==== Decoding captcha
44
+
45
+ ===== From URL
46
+
47
+ response = client.decode 'http://www.phpcaptcha.org/securimage/securimage_show.php'
48
+
49
+ puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
50
+
51
+ ===== From file path
52
+
53
+ response = client.decode 'path/to/my/captcha/file'
54
+
55
+ puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
56
+
57
+ ===== From a file
58
+
59
+ file = File.open('path/to/my/captcha/file', 'r')
60
+
61
+ response = client.decode file
62
+
63
+ puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
64
+
65
+ ===== From raw content
66
+
67
+ raw_content = File.open('path/to/my/captcha/file', 'r').read
68
+
69
+ response = client.decode(raw_content, :raw_content => true)
70
+
71
+ puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
72
+
73
+ ==== Get the solution of a captcha
74
+
75
+ puts client.get_captcha('130920620')['text'] # where 130920620 is the captcha id
76
+
77
+ ==== Get user account information
78
+
79
+ puts client.get_user
80
+
81
+ === Maintainers
82
+
83
+ * Rafael Barbolo Lopes (http://github.com/barbolo)
84
+ * Rafael Ivan Garcia (http://github.com/rafaelivan)
85
+
86
+ == License
87
+
88
+ MIT License. Copyright (C) 2011 by Infosimples. http://www.infosimples.com.br
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "deathbycaptcha/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.add_dependency 'rest-client', '~> 1.6.1'
7
+ s.add_dependency 'json', '~> 1.4.6'
8
+
9
+ s.name = "deathbycaptcha"
10
+ s.version = DeathByCaptcha::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.authors = ["Rafael Barbolo Lopes, Rafael Ivan Garcia"]
13
+ s.email = ["tech@infosimples.com.br"]
14
+ s.homepage = "http://github.com/infosimples/deathbycaptcha"
15
+ s.summary = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
16
+ s.description = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
17
+
18
+ s.rubyforge_project = "deathbycaptcha"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,9 @@
1
+ require 'deathbycaptcha/version'
2
+ require 'deathbycaptcha/config'
3
+ require 'deathbycaptcha/error'
4
+ require 'deathbycaptcha/client'
5
+ require 'deathbycaptcha/http_client'
6
+ require 'deathbycaptcha/socket_client'
7
+
8
+ module DeathByCaptcha
9
+ end
@@ -0,0 +1,197 @@
1
+ require 'logger'
2
+ require 'digest/sha1'
3
+ require 'rest_client'
4
+
5
+ module DeathByCaptcha
6
+
7
+ #
8
+ # DeathByCaptcha API Client
9
+ #
10
+ class Client
11
+
12
+ attr_accessor :config
13
+
14
+ def initialize(username, password, extra={})
15
+ data = {
16
+ :is_verbose => false, # If true, prints messages during execution
17
+ :logger_output => STDOUT, # Logger output path or IO instance
18
+ :api_version => API_VERSION, # API version (used as user-agent with http requests)
19
+ :software_vendor_id => 0, # API unique software ID
20
+ :max_captcha_file_size => 64 * 1024, # Maximum CAPTCHA image filesize, currently 64K
21
+ :default_timeout => 60, # Default CAPTCHA timeout
22
+ :polls_interval => 5, # Default decode polling interval
23
+ :http_base_url => 'http://api.deathbycaptcha.com/api', # Base HTTP API url
24
+ :http_response_type => 'application/json', # Preferred HTTP API server's response content type, do not change
25
+ :socket_host => 'api.deathbycaptcha.com', # Socket API server's host
26
+ :socket_port => (8123..8130).map {|p| p}, # Socket API server's ports range
27
+ :username => username, # DeathByCaptcha username
28
+ :password_hashed => Digest::SHA1.hexdigest(password), # DeathByCaptcha user's password encrypted
29
+ :password => '', # DeathByCaptcha user's password not encrypted
30
+ :password_is_hashed => true # True, if the password is hashed
31
+ }.merge(extra)
32
+
33
+ @config = DeathByCaptcha::Config.new(data) # Config instance
34
+ @logger = Logger.new(@config.logger_output) # Logger
35
+
36
+ end
37
+
38
+ #
39
+ # Fetch the user's details -- balance, rate and banned status
40
+ #
41
+ def get_user
42
+ raise DeathByCaptcha::Errors::NotImplemented
43
+ end
44
+
45
+ #
46
+ # Fetch the user's balance (in US cents)
47
+ #
48
+ def get_balance
49
+ raise DeathByCaptcha::Errors::NotImplemented
50
+ end
51
+
52
+ #
53
+ # Fetch a CAPTCHA details -- its numeric ID, text and correctness
54
+ #
55
+ def get_captcha(cid)
56
+ raise DeathByCaptcha::Errors::NotImplemented
57
+ end
58
+
59
+ #
60
+ # Fetch a CAPTCHA text
61
+ #
62
+ def get_text(cid)
63
+ raise DeathByCaptcha::Errors::NotImplemented
64
+ end
65
+
66
+ #
67
+ # Report a CAPTCHA as incorrectly solved
68
+ #
69
+ def report(cid)
70
+ raise DeathByCaptcha::Errors::NotImplemented
71
+ end
72
+
73
+ #
74
+ # Remove an unsolved CAPTCHA
75
+ #
76
+ def remove(cid)
77
+ raise DeathByCaptcha::Errors::NotImplemented
78
+ end
79
+
80
+ #
81
+ # Upload a CAPTCHA
82
+ #
83
+ # Accepts file names, file objects or urls, and an optional flag telling
84
+ # whether the CAPTCHA is case-sensitive or not. Returns CAPTCHA details
85
+ # on success.
86
+ #
87
+ def upload(captcha, options={})
88
+ raise DeathByCaptcha::Errors::NotImplemented
89
+ end
90
+
91
+ #
92
+ # Try to solve a CAPTCHA.
93
+ #
94
+ # See Client.upload() for arguments details.
95
+ #
96
+ # Uploads a CAPTCHA, polls for its status periodically with arbitrary
97
+ # timeout (in seconds). Removes unsolved CAPTCHAs. Returns CAPTCHA
98
+ # details if (correctly) solved.
99
+ #
100
+ def decode(captcha, options={})
101
+ options = {:timeout => config.default_timeout, :is_case_sensitive => false, :is_raw_content => false}.merge(options)
102
+ deadline = Time.now + options[:timeout]
103
+ c = upload(captcha, options)
104
+ if c
105
+ while deadline > Time.now and (c['text'].nil? or c['text'].empty?)
106
+ sleep(config.polls_interval)
107
+ c = get_captcha(c['captcha'])
108
+ end
109
+
110
+ if c['text']
111
+ if c['is_correct']
112
+ return c
113
+ end
114
+ else
115
+ remove(c['captcha'])
116
+ end
117
+
118
+ end
119
+
120
+
121
+ end
122
+
123
+ #
124
+ # Protected methods.
125
+ #
126
+ protected
127
+
128
+ #
129
+ # Return a hash with the user's credentials
130
+ #
131
+ def userpwd
132
+ if config.password_is_hashed
133
+ return {:username => config.username, :password => config.password_hashed, :is_hashed => '1'}
134
+ else
135
+ return {:username => config.username, :password => config.password}
136
+ end
137
+ end
138
+
139
+ #
140
+ # Private methods.
141
+ #
142
+ private
143
+
144
+ #
145
+ # Log a command and a message
146
+ #
147
+ def log(cmd, msg='')
148
+ if @config.is_verbose
149
+ @logger.info "#{cmd}: #{msg}"
150
+ end
151
+ end
152
+
153
+ #
154
+ # Return the File instance that corresponds to the captcha
155
+ #
156
+ # captcha can be:
157
+ # => a raw file content if is_raw_content is true
158
+ # => a File if its kind of File
159
+ # => a url if it's a String and starts with 'http://'
160
+ # => a filesystem path otherwise
161
+ #
162
+ def load_file(captcha, is_raw_content=false)
163
+ file = nil
164
+ if is_raw_content
165
+ # Create a temporary file, write the raw content and return it
166
+ tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
167
+ File.open(tmp_file_path, 'w') {|f| f.write captcha}
168
+ file = File.open(tmp_file_path, 'r')
169
+
170
+ elsif captcha.kind_of? File
171
+ # simply return the file
172
+ file = captcha
173
+
174
+ elsif captcha.kind_of? String and captcha[0,7] == 'http://'
175
+ # Create a temporary file, download the file, write it to tempfile and return it
176
+ tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
177
+ File.open(tmp_file_path, 'w') {|f| f.write RestClient.get(captcha)}
178
+ file = File.open(tmp_file_path, 'r')
179
+
180
+ else
181
+ # Return the File opened
182
+ file = File.open(captcha, 'r')
183
+
184
+ end
185
+
186
+ if file.nil?
187
+ raise DeathByCaptcha::Errors::CaptchaEmpty
188
+ elsif config.max_captcha_file_size <= file.size
189
+ raise DeathByCaptcha::Errors::CaptchaOverflow
190
+ end
191
+
192
+ return file
193
+ end
194
+
195
+ end
196
+
197
+ end
@@ -0,0 +1,42 @@
1
+ module DeathByCaptcha
2
+
3
+ #
4
+ # Config class
5
+ # based on http://mjijackson.com/2010/02/flexible-ruby-config-objects
6
+ #
7
+ class Config
8
+
9
+ def initialize(data={})
10
+ @data = {}
11
+ update!(data)
12
+ end
13
+
14
+ def update!(data)
15
+ data.each do |key, value|
16
+ self[key] = value
17
+ end
18
+ end
19
+
20
+ def [](key)
21
+ @data[key.to_sym]
22
+ end
23
+
24
+ def []=(key, value)
25
+ if value.class == Hash
26
+ @data[key.to_sym] = Config.new(value)
27
+ else
28
+ @data[key.to_sym] = value
29
+ end
30
+ end
31
+
32
+ def method_missing(sym, *args)
33
+ if sym.to_s =~ /(.+)=$/
34
+ self[$1] = args.first
35
+ else
36
+ self[sym]
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,62 @@
1
+ module DeathByCaptcha
2
+ module Errors
3
+
4
+
5
+ #
6
+ # Custom Error class for rescuing from DeathByCaptcha API errors
7
+ #
8
+ class Error < StandardError
9
+
10
+ def initialize(message)
11
+ super "#{message} (DEATHBYCAPTCHA API ERROR)"
12
+ end
13
+
14
+ end
15
+
16
+ #
17
+ # Raised when a method tries to access a not implemented method
18
+ #
19
+ class NotImplemented < Error
20
+ def initialize
21
+ super 'The requested functionality was not implemented'
22
+ end
23
+ end
24
+
25
+ #
26
+ # Raised when a HTTP call fails
27
+ #
28
+ class CallError < Error
29
+ def initialize
30
+ super 'HTTP call failed'
31
+ end
32
+ end
33
+
34
+ #
35
+ # Raised when the user is not allowed to access the API
36
+ #
37
+ class AccessDenied < Error
38
+ def initialize
39
+ super 'Access denied, please check your credentials and/or balance'
40
+ end
41
+ end
42
+
43
+ #
44
+ # Raised when the captcha file could not be loaded or is empty
45
+ #
46
+ class CaptchaEmpty
47
+ def initialize
48
+ super 'CAPTCHA image is empty or could not be loaded'
49
+ end
50
+ end
51
+
52
+ #
53
+ # Raised when the size of the captcha file is too big
54
+ #
55
+ class CaptchaOverflow
56
+ def initialize
57
+ super 'CAPTCHA image is too big'
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,93 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'digest/md5'
4
+
5
+ module DeathByCaptcha
6
+
7
+ #
8
+ # DeathByCaptcha HTTP API client
9
+ #
10
+ class HTTPClient < DeathByCaptcha::Client
11
+
12
+ def get_user
13
+ call('user', userpwd)
14
+ end
15
+
16
+ def get_captcha(cid)
17
+ call("captcha/#{cid}")
18
+ end
19
+
20
+ def report(cid)
21
+ call("captcha/#{cid}/report", userpwd)['is_correct']
22
+ end
23
+
24
+ def remove(cid)
25
+ not call("captcha/#{cid}/remove", userpwd)['captcha']
26
+ end
27
+
28
+ def a(nome, test=false, valor="verdade")
29
+ puts nome,test,valor
30
+ end
31
+
32
+ #
33
+ # Protected methods.
34
+ #
35
+ protected
36
+
37
+ def upload(captcha, options={})
38
+ options = {:is_case_sensitive => false, :is_raw_content => false}.merge(options)
39
+ data = userpwd
40
+ data[:swid] = config.software_vendor_id
41
+ data[:is_case_sensitive] = options[:is_case_sensitive] ? 1 : 0
42
+ data[:captchafile] = load_file(captcha, options[:is_raw_content])
43
+ response = call('captcha', data)
44
+ return response if response['captcha']
45
+ end
46
+
47
+ #
48
+ # Private methods.
49
+ #
50
+ private
51
+
52
+ def call(cmd, payload={}, headers={})
53
+ headers['Accept'] = config.http_response_type if headers['Accept'].nil?
54
+ headers['User-Agent'] = config.api_version if headers['User-Agent'].nil?
55
+
56
+ log('SEND', "#{cmd} #{payload}")
57
+
58
+ begin
59
+ url = "#{config.http_base_url}/#{cmd}"
60
+
61
+ if payload.empty?
62
+ response = RestClient.get(url, headers)
63
+ else
64
+ response = RestClient.post(url, payload, headers)
65
+ end
66
+
67
+ log('RECV', "#{response.size} #{response}")
68
+
69
+ return JSON.load(response)
70
+
71
+ rescue RestClient::Unauthorized => exc
72
+ raise DeathByCaptcha::Errors::AccessDenied
73
+
74
+ rescue RestClient::RequestFailed => exc
75
+ raise DeathByCaptcha::Errors::AccessDenied
76
+
77
+ else
78
+ raise DeathByCaptcha::Errors::CallError
79
+
80
+ end
81
+
82
+ return {}
83
+
84
+ end
85
+
86
+ end
87
+
88
+
89
+ def self.http_client(username, password, extra={})
90
+ DeathByCaptcha::HTTPClient.new(username, password, extra)
91
+ end
92
+
93
+ end
@@ -0,0 +1,249 @@
1
+ require 'json'
2
+ require 'digest/md5'
3
+ require 'thread'
4
+ require 'socket'
5
+ require 'base64'
6
+
7
+ module DeathByCaptcha
8
+
9
+ #
10
+ # DeathByCaptcha Socket API client
11
+ #
12
+ class SocketClient < DeathByCaptcha::Client
13
+
14
+ #
15
+ # Socket API server's host & ports range.
16
+ #
17
+ @@socket_host = 'api.deathbycaptcha.com'
18
+ @@socket_ports = (8123...8131).to_a
19
+
20
+ def initialize(username, password, extra = {})
21
+ @mutex = Mutex.new
22
+ @socket = nil
23
+
24
+ super(username, password, extra)
25
+ end
26
+
27
+ def get_user
28
+ call('user', userpwd)
29
+ end
30
+
31
+ def get_captcha(cid)
32
+ call('captcha', {:captcha => cid})
33
+ end
34
+
35
+ def report(cid)
36
+ call("captcha/#{cid}/report", userpwd)[:is_correct]
37
+
38
+ data = userpwd
39
+ data['captcha'] = cid
40
+ not call('report', data)[:is_correct]
41
+ end
42
+
43
+ def remove(cid)
44
+ not call("captcha/#{cid}/remove", userpwd)[:captcha]
45
+
46
+ data = userpwd
47
+ data['captcha'] = cid
48
+ not call('remove', data)[:captcha]
49
+ end
50
+
51
+ #
52
+ # Protected methods.
53
+ #
54
+ protected
55
+
56
+ def upload(captcha, is_case_sensitive=false, is_raw_content=false)
57
+ data = userpwd
58
+ data[:captcha] = Base64.encode64(load_file(captcha, is_raw_content).read)
59
+
60
+ data[:is_case_sensitive] = is_case_sensitive ? 1 : 0
61
+ response = call('upload', data)
62
+
63
+ response
64
+ end
65
+
66
+ #
67
+ # Private methods.
68
+ #
69
+ private
70
+
71
+ def connect
72
+ unless @socket
73
+ log('CONN')
74
+
75
+ begin
76
+ random_port = @@socket_ports[rand(@@socket_ports.size)]
77
+
78
+ # Creates a new Socket.
79
+ addr = Socket.pack_sockaddr_in(random_port, @@socket_host)
80
+
81
+ @socket = Socket.new(:INET, :STREAM)
82
+ @socket.connect_nonblock(addr)
83
+ rescue Exception => e
84
+ if e.errno == 36 # EINPROGRESS
85
+ # Nothing.
86
+ else
87
+ close # Closes the socket.
88
+ log('CONN', 'Could not connect.')
89
+ log('CONN', e.backtrace.join('\n'))
90
+
91
+ raise e
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ @socket
98
+ end
99
+
100
+ def close
101
+ if @socket
102
+ log('CLOSE')
103
+
104
+ begin
105
+ @socket.close
106
+ rescue Exception => e
107
+ log('CLOSE', 'Could not close socket.')
108
+ log('CLOSE', e.backtrace.join('\n'))
109
+ ensure
110
+ @socket = nil
111
+ end
112
+ end
113
+ end
114
+
115
+ def send(sock, buf)
116
+ # buf += '\n'
117
+ fds = [sock]
118
+
119
+ deadline = Time.now.to_f + 3 * config.polls_interval
120
+ while deadline > Time.now.to_f and not buf.empty? do
121
+ _, wr, ex = IO.select([], fds, fds, config.polls_interval)
122
+
123
+ if ex and ex.any?
124
+ raise IOError.new('send(): select() excepted')
125
+ elsif wr
126
+ while buf and not buf.empty? do
127
+ begin
128
+ sent = wr.first.send(buf, 0)
129
+ buf = buf[sent, buf.size - sent]
130
+ rescue Exception => e
131
+ if [35, 36].include? e.errno
132
+ break
133
+ else
134
+ raise e
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ unless buf.empty?
142
+ raise IOError.new('send() timed out')
143
+ else
144
+ return self
145
+ end
146
+ end
147
+
148
+ def recv(sock)
149
+ fds = [sock]
150
+ buf = ''
151
+
152
+ deadline = Time.now.to_f() + 3 * config.polls_interval
153
+ while deadline > Time.now.to_f do
154
+ rd, _, ex = IO.select(fds, [], fds, config.polls_interval)
155
+
156
+ if ex and ex.any?
157
+ raise IOError.new('send(): select() excepted')
158
+ elsif rd
159
+ while true do
160
+ begin
161
+ s = rd.first.recv_nonblock(256)
162
+ rescue Exception => e
163
+ if [35, 36].include? e.errno
164
+ break
165
+ else
166
+ raise e
167
+ end
168
+ else
169
+ if not s
170
+ raise IOError.new('recv(): connection lost')
171
+ else
172
+ buf += s
173
+ end
174
+ end
175
+ end
176
+
177
+ break if buf.size > 0
178
+ end
179
+ end
180
+
181
+ return buf[0, buf.size - 1] if buf.size > 0
182
+ raise IOError.new('recv() timed out')
183
+ end
184
+
185
+ def call(cmd, data = {})
186
+ data = {} if data.nil?
187
+ data.merge!({:cmd => cmd, :version => config.api_version})
188
+
189
+ request = data.to_json
190
+ log('SEND', request.to_s)
191
+
192
+ response = nil
193
+
194
+ (0...1).each do
195
+ # Locks other threads.
196
+ # If another thread has already acquired the lock, this thread will be locked.
197
+ @mutex.lock
198
+
199
+ begin
200
+ sock = connect
201
+ send(sock, request)
202
+
203
+ response = recv(sock)
204
+ rescue Exception => e
205
+ log('SEND', e.message)
206
+ log('SEND', e.backtrace.join('\n'))
207
+ close
208
+ else
209
+ # If no exception raised.
210
+ break
211
+ ensure
212
+ @mutex.unlock
213
+ end
214
+
215
+ end
216
+
217
+ if response.nil?
218
+ msg = 'Connection timed out during API request'
219
+ log('SEND', msg)
220
+
221
+ raise Exception.new(msg)
222
+ end
223
+
224
+ log('RECV', response.to_s)
225
+
226
+ begin
227
+ response = JSON.load(response)
228
+ rescue Exception => e
229
+ raise Exception.new('Invalid API response')
230
+ end
231
+
232
+ if 0x00 < response['status'] and 0x10 > response['status']
233
+ raise DeathByCaptcha::Errors::AccessDenied
234
+ elsif 0xff == response['status']
235
+ raise Exception.new('API server error occured')
236
+ else
237
+ return response
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
244
+
245
+ def self.socket_client(username, password, extra={})
246
+ DeathByCaptcha::SocketClient.new(username, password, extra)
247
+ end
248
+
249
+ end
@@ -0,0 +1,4 @@
1
+ module DeathByCaptcha
2
+ VERSION = "4.0.0"
3
+ API_VERSION = "DBC/Ruby v4.0.0"
4
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deathbycaptcha
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 4
7
+ - 0
8
+ - 0
9
+ version: 4.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Rafael Barbolo Lopes, Rafael Ivan Garcia
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-07 00:00:00 -03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 6
31
+ - 1
32
+ version: 1.6.1
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: json
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 1
45
+ - 4
46
+ - 6
47
+ version: 1.4.6
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
51
+ email:
52
+ - tech@infosimples.com.br
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - Gemfile
62
+ - MIT-LICENSE
63
+ - README.rdoc
64
+ - Rakefile
65
+ - deathbycaptcha.gemspec
66
+ - lib/deathbycaptcha.rb
67
+ - lib/deathbycaptcha/client.rb
68
+ - lib/deathbycaptcha/config.rb
69
+ - lib/deathbycaptcha/error.rb
70
+ - lib/deathbycaptcha/http_client.rb
71
+ - lib/deathbycaptcha/socket_client.rb
72
+ - lib/deathbycaptcha/version.rb
73
+ has_rdoc: true
74
+ homepage: http://github.com/infosimples/deathbycaptcha
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: deathbycaptcha
101
+ rubygems_version: 1.3.7
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
105
+ test_files: []
106
+