# \[ANN\] flatulent-0.0.3

**URL:** <https://rubytalk.org/t/ann-flatulent-0-0-3/38859>\
**Category:** ruby-talk\
**Created:** [5 July 2007 06:41 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859 "2007-07-05T06:41:25Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [5 July 2007 06:41 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/1 "2007-07-05T06:41:25Z")

</div>

(the demo has been updated too)

NAME

&nbsp;&nbsp;&nbsp;flatulent : CAPTCHA for FIGLET.

SYNOPSIS

&nbsp;&nbsp;&nbsp;the flatulent gem provides brain dead simple to use, but internally cunning,  
&nbsp;&nbsp;&nbsp;ascii art (figlet) captcha for ruby.

URI

&nbsp;&nbsp;&nbsp;[http://codeforpeople.com/lib/ruby](http://codeforpeople.com/lib/ruby)  
&nbsp;&nbsp;&nbsp;[http://rubyforge.org/projects/codeforpeople](http://rubyforge.org/projects/codeforpeople)

HOW DO I GET FLATULENT?

&nbsp;&nbsp;&nbsp;gem install flatulent

HISTORY  
&nbsp;&nbsp;&nbsp;0.0.3:  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- following are now all equivalent when posting (thanks botp)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0==o==O==Q (zero, oh's, and queue)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l==l (one and el)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2==z==Z (two and z's)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5==s==S (5 and s's)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- random horizontal and vertical displacement of each char

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- vastly improved background noise based on figlet char shapes

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- inputs are case sensitive (thanks john joyce, chris carter)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- expanded rails examples

&nbsp;&nbsp;&nbsp;0.0.2

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- ajax gets stinky: Flatulent.ajax! the result of this new addition is  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;that the captcha itself doesn't appear in the source file at all

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- blowfish encoding for timebomb and captcha fields

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- auto server key configuration using hostname and mac address

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- improved noise algorithm

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- improved character placement (chars shared edges to make ocr'ing harder)

&nbsp;&nbsp;&nbsp;0.0.1

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- initial version

RAILS EXAMPLES

&nbsp;&nbsp;&nbsp;REGULAR METHOD (LESS SECURE):

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def controller\_action  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if params.has\_key? 'flatulent'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flatulent.validate! params  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;render :inline =\> \<\<-html  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<html\>\<body\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#{ Flatulent.form }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</body\>\</html\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;html  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;AJAX METHOD (MORE SECURE):

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def controller\_action  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if params.has\_key? 'flatulent'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flatulent.validate! params  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;render :inline =\> \<\<-html  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<html\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<head\> \<%= javascript\_include\_tag 'prototype' %\> \</head\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<body\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<form action='./' method='post'\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<%= Flatulent.ajax %\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<input type='submit' name='submit' value='submit' /\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</form\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</body\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</html\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;html  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

DOCS

&nbsp;&nbsp;&nbsp;see source in ./lib/\*  
&nbsp;&nbsp;&nbsp;see the example rails project in ./rails

ONLINE SAMPLES

&nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/4791838](http://drawohara.tumblr.com/post/4791838)  
&nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/4944987](http://drawohara.tumblr.com/post/4944987)  
&nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/4968766](http://drawohara.tumblr.com/post/4968766)

ONLINE DEMO OF AJAX METHOD

&nbsp;&nbsp;&nbsp;[http://fortytwo.merseine.nu:3000/flatulent/ajax](http://fortytwo.merseine.nu:3000/flatulent/ajax) -- try to break it!

enjoy.

-a

> **···**
>
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![Patrick\_Hurley1](https://avatars.discourse-cdn.com/v4/letter/p/b77776/32.png) [@Patrick\_Hurley1](https://rubytalk.org/u/Patrick_Hurley1)\
**Post date:** [5 July 2007 12:51 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/2 "2007-07-05T12:51:55Z")

</div>

I can only guess 1 in 5 of the "images" -- that is one way to cut down  
on spammers 🙂  
pth

> **···**
>
> On 7/5/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:
> 
> > ONLINE DEMO OF AJAX METHOD
> > 
> > &nbsp;&nbsp;&nbsp;[http://fortytwo.merseine.nu:3000/flatulent/ajax](http://fortytwo.merseine.nu:3000/flatulent/ajax) -- try to break it!

---

<div class="post-metadata">

**Author:** ![Jannis\_Harder](https://avatars.discourse-cdn.com/v4/letter/j/5e9695/32.png) [@Jannis\_Harder](https://rubytalk.org/u/Jannis_Harder)\
**Post date:** [6 July 2007 18:29 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/3 "2007-07-06T18:29:14Z")

</div>

The method used in this captcha is very is to break. In fact I can  
solve  
the captchas 6 times as fast as it takes to generate them (locally)  
in  
only 63 lines of code. I do this by generating a regexp for each  
possible  
character. As the characters don't get damaged by the noise (as they  
get  
in most image bases captchas) this works all of the time.

$ ruby benchmark.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user system total real  
generate: 0.160000 0.020000 0.180000 ( 0.192005)  
setup: 0.030000 0.000000 0.030000 ( 0.025381)  
break: 0.010000 0.000000 0.010000 ( 0.010908)  
generate 200: 12.100000 1.000000 13.100000 ( 13.125787)  
break 200: 2.050000 0.100000 2.150000 ( 2.152749)  
$ wc -l deflatulent.rb /usr/local/lib/ruby/gems/1.8/gems/  
flatulent-0.0.3/lib/flatulent.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;63 deflatulent.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;604 /usr/local/lib/ruby/gems/1.8/gems/flatulent-0.0.3/lib/  
flatulent.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;667 total  
$ cat benchmark.rb  
require 'deflatulent'  
require 'flatulent'  
require 'benchmark'

defl = html = code = nil  
pairs = Array.new(200)

GC.disable

Benchmark.bm(13) do |x|  
&nbsp;&nbsp;x.report("generate:") { flat = Flatulent.new; html = flat.form; code  
= flat.string }  
&nbsp;&nbsp;x.report("setup:") { defl = Deflatulent.new }  
&nbsp;&nbsp;x.report("break:") { raise unless defl.deflatulent(html) ==  
code }

&nbsp;&nbsp;x.report("generate 200:") { 200.times{|index| flat = Flatulent.new;  
pairs[index] = [flat.form,flat.string] } }  
&nbsp;&nbsp;x.report("break 200:") { pairs.map{|(html,code)| raise unless  
defl.deflatulent(html) == code } }

end  
$ cat deflatulent.rb  
require 'flatulent'

class Deflatulent

&nbsp;&nbsp;def initialize font="big"  
&nbsp;&nbsp;&nbsp;&nbsp;font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font  
+".flf"))  
&nbsp;&nbsp;&nbsp;&nbsp;typesetter = Text::Figlet::Typesetter.new font  
&nbsp;&nbsp;&nbsp;&nbsp;letters = ('A'..'Z').to\_a + ('1'..'9').to\_a  
&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array = letters.map{|letter| [letter,  
gen\_figlet\_lines\_array(typesetter[letter])] }  
&nbsp;&nbsp;end

&nbsp;&nbsp;def deflatulent string  
&nbsp;&nbsp;&nbsp;&nbsp;if string =~ /\<pre id='[a-zA-Z0-9\_-]+\_element' style='.\*?'\>(.\*?)\<\/

> /m

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = $1

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[/\<\/?span\>/,''],["&nbsp;"," "],["\<br\>","\n"],["&lt;","\<"],  
["&gt;","\>"],["&quot;",'"'],["&amp;","&"]].each do |args|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.gsub!(\*args)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;width = string.index("\n")  
&nbsp;&nbsp;&nbsp;&nbsp;string.tr!("\n","")  
&nbsp;&nbsp;&nbsp;&nbsp;solution =

&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array.each do |(letter,(length,lines))|

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re = "(?="  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|line| re \<\< line \<\< ".{#{width-length}}" }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re \<\< ")"

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.scan(Regexp.new(re, Regexp::MULTILINE)) do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution[$~.begin(0) % width] = letter  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;solution.join  
&nbsp;&nbsp;end

private  
&nbsp;&nbsp;def gen\_figlet\_lines\_array string  
&nbsp;&nbsp;&nbsp;&nbsp;lines = string.split("\n")  
&nbsp;&nbsp;&nbsp;&nbsp;lines.shift while lines.first.strip.empty?  
&nbsp;&nbsp;&nbsp;&nbsp;lines.pop while lines.last.strip.empty?

&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}  
&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}

&nbsp;&nbsp;&nbsp;&nbsp;[lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ? '.' :  
Regexp.escape(q))}.join}]  
&nbsp;&nbsp;end

