Erlang captcha code for websites

Today I want to publish simple Erlang captcha library. It uses very simple algorithm - code & captcha image is generated and file name contains code itself. So, if file exists, than code entered correctly. Once code is entered, file is deleted. One can reload (in terms of web) or generate new code. In this case old file will be removed and new generated.

You can use Prefix parameter to identify sessions or even can create several captcha codes for single session. You can also define number of characters in the code via Len parameter and number of background lines by LineNum parameter.

Important!

  1. You can store images in any folder, but it should not be accessible from the web.
  2. This code potentially can leave  images in the storage folder. For example, if Prefix is generated randomly for each session and user decided not to enter captcha code, than image will not be removed. So, application should be aware of this or separate script should remove old captcha images.
-module(erlycaptcha).
-compile(export_all).

new(Prefix, Len, LineNum) ->
    Code = generate_rand(Len),
    FileName = Prefix++"_"++Code,

    File = io_lib:format("./captcha/~s.png",[FileName]),
    filelib:fold_files("./captcha", io_lib:format("~s_.{~p}.png", [Prefix, Len]), false, fun(F, Acc)-> io:format("File: ~p~n", [F]), file:delete(F), [F | Acc] end, []),
    
    Lines = lists:foldl(fun(_I, Acc) -> 
    	Acc ++  io_lib:format("Line ~p,0 ~p,100", [random:uniform(100), random:uniform(100)]) ++ " "
    end, "", lists:seq(1, LineNum)),
    
    Cmd = io_lib:format("convert -background '#FFFFFF' -fill '#000000' -size 100 -gravity Center -wave 5x100 -swirl 15 -font DejaVu-Serif-Book -pointsize 28 label:~s -draw '~s' ~s", [Code, Lines,File]),
    os:cmd(Cmd),

    {ok, BinPng} = file:read_file(File),
 
    BinPng.

check(Prefix, Code) ->
	FileName = Prefix++"_"++Code,
    File = io_lib:format("./captcha/~s.png",[FileName]),
    
	Res = filelib:is_regular(File),
	file:delete(File),
	Res.

generate_rand(Length) ->
	{A1,A2,A3} = now(),
    random:seed(A1, A2, A3),
    lists:foldl(fun(_I, Acc) -> 
    	Chars = "abcdefghijklmnopqrstuvwxyz0123456789",
    	Char = string:substr(Chars, random:uniform(length(Chars)), 1),
    	Char ++ Acc 
    end, "", lists:seq(1, Length)).

Brief ElryCaptcha usage example:

Eshell V5.8.4  (abort with ^G)
1> c(erlycaptcha).
{ok,erlycaptcha}
2> erlycaptcha:new("1", 3, 15).
File: "./captcha/1_bgu.png"
<<137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,100,
  0,0,0,44,16,4,0,0,0,...>>
3> erlycaptcha:check("1", "glh").
false
4> erlycaptcha:check("1", "qlh").
true 

Code can be downloaded from the GitHub: ErlyCaptcha

Posted by:
Enjoyed this post? Share and Leave a comment below, thanks! :)
Tags: ,