e x p r e s s i c a

ruby on rails, business and technicalities

No public Twitter messages.

  • RSS
  • Facebook
  • Twitter
  • Linkedin

Hello Everyone !!
I have released a captcha plugin Simple Captcha. It is really simple to implement, and provides a cool feature of multiple styles of images. Visit here for more explanation.


Previous post on improving image for validates_captcha

———————————————————

Validates captcha is a good plugin to implement captcha in your rails application.
However i found that there is repetition of the string of the image and the quality of image is not that good. To get a good quality image and random string
something like this

replace the code of the file /vendor/plugins/validates_captcha/lib/captcha_challenge.rb with the following code…


require 'digest/sha1'

module AngryMidgetPluginsInc #:nodoc:

	# CaptchaChallenge
	class CaptchaChallenge

		include CaptchaConfig
		extend CaptchaConfig

		DEFAULT_TTL = 1200#Lifetime in seconds. Default is 20 minutes.

		attr_reader :id, :created_at
		attr_accessor :ttl

		def initialize(options = {}) #:nodoc:
			generate_id

			options = {
				:ttl => config['default_ttl'] || DEFAULT_TTL
			}.update(options)

			self.ttl = options[:ttl]
			@created_at = Time.now

			self.class.prune
		end

		# Implement in subclasses.
		def correct? #:nodoc:
			raise NotImplementedError
		end

	private

		def generate_id #:nodoc:
			self.id = Digest::SHA1.hexdigest(Time.now.to_s+rand.to_s)
		end

		def id=(i) #:nodoc:
			@id = i
		end

		def write_to_store #:nodoc:
			store.transaction{
				store[:captchas] = Array.new unless store.root?(:captchas)
				store[:captchas] << self
			}
		end

	class << self

		# Removes old instances from PStore
		def prune
			store.transaction{
				if store.root?(:captchas)
					store[:captchas].each_with_index{|c,i|
						if Time.now > c.created_at+c.ttl
							store[:captchas].delete_at(i)
						end
					}
				end
			}
		end#prune
	end#class << self

	end

	# A CAPTCHA challenge where an image with text is
	# generated. A human can read the text with relative
	# ease, while most robots can not. There are accessibility
	# problems with this challenge, though, as people
	# with reduced or no vision are unlikely to pass the test.
	class CaptchaImageChallenge < CaptchaChallenge

		WORDS = 'gorilla costume, superman, banana bender, chuck norris, xray vision, ahoy me hearties,
				 chunky bacon, latex, rupert murdoch, clap your hands, year 2000,
				 sugar coated, coca cola, rastafarian, airbus a380'.split(/,s+/)
		DEFAULT_DIR = 'captcha'#public/images/captcha
		WRITE_DIR = File.join(RAILS_ROOT, 'public', 'images')
		DEFAULT_FILETYPE = 'jpg'

		attr_reader :image
		attr_accessor :string, :dir, :filename, :filetype

		# Creates an image challenge.
	  def initialize(options = {})
			super

			options = {
				:string => config['words'] ? config['words'][rand(config['words'].size)] : WORDS[rand(WORDS.size)],
				:dir => config['default_dir'] || DEFAULT_DIR,
				:filetype => config['default_filetype'] || DEFAULT_FILETYPE
			}.update(options)

            self.string = Digest::SHA1.hexdigest(Time.now.to_s)[0..4].upcase #options[:string]
            self.dir = options[:dir]
            self.filetype = options[:filetype]
            self.filename = options[:filename] || generate_filename
            write_to_store
          end

		# Generates the image.
		def generate(options = {})
			options = {
				:fontsize => 50,
				:padding => 20,
				:color => '#000',
				:background => '#fff',
				:fontweight => 'bolw',
				:rotate => true
			}.update(options)

			options[:fontweight] = case options[:fontweight]
				when 'bold' then 700
				else 400
			end

			#added
			text = Array.new
			0.upto(4) do |i|
				text[i] = Magick::Draw.new
				text[i].pointsize = options[:fontsize]
				text[i].font_weight = 600 #options[:fontweight]
				text[i].fill = 'black' #options[:color]
				text[i].gravity = Magick::CenterGravity
				text[i].rotation = (rand(2)==1 ? rand(30) : -rand(30)) if options[:rotate]
			end

                  metric = text[2].get_type_metrics(self.string)
                  #add bg
		  canvas = Magick::ImageList.new
		  fill = Magick::HatchFill.new('white','black')
		  x = metric.width+options[:padding]
		  y = metric.height+options[:padding]

		  #ADDING NOISE

		  img1 = Magick::Image.new(x,y,fill)
		  gc = Magick::Draw.new
		  gc.stroke_linejoin('round')
		  gc.stroke('black')
		  gc.stroke_width(1)
                  100.times do |i|
                    x1 = rand(x)
                    y1 = rand(y)
                    gc.circle(x1,y1,x1.next,y1)
                  end
                  gc.draw(img1)
                  canvas << img1
                  0.upto(4) do |i|
				canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
					self.background_color = '#000F'
					y_loc = rand(y)
				}.annotate(text[i], 0, 0, (i-2)*30, rand(y/4), self.string[i,1]) #.wave(5, 20)
		  end

		  canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
		    #changed
		    p = Magick::Pixel.from_color(options[:background])
		    p.opacity = Magick::MaxRGB
		    self.background_color = p
		  }  #.add_noise(Magick::LaplacianNoise)

			self.image = canvas.flatten_images.blur_image(1)
		end

		# Writes image to file.
		def write(dir = self.dir, filename = self.filename)
			self.image.write(File.join(WRITE_DIR, dir, filename))
		end

		# Determine if the supplied +string+ matches
		# that used when generating the image.
		def correct?(string)
			string.downcase == self.string.downcase
		end

		# The full path to the image file, relative
		# to <tt>public/images</tt>.
		def file_path
			File.join(dir,filename)
		end

	class << self

		# Deletes old image files. Also calls CaptchaChallenge.prune
		def prune
			store.transaction{
				if store.root?(:captchas)
					store[:captchas].each_with_index{|c,i|
						if Time.now > c.created_at+c.ttl
							if File.exists?(File.join(WRITE_DIR, c.file_path))
								begin
									File.unlink(File.join(WRITE_DIR, c.file_path))
								rescue Exception
								end
							end
						end
					}
				end
			}
			super
		end#prune
	end#class << self

	private

		def generate_filename #:nodoc:
			self.id+'.'+self.filetype
		end

		def image=(i) #:nodoc:
			@image = i
		end

	end