end

if \_\_FILE\_\_ == $0  
&nbsp;&nbsp;defl = Deflatulent.new(ARGV[0] || "big")  
&nbsp;&nbsp;loop do  
&nbsp;&nbsp;&nbsp;&nbsp;input = ""  
&nbsp;&nbsp;&nbsp;&nbsp;while line=gets and not line.chomp.empty?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;input \<\< line  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;puts defl.deflatulent(input)  
&nbsp;&nbsp;&nbsp;&nbsp;break unless line  
&nbsp;&nbsp;end  
end

> **···**
>
> --  
> Jannis Harder

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [5 July 2007 13:14 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/4 "2007-07-05T13:14:05Z")

</div>

Wow, no kidding. Here are a couple it tried on me:

[![](http://graysoftinc.com/ruby/images/scary_captcha.png) ](http://graysoftinc.com/ruby/images/scary_captcha.png)

Yikes!

James Edward Gray II

> **···**
>
> On Jul 5, 2007, at 7:51 AM, Patrick Hurley wrote:
> 
> > On 7/5/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:
> > 
> > > ONLINE DEMO OF AJAX METHOD
> > > 
> > > &nbsp;&nbsp;&nbsp;[http://fortytwo.merseine.nu:3000/flatulent/ajax](http://fortytwo.merseine.nu:3000/flatulent/ajax) -- try to break it!
> > 
> > I can only guess 1 in 5 of the "images" -- that is one way to cut down  
> > on spammers 🙂

---

<div class="post-metadata">

**Author:** ![Lionel\_Bouton](https://avatars.discourse-cdn.com/v4/letter/l/a88e4f/32.png) [@Lionel\_Bouton](https://rubytalk.org/u/Lionel_Bouton)\
**Post date:** [5 July 2007 13:16 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/5 "2007-07-05T13:16:31Z")

</div>

Patrick Hurley wrote the following on 05.07.2007 14:51 :

> > ONLINE DEMO OF AJAX METHOD
> > 
> > &nbsp;&nbsp;&nbsp;[http://fortytwo.merseine.nu:3000/flatulent/ajax](http://fortytwo.merseine.nu:3000/flatulent/ajax) -- try to break it!
> 
> I can only guess 1 in 5 of the "images" -- that is one way to cut down  
> on spammers 🙂  
> pth

I can confirm there are sometimes problems with the output (on Firefox 2.0.0.4). 1/4 of the time no recognizable character is shown.

Other notes:

I don't like the obstrusive Ajax feature at all (I use NoScript...): what's the benefit? Spammers trying to get around captcha can easily make an extra step and make XmlHTTPRequests too... From what I understand, there's at least a way to generate pure HTML, but I'd still like to understand why AJAX is an option.

I'm not sure why there are nested span in the captcha :

\<span\>\<span\>\<span\>\<span\>\<span\>\<span\>\<span\>&nbsp;\</span\>\</span\>\</span\>\</span\>\</span\>\</span\>\</span\>

?! Is it to accomodate rendering bugs?

There are &nbsp; in a \<pre\> with style='...,white-space:pre,... '. Seems the author \*really\* wants to be sure that spaces can not be rendered with newlines...

Lionel

> **···**
>
> > On 7/5/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [5 July 2007 15:05 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/6 "2007-07-05T15:05:33Z")

</div>

yeah - it's a redering bug but i can't figure our what causes it - thanks!

-a

> **···**
>
> On Jul 5, 2007, at 6:51 AM, Patrick Hurley wrote:
> 
> > I can only guess 1 in 5 of the "images" -- that is one way to cut down  
> > on spammers 🙂  
> > pth
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 17:17 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/7 "2007-07-07T17:17:40Z")

</div>

hmmm - not for me?

cfp:~ \> ruby a.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user system total real  
generate: 0.140000 0.020000 0.170000 ( 0.178928)  
setup: 0.020000 0.000000 0.020000 ( 0.022138)  
break: Flatulent.version : 0.0.4  
a.rb:63: failed on attempt 1 (RuntimeError)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:63  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:59

cfp:~ \> cat a.rb  
require 'flatulent'  
require 'benchmark'  
require 'flatulent'

class Deflatulent  
&nbsp;&nbsp;&nbsp;def initialize font="big"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font +".flf"))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typesetter = Text::Figlet::Typesetter.new font  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;letters = ('A'..'Z').to\_a + ('1'..'9').to\_a  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array = letters.map{|letter| [letter, gen\_figlet\_lines\_array(typesetter[letter])] }  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def deflatulent string  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if string =~ /\<pre id='[a-zA-Z0-9\_-]+\_element' style='.\*?'\>(.\*?)\<\/ pre\>/m  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = $1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[/\<\/?span\>/,''],["&nbsp;"," "],["\<br\>","\n"],["&lt;","\<"], ["&gt;","\>"],["&quot;",'"'],["&amp;","&"]].each do |args|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.gsub!(\*args)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width = string.index("\n")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.tr!("\n","")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution =

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array.each do |(letter,(length,lines))|

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re = "(?="  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|line| re \<\< line \<\< ".{#{width-length}}" }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re \<\< ")"

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.scan(Regexp.new(re, Regexp::MULTILINE)) do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution[$~.begin(0) % width] = letter  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution.join  
&nbsp;&nbsp;&nbsp;end

