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
                   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
      63 deflatulent.rb
     604 /usr/local/lib/ruby/gems/1.8/gems/flatulent-0.0.3/lib/
flatulent.rb
     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|
  x.report("generate:") { flat = Flatulent.new; html = flat.form; code
= flat.string }
  x.report("setup:")    { defl = Deflatulent.new  }
  x.report("break:")    { raise unless defl.deflatulent(html) ==
code }

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

end
$ cat deflatulent.rb
require 'flatulent'

class Deflatulent

  def initialize font="big"
    font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font
+".flf"))
    typesetter = Text::Figlet::Typesetter.new font
    letters = ('A'..'Z').to_a + ('1'..'9').to_a
    @lines_array = letters.map{|letter| [letter,
gen_figlet_lines_array(typesetter[letter])] }
  end

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

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

    width = string.index("\n")
    string.tr!("\n","")
    solution = []

    @lines_array.each do |(letter,(length,lines))|

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

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

    solution.join
  end

private
  def gen_figlet_lines_array string
    lines = string.split("\n")
    lines.shift while lines.first.strip.empty?
    lines.pop   while lines.last.strip.empty?

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

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

end

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

--
Jannis Harder