kamcaptcha 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ pkg/
3
+ tmp/
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ bundler_args: --without test
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.3
6
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :test do
4
+ gem "ruby-debug", "~> 0.10.4", :require => nil, :platforms => :ruby_18
5
+ gem "debugger", "~> 1.1.1", :require => nil, :platforms => :ruby_19
6
+ end
7
+
8
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Morten Primdahl
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.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Kamcaptcha
2
+
3
+ A captcha system that uses less ridiculous images than ReCAPTCHA. Kamcaptcha has two somewhat independent parts to it, a generator for building the word images to present, and then a runtime configuration for checking validity of what gets submitted.
4
+
5
+ ## Generating images
6
+
7
+ This is something you do locally, probably just once. You need to generate a series of images for your application to serve, using the kamcaptcha command line tool. The tool depends on RMagick being installed, so:
8
+
9
+ 1. `gem install rmagick`
10
+ 2. kamcaptcha --help
11
+
12
+ And then generate the images with your preferences. If you specify a salt, make sure to use that same salt when you configure your application runtime. If you do not specify a salt, kamcaptcha will generate you an appropriate one to use, it looks like this:
13
+
14
+ ```sh
15
+ $ kamcaptcha --count 3 /tmp
16
+ Generating 3 words into /tmp
17
+
18
+ 1 /tmp/d519172b9cdfb2de5a5b30cf60836defc9b393f0e38a680937fcd5179467b191.png
19
+ 2 /tmp/a235210981703ca66e6d44450c39d42f40ad482bf165401a485b0d3e6c98e3e8.png
20
+ 3 /tmp/4ae2941beeea48773243cbf1366520c32dcc7b6375630c0e65bdf313df164ddc.png
21
+
22
+ Remember to set Kamcaptcha.salt = '58be24c9f6d0293ce2c9316fca1a6ec65d04c9156482b5ae51a267e022ba5a5c' in your application
23
+ ```
24
+
25
+ ## Runtime configuration
26
+
27
+ Presumably, Kamcaptcha will be used primarily with Rails, so the following instructions are Rails centric although there's nothing Rails specific about Kamcaptcha.
28
+
29
+ You need to configure Kamcaptcha in e.g. an initializer:
30
+
31
+ ```ruby
32
+ Kamcaptcha.salt = '58be24c9f6d0293ce2c9316fca1a6ec65d04c9156482b5ae51a267e022ba5a5c'
33
+ Kamcaptcha.path = File.join(Rails.root, "app", "assets", "images", "kampcaptcha")
34
+
35
+ # Optionally make use of the included form helper and and validations
36
+ ActionController::Base.send(:helper, Kamcaptcha::Helper)
37
+ ActionController::Base.send(:include, Kamcaptcha::Validation)
38
+ ```
39
+
40
+ You can now use Kamcaptcha in your views:
41
+
42
+ ```ruby
43
+ <%= kamcaptcha :label => "Please type in the letters below" %>
44
+ ```
45
+
46
+ And in your controllers:
47
+
48
+ ```ruby
49
+ return head(:bad_request) unless kamcaptcha_validates?
50
+ ```
51
+
52
+ You can write your own helper and controller logic easily, take a look at the source.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |test|
5
+ test.libs << 'lib'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default do
11
+ sh "bundle exec rake test"
12
+ end
data/bin/kamcaptcha ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler"
5
+
6
+ require "kamcaptcha"
7
+ require "kamcaptcha/generator"
8
+
9
+ require "trollop"
10
+ require "digest"
11
+
12
+ ARGV << "--help" if ARGV.empty?
13
+
14
+ opts = Trollop.options do
15
+ banner <<-EOS
16
+ Captcha word library generator. Builds images of words to present to users.
17
+
18
+ kamcaptcha [options] <output directory>
19
+
20
+ Usage examples:
21
+ kamcaptcha tmp/
22
+ kamcaptcha --count 100 tmp/
23
+ kamcaptcha --height 100 --width 300 tmp/
24
+ kamcaptcha --length 8 --format gif tmp/
25
+
26
+ A default run creates a "./tmp" directory and generates 10 random five character word PNG images, each 240x50 pixels in size.
27
+
28
+ Full list of options:
29
+ EOS
30
+
31
+ opt :salt, "Salt to use, if not specified one will be generated"
32
+ opt :count, "How many words to generate", :default => 10
33
+ opt :height, "Height of the word image in pixels", :default => 50
34
+ opt :width, "Width of the word image in pixesks", :default => 240
35
+ opt :length, "Length of the word in number of characters", :default => 5
36
+ opt :format, "Image file format", :default => "png"
37
+ end
38
+
39
+ if ARGV.empty? || !File.exist?(ARGV[0])
40
+ puts "Output directory does not exist"
41
+ exit 1
42
+ end
43
+
44
+ Kamcaptcha.salt = opts[:salt] || Digest::SHA2.hexdigest((0...64).map { rand(150).chr }.join)
45
+
46
+ puts "Generating #{opts[:count]} words into #{ARGV[0]}\n\n"
47
+
48
+ opts[:count].times do |i|
49
+ puts "\t#{i+1}\t"+Kamcaptcha::Generator.generate(ARGV[0], opts)
50
+ end
51
+
52
+ puts "\nRemember to set Kamcaptcha.salt = '#{Kamcaptcha.salt}' in your application\n"
53
+
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new "kamcaptcha", "0.0.1" do |s|
2
+ s.summary = "A captcha image generator that's a little less retarded"
3
+ s.description = "Helps humankind with simpler captchas"
4
+ s.authors = [ "Morten Primdahl" ]
5
+ s.email = "primdahl@me.com"
6
+ s.homepage = "http://github.com/morten/kamcaptcha"
7
+ s.files = `git ls-files`.split("\n")
8
+ s.license = "MIT"
9
+
10
+ s.add_runtime_dependency("trollop", ">= 2.0")
11
+
12
+ s.add_development_dependency("rake")
13
+ s.add_development_dependency("bundler")
14
+ s.add_development_dependency("rmagick")
15
+ s.add_development_dependency("minitest")
16
+ s.add_development_dependency("uuidtools", ">= 2.1.3")
17
+
18
+ s.executables << "kamcaptcha"
19
+ end
data/lib/kamcaptcha.rb ADDED
@@ -0,0 +1,28 @@
1
+ require "digest"
2
+
3
+ # You must configure Kamcaptcha with a path to the generated words, and a salt.
4
+ # The salt must be the same you used when generating words.
5
+ module Kamcaptcha
6
+
7
+ class << self
8
+ attr_accessor :salt, :path
9
+ end
10
+
11
+ def self.random
12
+ images[rand(images.size)]
13
+ end
14
+
15
+ def self.images
16
+ @images ||= Dir.glob("#{path}/*.*").map { |f| File.basename(f) }
17
+ end
18
+
19
+ def self.valid?(input, validation)
20
+ input = input.to_s.delete(" ").downcase
21
+ encrypt(input) == validation
22
+ end
23
+
24
+ def self.encrypt(string)
25
+ raise "You must configure a salt" if Kamcaptcha.salt.nil?
26
+ Digest::SHA2.hexdigest("#{salt}::#{string}")
27
+ end
28
+ end
@@ -0,0 +1,73 @@
1
+ require "rubygems"
2
+
3
+ begin
4
+ require "rmagick"
5
+ rescue LoadError => e
6
+ puts "RMagick required to run the generator, gem install rmagick"
7
+ return
8
+ end
9
+
10
+ module Kamcaptcha
11
+ class Generator
12
+ DEFAULTS = {
13
+ :chars => "23456789ABCDEFGHJKLMNPQRSTUVXYZ".split(""),
14
+ :width => 240,
15
+ :height => 50,
16
+ :length => 5,
17
+ :format => "png"
18
+ }
19
+
20
+ def self.generate(path, options = {})
21
+ options = DEFAULTS.merge(options)
22
+
23
+ word = generate_word(options[:chars], options[:length])
24
+ image = build_image(word, options[:width], options[:height])
25
+
26
+ name = word.delete(" ").downcase
27
+ file = File.join(path, Kamcaptcha.encrypt(name) + ".#{options[:format]}")
28
+
29
+ image.write(file)
30
+ image.destroy!
31
+
32
+ file
33
+ end
34
+
35
+ def self.generate_word(chars, size)
36
+ (1..size).map { chars[rand(chars.size)] }.join(" ")
37
+ end
38
+
39
+ def self.build_image(word, width, height)
40
+ text_img = Magick::Image.new(width, height)
41
+ black_img = Magick::Image.new(width, height) do
42
+ self.background_color = "black"
43
+ end
44
+
45
+ text_img.annotate(Magick::Draw.new, 0, 0, 0, 0, word) do
46
+ self.gravity = Magick::WestGravity
47
+ self.font_family = "Verdana"
48
+ self.font_weight = Magick::BoldWeight
49
+ self.fill = "#666666"
50
+ self.stroke = "black"
51
+ self.stroke_width = 2
52
+ self.pointsize = 44
53
+ end
54
+
55
+ # Apply a little blur and fuzzing
56
+ text_img = text_img.gaussian_blur(1.2, 1.2)
57
+ text_img = text_img.sketch(20, 30.0, 30.0)
58
+ text_img = text_img.wave(3, 90)
59
+
60
+ # Now we need to get the white out
61
+ text_mask = text_img.negate
62
+ text_mask.matte = false
63
+
64
+ # Add cut-out our captcha from the black image with varying tranparency
65
+ black_img.composite!(text_mask, Magick::CenterGravity, Magick::CopyOpacityCompositeOp)
66
+
67
+ text_img.destroy!
68
+ text_mask.destroy!
69
+
70
+ black_img
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ module Kamcaptcha
2
+ module Helper
3
+ DEFAULT_LABEL = "Please type the characters in the image below"
4
+
5
+ # Usage: <%= kamcaptcha :label => "Please prove that you're a human" %>
6
+ def kamcaptcha(options = {})
7
+ label = options.fetch(:label, DEFAULT_LABEL)
8
+ image = Kamcaptcha.random
9
+ token = image.split(".").first
10
+
11
+ <<-FORM
12
+ <div class="kamcaptcha">
13
+ <label for="kamcaptcha[input]">#{label}</label><input type="text" id="kamcaptcha[input]" name="kamcaptcha[input]" />
14
+ <input type="hidden" name="kamcaptcha[validation]" value="#{token}" />
15
+ <img src="#{image}" />
16
+ </div
17
+ FORM
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module Kamcaptcha
2
+ module Validation
3
+ def kamcaptcha_validates?
4
+ return false unless defined?(params)
5
+ return false unless params.respond_to?(:key?)
6
+ return false unless params.key?(:kamcaptcha)
7
+
8
+ Kamcaptcha.valid?(params[:kamcaptcha][:input], params[:kamcaptcha][:validation])
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require "bundler"
2
+
3
+ require "minitest/spec"
4
+ require "minitest/mock"
5
+ require "minitest/autorun"
6
+
7
+ Bundler.require
8
+
9
+ require "debugger" unless ENV["TRAVIS"]
10
+
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
12
+
13
+ require "kamcaptcha"
14
+ require "kamcaptcha/helper"
15
+ require "kamcaptcha/validation"
16
+ require "kamcaptcha/generator"
17
+
18
+ Kamcaptcha.salt = Time.now.to_f.to_s
@@ -0,0 +1,17 @@
1
+ describe Kamcaptcha::Generator do
2
+ describe "#generate_word" do
3
+ it "builds a word by the given chars and length" do
4
+ word = Kamcaptcha::Generator.generate_word([ "a", "b" ], 6)
5
+ assert_equal 6, word.split(" ").size
6
+ assert_match /[ab ]+/, word
7
+ end
8
+ end
9
+
10
+ describe "#generate" do
11
+ it "generates a file" do
12
+ file = Kamcaptcha::Generator.generate("/tmp")
13
+ assert File.exist?(file)
14
+ File.unlink(file)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ describe Kamcaptcha::Helper do
2
+ include Kamcaptcha::Helper
3
+
4
+ describe "#kamcaptcha" do
5
+ subject { Kamcaptcha.stub(:random, "hello.png") { kamcaptcha } }
6
+
7
+ describe "when given a label" do
8
+ subject { Kamcaptcha.stub(:random, "hello.png") { kamcaptcha(:label => "KAMCAPTCHA!") } }
9
+
10
+ it "uses that label" do
11
+ assert_match /KAMCAPTCHA!/, subject
12
+ end
13
+ end
14
+
15
+ it "uses a default label" do
16
+ assert_match /#{Kamcaptcha::Helper::DEFAULT_LABEL}/, subject
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ describe Kamcaptcha do
2
+
3
+ describe "#images" do
4
+ end
5
+
6
+ end
@@ -0,0 +1,47 @@
1
+ describe Kamcaptcha::Validation do
2
+ def params
3
+ @params
4
+ end
5
+
6
+ include Kamcaptcha::Validation
7
+
8
+ describe "#kamcaptcha_validates?" do
9
+ describe "when @params.nil?" do
10
+ before { @params = nil }
11
+ it "returns false" do
12
+ refute kamcaptcha_validates?
13
+ end
14
+ end
15
+
16
+ describe "when @params.empty?" do
17
+ before { @params = {} }
18
+ it "returns false" do
19
+ refute kamcaptcha_validates?
20
+ end
21
+ end
22
+
23
+ describe "when a correct word was entered" do
24
+ before do
25
+ word = "hello"
26
+ encoded = Kamcaptcha.encrypt(word)
27
+ @params = { :kamcaptcha => { :input => word, :validation => encoded }}
28
+ end
29
+
30
+ it "returns true" do
31
+ assert kamcaptcha_validates?
32
+ end
33
+ end
34
+
35
+ describe "when a wrong word was entered" do
36
+ before do
37
+ word = "hello"
38
+ encoded = Kamcaptcha.encrypt(word)
39
+ @params = { :kamcaptcha => { :input => "other", :validation => encoded }}
40
+ end
41
+
42
+ it "returns false" do
43
+ refute kamcaptcha_validates?
44
+ end
45
+ end
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kamcaptcha
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Morten Primdahl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: trollop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rmagick
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: minitest
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: uuidtools
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 2.1.3
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 2.1.3
110
+ description: Helps humankind with simpler captchas
111
+ email: primdahl@me.com
112
+ executables:
113
+ - kamcaptcha
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .travis.yml
119
+ - Gemfile
120
+ - MIT-LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - bin/kamcaptcha
124
+ - kamcaptcha.gemspec
125
+ - lib/kamcaptcha.rb
126
+ - lib/kamcaptcha/generator.rb
127
+ - lib/kamcaptcha/helper.rb
128
+ - lib/kamcaptcha/validation.rb
129
+ - test/test_helper.rb
130
+ - test/unit/test_generator.rb
131
+ - test/unit/test_helper.rb
132
+ - test/unit/test_kamcaptcha.rb
133
+ - test/unit/test_validation.rb
134
+ homepage: http://github.com/morten/kamcaptcha
135
+ licenses:
136
+ - MIT
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ! '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 1.8.23
156
+ signing_key:
157
+ specification_version: 3
158
+ summary: A captcha image generator that's a little less retarded
159
+ test_files: []