private  
&nbsp;&nbsp;&nbsp;def gen\_figlet\_lines\_array string  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines = string.split("\n")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.shift while lines.first.strip.empty?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.pop while lines.last.strip.empty?

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ? '.' : Regexp.escape(q))}.join}]  
&nbsp;&nbsp;&nbsp;end  
end

defl = html = code = nil  
pairs = Array.new(200)

GC.disable  
i = 0

begin  
&nbsp;&nbsp;&nbsp;Benchmark.bm(13) do |x|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate:") { flat = Flatulent.new; html = flat.form; code = flat.string }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("setup:") { defl = Deflatulent.new }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break:") { raise "failed on attempt #{ i }" unless defl.deflatulent(html) == code }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate 200:") { 200.times{|index| flat = Flatulent.new; pairs[index] = [flat.form,flat.string] } }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break 200:") { pairs.map{|(html,code)| raise unless defl.deflatulent(html) == code } }  
&nbsp;&nbsp;&nbsp;end  
ensure  
&nbsp;&nbsp;&nbsp;puts "Flatulent.version : #{ Flatulent.version }"  
end

nevertheless, i'm not for one second claiming flatulent is ready for prime time. however, i will state that i think it's quite a bit of work if you use it in the intended way, which is for the html to make an ajax call to get the flatulent source because this make said source available only to javascript. no doubt someone could crack it from there, but the latest version adds vertical and horizontal offset to each char. my version is turning that source into a png. anyhow, the attention is welcome - but next time send a patch! 😉

