easy_captcha-ftbpro 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module EasyCaptcha
3
+ # captcha generation class
4
+ class Captcha
5
+ # code for captcha generation
6
+ attr_reader :code
7
+ # blob of generated captcha image
8
+ attr_reader :image
9
+
10
+ # generate captcha by code
11
+ def initialize code
12
+ @code = code
13
+ generate_captcha
14
+ end
15
+
16
+ def inspect #:nodoc:
17
+ "<EasyCaptcha::Captcha @code=#{code}>"
18
+ end
19
+
20
+ private
21
+
22
+ def generate_captcha #:nodoc:
23
+ @image = EasyCaptcha.generator.generate(@code)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module EasyCaptcha
2
+ # captcha controller
3
+ class Controller < ActionController::Base
4
+ before_filter :overwrite_cache_control
5
+ # captcha action send the generated image to browser
6
+ def captcha
7
+ if params[:format] == "wav" and EasyCaptcha.espeak?
8
+ send_data generate_speech_captcha, :disposition => 'inline', :type => 'audio/wav'
9
+ else
10
+ send_data generate_captcha, :disposition => 'inline', :type => 'image/png'
11
+ end
12
+ end
13
+
14
+ private
15
+ # Overwrite cache control for Samsung Galaxy S3 (remove no-store)
16
+ def overwrite_cache_control
17
+ response.headers["Cache-Control"] = "no-cache, max-age=0, must-revalidate"
18
+ response.headers["Pragma"] = "no-cache"
19
+ response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,93 @@
1
+ module EasyCaptcha
2
+ # helper class for ActionController
3
+ module ControllerHelpers
4
+
5
+ def self.included(base) #:nodoc:
6
+ base.class_eval do
7
+ helper_method :valid_captcha?, :captcha_valid?
8
+ end
9
+ end
10
+
11
+ # generate captcha image and return it as blob
12
+ def generate_captcha
13
+ if EasyCaptcha.cache
14
+ # create cache dir
15
+ FileUtils.mkdir_p(EasyCaptcha.cache_temp_dir)
16
+
17
+ # select all generated captchas from cache
18
+ files = Dir.glob(EasyCaptcha.cache_temp_dir + "*.png")
19
+
20
+ unless files.size < EasyCaptcha.cache_size
21
+ file = File.open(files.at(Kernel.rand(files.size)))
22
+ session[:captcha] = File.basename(file.path, '.*')
23
+
24
+ if file.mtime < EasyCaptcha.cache_expire.ago
25
+ File.unlink(file.path)
26
+ # remove speech version
27
+ File.unlink(file.path.gsub(/png\z/, "wav")) if File.exists?(file.path.gsub(/png\z/, "wav"))
28
+ else
29
+ return file.readlines.join
30
+ end
31
+ end
32
+ generated_code = generate_captcha_code
33
+ image = Captcha.new(generated_code).image
34
+
35
+ # write captcha for caching
36
+ File.open(captcha_cache_path(generated_code), 'w') { |f| f.write image }
37
+
38
+ # write speech file if u create a new captcha image
39
+ EasyCaptcha.espeak.generate(generated_code, speech_captcha_cache_path(generated_code)) if EasyCaptcha.espeak?
40
+
41
+ # return image
42
+ image
43
+ else
44
+ Captcha.new(generate_captcha_code).image
45
+ end
46
+ end
47
+
48
+ # generate speech by captcha from session
49
+ def generate_speech_captcha
50
+ raise RuntimeError, "espeak disabled" unless EasyCaptcha.espeak?
51
+ if EasyCaptcha.cache
52
+ File.read(speech_captcha_cache_path(current_captcha_code))
53
+ else
54
+ wav_file = Tempfile.new("#{current_captcha_code}.wav")
55
+ EasyCaptcha.espeak.generate(current_captcha_code, wav_file.path)
56
+ File.read(wav_file.path)
57
+ end
58
+ end
59
+
60
+ # return cache path of captcha image
61
+ def captcha_cache_path(code)
62
+ "#{EasyCaptcha.cache_temp_dir}/#{code}.png"
63
+ end
64
+
65
+ # return cache path of speech captcha
66
+ def speech_captcha_cache_path(code)
67
+ "#{EasyCaptcha.cache_temp_dir}/#{code}.wav"
68
+ end
69
+
70
+ # current active captcha from session
71
+ def current_captcha_code
72
+ session[:captcha]
73
+ end
74
+
75
+ # generate captcha code, save in session and return
76
+ def generate_captcha_code
77
+ session[:captcha] = EasyCaptcha.length.times.collect { EasyCaptcha.chars[rand(EasyCaptcha.chars.size)] }.join
78
+ end
79
+
80
+ # validate given captcha code and re
81
+ def captcha_valid?(code)
82
+ return false if session[:captcha].blank? or code.blank?
83
+ session[:captcha].to_s.upcase == code.to_s.upcase
84
+ end
85
+ alias_method :valid_captcha?, :captcha_valid?
86
+
87
+ # reset the captcha code in session for security after each request
88
+ def reset_last_captcha_code!
89
+ session.delete(:captcha)
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,79 @@
1
+ module EasyCaptcha
2
+ # espeak wrapper
3
+ class Espeak
4
+
5
+ # generator for captcha images
6
+ def initialize(&block)
7
+ defaults
8
+ yield self if block_given?
9
+ end
10
+
11
+ # set default values
12
+ def defaults
13
+ @amplitude = 80..120
14
+ @pitch = 30..70
15
+ @gap = 80
16
+ @voice = nil
17
+ end
18
+
19
+ attr_writer :amplitude, :pitch, :gap, :voice
20
+
21
+ # return amplitude
22
+ def amplitude
23
+ if @amplitude.is_a? Range
24
+ @amplitude.to_a.sort_by { rand }.first
25
+ else
26
+ @amplitude.to_i
27
+ end
28
+ end
29
+
30
+ # return amplitude
31
+ def pitch
32
+ if @pitch.is_a? Range
33
+ @pitch.to_a.sort_by { rand }.first
34
+ else
35
+ @pitch.to_i
36
+ end
37
+ end
38
+
39
+ def gap
40
+ @gap.to_i
41
+ end
42
+
43
+ def voice
44
+ if @voice.is_a? Array
45
+ v = @voice.sort_by { rand }.first
46
+ else
47
+ v = @voice
48
+ end
49
+
50
+ v.try :gsub, /[^A-Za-z0-9\-\+]/, ""
51
+ end
52
+
53
+ # generate wav file by captcha
54
+ def generate(captcha, wav_file)
55
+ # get code
56
+ if captcha.is_a? Captcha
57
+ code = captcha.code
58
+ elsif captcha.is_a? String
59
+ code = captcha
60
+ else
61
+ raise ArgumentError, "invalid captcha"
62
+ end
63
+
64
+ # add spaces
65
+ code = code.each_char.to_a.join(" ")
66
+
67
+ cmd = "espeak -g 10"
68
+ cmd << " -a #{amplitude}" unless @amplitude.nil?
69
+ cmd << " -p #{pitch}" unless @pitch.nil?
70
+ cmd << " -g #{gap}" unless @gap.nil?
71
+ cmd << " -v '#{voice}'" unless @voice.nil?
72
+ cmd << " -w #{wav_file} '#{code}'"
73
+
74
+ %x{#{cmd}}
75
+ true
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,8 @@
1
+ module EasyCaptcha
2
+ # module for generators
3
+ module Generator
4
+ autoload :Base, 'easy_captcha/generator/base'
5
+ autoload :Default, 'easy_captcha/generator/default'
6
+
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ module EasyCaptcha
2
+ module Generator
3
+
4
+ # base class for generators
5
+ class Base
6
+
7
+ # generator for captcha images
8
+ def initialize(&block)
9
+ defaults
10
+ yield self if block_given?
11
+ end
12
+
13
+ # default values for generator
14
+ def defaults
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+ module EasyCaptcha
3
+ module Generator
4
+
5
+ # default generator
6
+ class Default < Base
7
+
8
+ # set default values
9
+ def defaults
10
+ @font_size = 24
11
+ @font_fill_color = '#333333'
12
+ @font = File.expand_path('../../../../resources/captcha.ttf', __FILE__)
13
+ @font_stroke = '#000000'
14
+ @font_stroke_color = 0
15
+ @image_background_color = '#FFFFFF'
16
+ @sketch = true
17
+ @sketch_radius = 3
18
+ @sketch_sigma = 1
19
+ @wave = true
20
+ @wave_length = (60..100)
21
+ @wave_amplitude = (3..5)
22
+ @implode = 0.05
23
+ @blur = true
24
+ @blur_radius = 1
25
+ @blur_sigma = 2
26
+ end
27
+
28
+ # Font
29
+ attr_accessor :font_size, :font_fill_color, :font, :font_family, :font_stroke, :font_stroke_color
30
+
31
+ # Background
32
+ attr_accessor :image_background_color, :background_image
33
+
34
+ # Sketch
35
+ attr_accessor :sketch, :sketch_radius, :sketch_sigma
36
+
37
+ # Wave
38
+ attr_accessor :wave, :wave_length, :wave_amplitude
39
+
40
+ # Implode
41
+ attr_accessor :implode
42
+
43
+ # Gaussian Blur
44
+ attr_accessor :blur, :blur_radius, :blur_sigma
45
+
46
+ def sketch? #:nodoc:
47
+ @sketch
48
+ end
49
+
50
+ def wave? #:nodoc:
51
+ @wave
52
+ end
53
+
54
+ def blur? #:nodoc:
55
+ @blur
56
+ end
57
+
58
+ # generate image
59
+ def generate(code)
60
+ config = self
61
+ canvas = Magick::Image.new(EasyCaptcha.image_width, EasyCaptcha.image_height) do |variable|
62
+ self.background_color = config.image_background_color unless config.image_background_color.nil?
63
+ self.background_color = 'none' if config.background_image.present?
64
+ end
65
+
66
+ # Render the text in the image
67
+ canvas.annotate(Magick::Draw.new, 0, 0, 0, 0, code) {
68
+ self.gravity = Magick::CenterGravity
69
+ self.font = config.font
70
+ self.font_weight = Magick::LighterWeight
71
+ self.fill = config.font_fill_color
72
+ if config.font_stroke.to_i > 0
73
+ self.stroke = config.font_stroke_color
74
+ self.stroke_width = config.font_stroke
75
+ end
76
+ self.pointsize = config.font_size
77
+ }
78
+
79
+ # Blur
80
+ canvas = canvas.blur_image(config.blur_radius, config.blur_sigma) if config.blur?
81
+
82
+ # Wave
83
+ w = config.wave_length
84
+ a = config.wave_amplitude
85
+ canvas = canvas.wave(rand(a.last - a.first) + a.first, rand(w.last - w.first) + w.first) if config.wave?
86
+
87
+ # Sketch
88
+ canvas = canvas.sketch(config.sketch_radius, config.sketch_sigma, rand(180)) if config.sketch?
89
+
90
+ # Implode
91
+ canvas = canvas.implode(config.implode.to_f) if config.implode.is_a? Float
92
+
93
+ # Crop image because to big after waveing
94
+ canvas = canvas.crop(Magick::CenterGravity, EasyCaptcha.image_width, EasyCaptcha.image_height)
95
+
96
+
97
+ # Combine images if background image is present
98
+ if config.background_image.present?
99
+ background = Magick::Image.read(config.background_image).first
100
+ background.composite!(canvas, Magick::CenterGravity, Magick::OverCompositeOp)
101
+
102
+ image = background.to_blob { self.format = 'PNG' }
103
+ else
104
+ image = canvas.to_blob { self.format = 'PNG' }
105
+ end
106
+
107
+ # ruby-1.9
108
+ image = image.force_encoding 'UTF-8' if image.respond_to? :force_encoding
109
+
110
+ canvas.destroy!
111
+ image
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,29 @@
1
+ module EasyCaptcha
2
+ module ModelHelpers #:nodoc:
3
+ # helper class for ActiveRecord
4
+ def self.included(base) #:nodoc:
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods #:nodoc:
9
+ # to activate model captcha validation
10
+ def acts_as_easy_captcha
11
+ include InstanceMethods
12
+ attr_writer :captcha, :captcha_verification
13
+ end
14
+ end
15
+
16
+ module InstanceMethods #:nodoc:
17
+
18
+ def captcha #:nodoc:
19
+ ""
20
+ end
21
+
22
+ # validate captcha
23
+ def captcha_valid?
24
+ errors.add(:captcha, :invalid) if @captcha.blank? or @captcha_verification.blank? or @captcha.to_s.upcase != @captcha_verification.to_s.upcase
25
+ end
26
+ alias_method :valid_captcha?, :captcha_valid?
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ module ActionDispatch #:nodoc:
2
+ module Routing #:nodoc:
3
+ class Mapper #:nodoc:
4
+ # call to add default captcha root
5
+ def captcha_route
6
+ match 'captcha' => EasyCaptcha::Controller, :action => :captcha, :via => :get
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module EasyCaptcha
2
+ VERSION = '0.6.4'
3
+ end
@@ -0,0 +1,11 @@
1
+ module EasyCaptcha
2
+ # helper class for ActionView
3
+ module ViewHelpers
4
+ # generate an image_tag for captcha image
5
+ def captcha_tag(*args)
6
+ options = { :alt => 'captcha', :width => EasyCaptcha.image_width, :height => EasyCaptcha.image_height }
7
+ options.merge! args.extract_options!
8
+ image_tag(captcha_url(:i => Time.now.to_i), options)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module EasyCaptcha
2
+ module Generators #:nodoc:
3
+ class InstallGenerator < Rails::Generators::Base #:nodoc:
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ desc "Install easy_captcha"
7
+
8
+ def copy_initializer #:nodoc:
9
+ template "easy_captcha.rb", "config/initializers/easy_captcha.rb"
10
+ end
11
+
12
+ def add_devise_routes #:nodoc:
13
+ route 'captcha_route'
14
+ end
15
+
16
+ def add_after_filter #:nodoc:
17
+ inject_into_class "app/controllers/application_controller.rb", ApplicationController do
18
+ " # reset captcha code after each request for security\n after_filter :reset_last_captcha_code!\n\n"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end