end

visit Here to view the customized use of the plugin validates_captcha in RoR.

16 Responses so far.

  1. Jerry says:

    Hello,Sur
    Can you send a mail about this correct source code to me?
    When i put above code to my ruby editor,it show some error.
    I think it has some syntax error.Maybe it be bring by html editor.
    So,if you correct source code please send a copy to me.
    Thank you so much!!!

    wangchangxiao@hotmail.com

  2. Sur Max says:

    Hi Jerry !!
    Sorry if there is any mistake residing in the above code. I have also done some more modifications in the source code of this file to make the images more practically implementable. I will upload the new code here today itself, side by i will mail it to you also.
    ——————————————————————————
    Hello everybody !!
    Everyone who is reading this post, as jerry suggested… if you are getting any problem with the code provided here then let me know via comment(or else) and provide your email… so that i can mail you that modified file itself.

  3. Chintan Shah says:

    hi ,

    it is a great thing and thnx a lot

    can u help me for doing some changes in yr code.

    1) how can i change the size of the image ie height and width?

    2)how can i remove the black dots from the image that u r generating for the word?

  4. Sur Max says:

    Hi Chintan !!
    Although i would recommend to use the dots coz there are some smart bots available which are based on pixel recognition and can even read the string from images if images are clean(without noise) and easy to read.
    However remove this code from the code above given to remove the dots

    100.times do |i|
    x1 = rand(x)
    y1 = rand(y)
    gc.circle(x1,y1,x1.next,y1)
    end

    Do always remember to restart the server whenever you change the code of any plugin.

  5. Sur Max says:

    I havent explored the resizing yet…
    hopefully i will tell you soon… and will upload the code also.

  6. Chintan Shah says:

    thnx Sur

  7. Laurent says:

    Hi,

    I still have problems with the image (i have a lot of noise and no characters) even with the modified code of the class from this page.
    Could you please send me the source code at laurent.bois@gmail.com

  8. Dirk says:

    I would like to use this too.

    Could you please send me the source code at

    diddek@gmail.com

    Thanks in advance

    Dirk

  9. Sur Max says:

    Hi Dirk !!
    The modified code for the file capcha_challenge.rb is right there in the post itself. If you wanna see the usage of this plugin then see this post to implement captcha and then add the code given above, to improve the image quality !!

  10. Dirk says:

    Dear ‘Sur Max’

    I am sorry bu thte code provided above does not run ( `const_missing’: uninitialized constant CaptchaConfig (NameError))

    The posts here mention the same, so I wondered if you could provide me with the working version :)

    Can you either update the source in the post or mail me?

    Dirk

  11. Sur Max says:

    Hi Dirk !
    I have mailed the captcha plugin which i am using with all the modified code. Try it by just putting it into vendor/plugin directory and restarting the server… Assuming u have written all the code in model and controller correctly it would work fine.

  12. Dirk says:

    Hi Sur Max

    I am sorry if I dont get it, but what’s the difference with the official version?

    The word list is the same in your version and the ‘official’ one:

    WORDS = ‘gorilla costume, superman, chuck norris, xray vision, ahoy me hearties,
    chunky bacon, latex, rupert murdoch, clap your hands, year 2000, disco rocks,
    sugar coated, staple my ears, rastafarian, airbus a380, good old days’.split(/,\s+/)

    I dont see a random text or the image like you display on top of this page.

    Differences I do see is that the code is arranged differently and ‘namespaces’ have been changed (AngryMidget … instead of FleksPlugin) – what am I missing here?

    Dirk

  13. Sur Max says:

    Yes u didnt get it !! lol …
    The words written now are only text in the modified code and not being used anywhere, i am wandering if you are still getting the repeated words and not random string… probably u havnt restarted the server or u have not implemented it and just looking at code only …

  14. Chintan Shah says:

    Hi Sur,

    i m in big problem, u have to help me at any how? check yr captcha code with the mongrel? it creates big problems so pls reply me soon i m waiting for reply

    it gives me an error like “pid…….” and when i tried to run the method which contains the captcha code it just displays as blank page and when i go for the page source it showm me blank there also, and it also clear the clusters of the mongrel, from there mongrel will be stoped so when i tried to go some where else it gives me an error of 503…. so pls help me as soon as possible.

    bye and thnx

  15. Matt says:

    Could you send me the code?

  16. Stabilo says:

    Got the same error as Dirk from above ( `const_missing’: uninitialized constant CaptchaConfig (NameError))

    Any ideas? Seems to be some namespace problem

    Regards

    Stabilo


Sponsors