-a

> **···**
>
> On Jul 6, 2007, at 12:29 PM, jannis@harderweb.de wrote:
> 
> > The method used in this captcha is very is to break. In fact I can  
> > solve  
> > the captchas 6 times as fast as it takes to generate them (locally)  
> > in  
> > only 63 lines of code. I do this by generating a regexp for each  
> > possible  
> > character. As the characters don't get damaged by the noise (as they  
> > get  
> > in most image bases captchas) this works all of the time.
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![Chris\_Carter](https://avatars.discourse-cdn.com/v4/letter/c/a8b319/32.png) [@Chris\_Carter](https://rubytalk.org/u/Chris_Carter)\
**Post date:** [7 July 2007 17:25 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/8 "2007-07-07T17:25:41Z")

</div>

Ara,  
That is because you set defl and flat inside a block, without setting  
the variables to nil before the block is executed, so they stay  
existing for the actual decode stage.

> **···**
>
> On 7/7/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:
> 
> > On Jul 6, 2007, at 12:29 PM, jannis@harderweb.de wrote:
> > 
> > \> The method used in this captcha is very is to break. In fact I can  
> > \> solve  
> > \> the captchas 6 times as fast as it takes to generate them (locally)  
> > \> in  
> > \> only 63 lines of code. I do this by generating a regexp for each  
> > \> possible  
> > \> character. As the characters don't get damaged by the noise (as they  
> > \> get  
> > \> in most image bases captchas) this works all of the time.
> > 
> > hmmm - not for me?
> > 
> > cfp:~ \> ruby a.rb  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user system total real  
> > generate: 0.140000 0.020000 0.170000 ( 0.178928)  
> > setup: 0.020000 0.000000 0.020000 ( 0.022138)  
> > break: Flatulent.version : 0.0.4  
> > a.rb:63: failed on attempt 1 (RuntimeError)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:63  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:59
> > 
> > cfp:~ \> cat a.rb  
> > require 'flatulent'  
> > require 'benchmark'  
> > require 'flatulent'
> > 
> > class Deflatulent  
> > &nbsp;&nbsp;&nbsp;def initialize font="big"  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font  
> > +".flf"))  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typesetter = Text::Figlet::Typesetter.new font  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;letters = ('A'..'Z').to\_a + ('1'..'9').to\_a  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array = letters.map{|letter| [letter,  
> > gen\_figlet\_lines\_array(typesetter[letter])] }  
> > &nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;def deflatulent string  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if string =~ /\<pre id='[a-zA-Z0-9\_-]+\_element' style='.\*?'\>(.\*?)\<  
> > \/ pre\>/m  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = $1  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[/\<\/?span\>/,''],["&nbsp;"," "],["\<br\>","\n"],["&lt;","\<"],  
> > ["&gt;","\>"],["&quot;",'"'],["&amp;","&"]].each do |args|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.gsub!(\*args)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width = string.index("\n")  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.tr!("\n","")  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution =
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@lines\_array.each do |(letter,(length,lines))|
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re = "(?="  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|line| re \<\< line \<\< ".{#{width-length}}" }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;re \<\< ")"
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.scan(Regexp.new(re, Regexp::MULTILINE)) do  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution[$~.begin(0) % width] = letter  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution.join  
> > &nbsp;&nbsp;&nbsp;end
> > 
> > private  
> > &nbsp;&nbsp;&nbsp;def gen\_figlet\_lines\_array string  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines = string.split("\n")  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.shift while lines.first.strip.empty?  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.pop while lines.last.strip.empty?
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ?  
> > '.' : Regexp.escape(q))}.join}]  
> > &nbsp;&nbsp;&nbsp;end  
> > end
> > 
> > defl = html = code = nil  
> > pairs = Array.new(200)
> > 
> > GC.disable  
> > i = 0
> > 
> > begin  
> > &nbsp;&nbsp;&nbsp;Benchmark.bm(13) do |x|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate:") { flat = Flatulent.new; html = flat.form;  
> > code = flat.string }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("setup:") { defl = Deflatulent.new }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break:") { raise "failed on attempt #{ i }" unless  
> > defl.deflatulent(html) == code }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate 200:") { 200.times{|index| flat =  
> > Flatulent.new; pairs[index] = [flat.form,flat.string] } }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break 200:") { pairs.map{|(html,code)| raise unless  
> > defl.deflatulent(html) == code } }  
> > &nbsp;&nbsp;&nbsp;end  
> > ensure  
> > &nbsp;&nbsp;&nbsp;puts "Flatulent.version : #{ Flatulent.version }"  
> > end
> > 
> > nevertheless, i'm not for one second claiming flatulent is ready for  
> > prime time. however, i will state that i think it's quite a bit of  
> > work if you use it in the intended way, which is for the html to make  
> > an ajax call to get the flatulent source because this make said  
> > source available only to javascript. no doubt someone could crack it  
> > from there, but the latest version adds vertical and horizontal  
> > offset to each char. my version is turning that source into a png.  
> > anyhow, the attention is welcome - but next time send a patch! 😉
> > 
> > -a  
> > --  
> > we can deny everything, except that we have the possibility of being  
> > better. simply reflect on that.  
> > h.h. the 14th dalai lama
> 
> --  
> Chris Carter  
> concentrationstudios.com  
> brynmawrcs.com

---

<div class="post-metadata">

**Author:** ![Jannis\_Harder](https://avatars.discourse-cdn.com/v4/letter/j/5e9695/32.png) [@Jannis\_Harder](https://rubytalk.org/u/Jannis_Harder)\
**Post date:** [7 July 2007 17:41 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/9 "2007-07-07T17:41:09Z")

</div>

It seems that google groups added line breaks inside the regexp that  
somehow  
turned into spaces for you... try it without the space before pre...  
if that  
doesn't work I can upload my code somewhere...

> **···**
>
> On 7 Jul., 19:17, "ara.t.howard" \<ara.t.how...@gmail.com\> wrote:
> 
> > &nbsp;&nbsp;&nbsp;def deflatulent string  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if string =~ /\<pre id='[a-zA-Z0-9\_-]+\_element' style='.\*?'\>(.\*?)\<  
> > \/ pre\>/m ########## there is a space before pre  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = $1  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[/\<\/?span\>/,''],["&nbsp;"," "],["\<br\>","\n"],["&lt;","\<"],  
> > ["&gt;","\>"],["&quot;",'"'],["&amp;","&"]].each do |args|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.gsub!(\*args)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> --  
> Jannis Harder

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 17:35 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/10 "2007-07-07T17:35:18Z")

</div>

??

# defl = html = code = nil ### irrelevant  
pairs = Array.new(200)

GC.disable  
i = 0

begin  
&nbsp;&nbsp;&nbsp;Benchmark.bm(13) do |x|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defl = html = code = nil ### irrelevant  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate:") { flat = Flatulent.new; html = flat.form; code = flat.string }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("setup:") { defl = Deflatulent.new }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break:") { raise "failed on attempt #{ i }" unless defl.deflatulent(html) == code }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate 200:") { 200.times{|index| flat = Flatulent.new; pairs[index] = [flat.form,flat.string] } }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break 200:") { pairs.map{|(html,code)| raise unless defl.deflatulent(html) == code } }  
&nbsp;&nbsp;&nbsp;end  
ensure  
&nbsp;&nbsp;&nbsp;puts "Flatulent.version : #{ Flatulent.version }"  
end

it fails on the very first attempt:

cfp:~ \> ruby a.rb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user system total real  
generate: 0.140000 0.020000 0.170000 ( 0.179983)  
setup: 0.020000 0.000000 0.020000 ( 0.022315)  
break: Flatulent.version : 0.0.4  
a.rb:64: failed on attempt 1 (RuntimeError)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:64  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:59

cheers.

-a

> **···**
>
> On Jul 7, 2007, at 11:25 AM, Chris Carter wrote:
> 
> > Ara,  
> > That is because you set defl and flat inside a block, without setting  
> > the variables to nil before the block is executed, so they stay  
> > existing for the actual decode stage.
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 17:52 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/11 "2007-07-07T17:52:49Z")

</div>

my version doesn't seem to have line breaks - here it is:

&nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/5164285](http://drawohara.tumblr.com/post/5164285)

thanks for having a go - i'm not sure this can be made to work, but i'm still very interested in an ImageMagick-less captcha system.

cheers

-a

> **···**
>
> On Jul 7, 2007, at 11:41 AM, jannis@harderweb.de wrote:
> 
> > On 7 Jul., 19:17, "ara.t.howard" \<ara.t.how...@gmail.com\> wrote:
> > 
> > > &nbsp;&nbsp;&nbsp;def deflatulent string  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if string =~ /\<pre id='[a-zA-Z0-9\_-]+\_element' style='.\*?'\>(.\*?)\<  
> > > \/ pre\>/m ########## there is a space before pre  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = $1  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[[/\<\/?span\>/,''],["&nbsp;"," "],["\<br\>","\n"],["&lt;","\<"],  
> > > ["&gt;","\>"],["&quot;",'"'],["&amp;","&"]].each do |args|  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.gsub!(\*args)  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> > 
> > It seems that google groups added line breaks inside the regexp that  
> > somehow  
> > turned into spaces for you... try it without the space before pre...  
> > if that  
> > doesn't work I can upload my code somewhere...  
> > --  
> > Jannis Harder
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![Chris\_Carter](https://avatars.discourse-cdn.com/v4/letter/c/a8b319/32.png) [@Chris\_Carter](https://rubytalk.org/u/Chris_Carter)\
**Post date:** [7 July 2007 17:42 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/12 "2007-07-07T17:42:49Z")

</div>

Huh, I guess I am blind then...

> **···**
>
> On 7/7/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:
> 
> > On Jul 7, 2007, at 11:25 AM, Chris Carter wrote:
> > 
> > \> Ara,  
> > \> That is because you set defl and flat inside a block, without setting  
> > \> the variables to nil before the block is executed, so they stay  
> > \> existing for the actual decode stage.  
> > \>
> > 
> > ??
> > 
> > # defl = html = code = nil ### irrelevant  
> > pairs = Array.new(200)
> > 
> > GC.disable  
> > i = 0
> > 
> > begin  
> > &nbsp;&nbsp;&nbsp;Benchmark.bm(13) do |x|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i += 1  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defl = html = code = nil ### irrelevant  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate:") { flat = Flatulent.new; html = flat.form;  
> > code = flat.string }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("setup:") { defl = Deflatulent.new }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break:") { raise "failed on attempt #{ i }" unless  
> > defl.deflatulent(html) == code }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("generate 200:") { 200.times{|index| flat =  
> > Flatulent.new; pairs[index] = [flat.form,flat.string] } }  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x.report("break 200:") { pairs.map{|(html,code)| raise unless  
> > defl.deflatulent(html) == code } }  
> > &nbsp;&nbsp;&nbsp;end  
> > ensure  
> > &nbsp;&nbsp;&nbsp;puts "Flatulent.version : #{ Flatulent.version }"  
> > end
> > 
> > it fails on the very first attempt:
> > 
> > cfp:~ \> ruby a.rb  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user system total real  
> > generate: 0.140000 0.020000 0.170000 ( 0.179983)  
> > setup: 0.020000 0.000000 0.020000 ( 0.022315)  
> > break: Flatulent.version : 0.0.4  
> > a.rb:64: failed on attempt 1 (RuntimeError)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:377:in `report'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:64  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:177:in `benchmark'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /opt/local/lib/ruby/1.8/benchmark.rb:207:in `bm'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from a.rb:59
> > 
> > cheers.
> > 
> > -a
> 
> --  
> Chris Carter  
> concentrationstudios.com  
> brynmawrcs.com

---

<div class="post-metadata">

**Author:** ![Jannis\_Harder](https://avatars.discourse-cdn.com/v4/letter/j/5e9695/32.png) [@Jannis\_Harder](https://rubytalk.org/u/Jannis_Harder)\
**Post date:** [7 July 2007 18:31 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/13 "2007-07-07T18:31:00Z")

</div>

That version still has an additional space in the html stripping  
regexp. I think it was inserted by google groups when I pasted that  
message (because it displays a line break here). Anyway here is a  
version without any additional characters.  
&nbsp;&nbsp;[http://pastie.caboo.se/76886](http://pastie.caboo.se/76886)  
To avoid this cracking method one could change at least one of the  
characters of each letter. This would break simple regexp attacks. But  
I still think this wouldn't be too difficult to break. But even if  
there is a simple way to crack a captcha it will stop most of the  
spambots so I'm not saying this is something useless.

> **···**
>
> On 7 Jul., 19:52, "ara.t.howard" \<ara.t.how...@gmail.com\> wrote:
> 
> > my version doesn't seem to have line breaks - here it is:
> > 
> > &nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/5164285](http://drawohara.tumblr.com/post/5164285)
> 
> --  
> Jannis Harder

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 17:53 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/14 "2007-07-07T17:53:29Z")

</div>

i wouldn't say that 😉

did i understand you correctly? i wasn't sure i did...

cheers.

-a

> **···**
>
> On Jul 7, 2007, at 11:42 AM, Chris Carter wrote:
> 
> > Huh, I guess I am blind then...
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 18:43 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/15 "2007-07-07T18:43:46Z")

</div>

cool. i've updated here

&nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/5164285](http://drawohara.tumblr.com/post/5164285)

but it's still failing (yay!)

note the new output - sample at bottom of above post. it's much harder.

fun stuff!

-a

> **···**
>
> On Jul 7, 2007, at 12:31 PM, jannis@harderweb.de wrote:
> 
> > That version still has an additional space in the html stripping  
> > regexp. I think it was inserted by google groups when I pasted that  
> > message (because it displays a line break here). Anyway here is a  
> > version without any additional characters.  
> > &nbsp;&nbsp;[http://pastie.caboo.se/76886](http://pastie.caboo.se/76886)  
> > To avoid this cracking method one could change at least one of the  
> > characters of each letter. This would break simple regexp attacks. But  
> > I still think this wouldn't be too difficult to break. But even if  
> > there is a simple way to crack a captcha it will stop most of the  
> > spambots so I'm not saying this is something useless.
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![Jannis\_Harder](https://avatars.discourse-cdn.com/v4/letter/j/5e9695/32.png) [@Jannis\_Harder](https://rubytalk.org/u/Jannis_Harder)\
**Post date:** [7 July 2007 21:28 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/16 "2007-07-07T21:28:38Z")

</div>

Testing with an updated flatulent version is cheating 😉 .... but I  
love challenge, so here is an updated version successfully breaking  
flatulent 0.0.4. 9 times faster than generating.  
[http://pastie.caboo.se/76923](http://pastie.caboo.se/76923)  
This version should be able to break 0.0.3 and 0.0.4.

> **···**
>
> On 7 Jul., 20:43, "ara.t.howard" \<ara.t.how...@gmail.com\> wrote:
> 
> > cool. i've updated here
> > 
> > &nbsp;&nbsp;&nbsp;[http://drawohara.tumblr.com/post/5164285](http://drawohara.tumblr.com/post/5164285)
> > 
> > but it's still failing (yay!)
> > 
> > note the new output - sample at bottom of above post. it's much harder.
> 
> --  
> Jannis Harder

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [7 July 2007 22:04 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/17 "2007-07-07T22:04:13Z")

</div>

> Testing with an updated flatulent version is cheating 😉 .... but I  
> love challenge,

have a whack at this then

&nbsp;&nbsp;[http://drawohara.tumblr.com/post/5126190](http://drawohara.tumblr.com/post/5126190)

😉

> so here is an updated version successfully breaking  
> flatulent 0.0.4. 9 times faster than generating.  
> [http://pastie.caboo.se/76923](http://pastie.caboo.se/76923)  
> This version should be able to break 0.0.3 and 0.0.4.

awesome. i'll post it on my blog later. guess i'll have to make an image after all ;-(

-a

> **···**
>
> On Jul 7, 2007, at 3:28 PM, jannis@harderweb.de wrote:  
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

---

<div class="post-metadata">

**Author:** ![Patrick\_Hurley1](https://avatars.discourse-cdn.com/v4/letter/p/b77776/32.png) [@Patrick\_Hurley1](https://rubytalk.org/u/Patrick_Hurley1)\
**Post date:** [8 July 2007 14:07 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/18 "2007-07-08T14:07:21Z")

</div>

I think if you can kern the letters into each other, finding some way  
to make them overlap, much of this approach (regex) will be defeated.  
Couple that with using more fonts will increase the difficulty of  
solving the problem.

pth

> **···**
>
> On 7/7/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:
> 
> > awesome. i'll post it on my blog later. guess i'll have to make an  
> > image after all ;-(

---

<div class="post-metadata">

**Author:** ![botp1](https://avatars.discourse-cdn.com/v4/letter/b/6de8d8/32.png) [@botp1](https://rubytalk.org/u/botp1)\
**Post date:** [8 July 2007 15:00 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/19 "2007-07-08T15:00:30Z")

</div>

i agree.  
ara, i've seen your raptcha. how about something like that but in text  
mode. The pixel would represent a character (that varies). it's like  
converting from bmp to ascii art. Your text captcha must be able to  
display on text browsers, otherwise it has no use for me like most  
other captchas. yes, i'm usually a text browser 😉

> **···**
>
> On 7/8/07, Patrick Hurley \<phurley@gmail.com\> wrote:
> 
> > On 7/7/07, ara.t.howard \<ara.t.howard@gmail.com\> wrote:  
> > \> awesome. i'll post it on my blog later. guess i'll have to make an  
> > \> image after all ;-(
> > 
> > I think if you can kern the letters into each other, finding some way  
> > to make them overlap, much of this approach (regex) will be defeated.  
> > Couple that with using more fonts will increase the difficulty of  
> > solving the problem.

---

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [9 July 2007 14:39 UTC](https://rubytalk.org/t/ann-flatulent-0-0-3/38859/20 "2007-07-09T14:39:27Z")

</div>

i've played with quite a few things, but not overlapping the the letters. i'll give it a shot.

thanks!

-a

> **···**
>
> On Jul 8, 2007, at 8:07 AM, Patrick Hurley wrote:
> 
> > I think if you can kern the letters into each other, finding some way  
> > to make them overlap, much of this approach (regex) will be defeated.  
> > Couple that with using more fonts will increase the difficulty of  
> > solving the problem.
> 
> --  
> we can deny everything, except that we have the possibility of being better. simply reflect on that.  
> h.h. the 14th dalai lama

[Next page](https://rubytalk.org/t/ann-flatulent-0-0-3/38859.md?page=2)
