3
Does emit a CAPTCHA graphic and form fields, which allows to tell real
5
Though a textual description is generated as well, this sort of access
6
restriction will knock out visually impaired users, and frustrate all
7
others anyhow. Therefore this should only be used as last resort for
8
defending against spambots. Because of the readable text and the used
9
colorspaces this is a weak implementation, not completely OCR-secure.
11
captcha::form () will return a html string to be inserted into textarea/
12
[save] <forms> and alike. User input is veryfied with captcha::check ().
13
You should leave the sample COLLEGE.ttf next to this script, else you
14
have to define the _FONT_DIR constant correctly. Use only 1 font type.
16
Creates temporary files, which however get purged automatically after
19
Public Domain, available via http://freshmeat.net/p/captchaphp
21
Modified to work with WebCalendar by Ray Jones
22
Added translation function calls
23
Formatting to display properly within our layout
24
Disabled textual_riddle function
29
define( 'EWIKI_FONT_DIR', dirname(__FILE__)); // which fonts to use
30
define( 'CAPTCHA_INVERSE', 0); // white or black(=1)
31
define( 'CAPTCHA_TIMEOUT', 5000); // in seconds (=max 4 hours)
32
define( 'CAPTCHA_MAXSIZE', 4500); // preferred image size
33
define( 'CAPTCHA_COOKIE', 'captcha_solved'); // to unlock captcha protection
34
define( 'CAPTCHA_DATA_URLS', 0); // RFC2397-URLs exclude MSIE users
35
define( 'CAPTCHA_TEMP_DIR', 'images/captcha');
37
/* static - (you could instantiate it, but...) */
41
/* gets parameter from $_REQUEST[] array (POST vars) and so can
42
verify input, @returns boolean
45
$to = (int)(time ()/1000000);
46
if ( ! empty ( $_COOKIE[CAPTCHA_COOKIE] ) && $_COOKIE[CAPTCHA_COOKIE] == $to) {
49
if (($hash = $_REQUEST['captcha_hash'])
50
and ($pw = trim($_REQUEST['captcha_input']))) {
51
$r = (captcha::hash($pw)==$hash) || (captcha::hash($pw,-1)==$hash);
53
setcookie(CAPTCHA_COOKIE, $to, time ()+1000000);
60
/* yields <input> fields html string (no complete form), with captcha
61
image already embedded as data:-URI
65
#-- stop if user already verified
66
if ( ! empty ( $_COOKIE[CAPTCHA_COOKIE] ) &&
67
$_COOKIE[CAPTCHA_COOKIE] == (int)(time ()/1000000)) {
71
$title = translate ( 'Enter Characters Seen in Graphic' );
72
$more = translate ( 'Enter the correct letters and numbers from the image into the text box...' );
73
#-- prepare image text
74
$pw = captcha::mkpass ();
75
$hash = captcha::hash($pw);
76
//$alt = htmlentities(captcha::textual_riddle($pw));
80
$img = captcha::image($pw, 200, 60, CAPTCHA_INVERSE, CAPTCHA_MAXSIZE);
81
if (CAPTCHA_DATA_URLS && !strpos ('MSIE', $_SERVER['HTTP_USER_AGENT'])) {
82
$img_fn = 'data:image/jpeg;base64,' . base64_encode($img);
85
$img_fn = CAPTCHA_TEMP_DIR . '/' . captcha::store_image($img) . '.jpg';
91
<fieldset style="width:420px">
92
<legend>' . translate ('Challenge/Response') .'</legend>
93
<table border="0" summary="captcha input" width="400px"><tr>
94
<td colspan="2"><small>'.$more.'</small></td></tr><tr>
95
<td><img name="captcha_image" id="captcha_image" src="' .$img_fn.
96
'" height="60" width="200" alt="' .$alt. '" /></td>
97
<td>'.$title. '<br /><input name="captcha_hash" type="hidden" value="'.$hash. '" />
98
<input name="captcha_input" type="text" size="7" maxlength="16" style="height:46px;font-size:34px; font-weight:bold;" />
107
/* generates alternative (non-graphic), human-understandable
108
representation of the passphrase
110
function textual_riddle($phrase) {
111
$symbols0 = '"\'-/_:';
112
$symbols1 = array ("\n,", "\n;", ";", "\n&", "\n-", ",", ",", "\nand then", "\nfollowed by", "\nand", "\nand not a\n\"".chr(65+rand(0,26))."\",\nbut");
113
$s = "Guess the letters and numbers\n(passphrase riddle)\n--\n";
114
for ($p=0; $p<strlen($phrase); $p++) {
119
$i = $symbols0[rand(0,strlen($symbols0)-1)];
124
$type = ($c >= 'a' ? "small " : "");
127
$c2 = chr((ord($c) & 0x5F) + $n);
129
while (($c2 < 'A') || ($c2 > 'Z'));
132
$add .= "$type'$c2' +$n letters";
135
$add .= "$n chars before $type$c2";
143
do { $x = rand(1, 10); } while (!$x);
146
$add = "($add * $x)"; $n *= $x;
150
$add = "($add / $x)"; $n /= $x;
152
elseif ( ! empty ( $sel ) && $sel % 2) {
153
$add = "($add + $x)"; $n += $x;
156
$add = "($add - $x)"; $n -= $x;
163
$s .= $symbols1[rand(0,count($symbols1)-1)] . "\n";
169
/* returns jpeg file stream with unscannable letters encoded
170
in front of colorful disturbing background
172
function image($phrase, $width=200, $height=60, $inverse=0, $maxsize=0xFFFFF) {
174
#-- initialize in-memory image with gd library
175
srand(microtime ()*21017);
176
$img = imagecreatetruecolor($width, $height);
177
$R = $inverse ? 0xFF : 0x00;
178
imagefilledrectangle($img, 0,0, $width,$height, captcha::random_color($img, 222^$R, 255^$R));
179
$c1 = rand(150^$R, 185^$R);
180
$c2 = rand(195^$R, 230^$R);
186
$fonts += glob(EWIKI_FONT_DIR."/*.ttf");
192
while ($x < $width) {
193
imagefilledrectangle($img, $x, 0, $x+=$wd, $height, captcha::random_color($img, 222^$R, 255^$R));
194
$wd += max(10, rand(0, 20) - 10);
197
#-- make interesting background I, lines
201
for ($x=0; $x<$width; $x+=(int)$wd) {
202
if ($x < $width) { // verical
203
imageline($img, $x+$w1, 0, $x+$w2, $height-1, captcha::random_color($img,$c1,$c2));
205
if ($x < $height) { // horizontally ("y")
206
imageline($img, 0, $x-$w2, $width-1, $x-$w1, captcha::random_color($img,$c1,$c2));
208
$wd += rand(0,8) - 4;
209
if ($wd < 1) { $wd = 2; }
210
$w1 += rand(0,8) - 4;
211
$w2 += rand(0,8) - 4;
212
if (($x > $height) && ($y > $height)) {
217
#-- more disturbing II, random letters
218
$limit = rand(30,90);
219
for ($n=0; $n<$limit; $n++) {
222
$letter .= chr(rand(31,125)); // random symbol
224
$size = rand(5, $height/2);
225
$half = (int) ($size / 2);
226
$x = rand(-$half, $width+$half);
227
$y = rand(+$half, $height);
228
$rotation = rand(60, 300);
229
$c1 = captcha::random_color($img, 130^$R, 240^$R);
230
$font = $fonts[rand(0, count($fonts)-1)];
231
imagettftext($img, $size, $rotation, $x, $y, $c1, $font, $letter);
234
#-- add the real text to it
235
$len = strlen($phrase);
237
$w2 = $width / ($len+1);
238
for ($p=0; $p<$len; $p++) {
239
$letter = $phrase[$p];
240
$size = rand(18, $height/2.2);
241
$half = (int) $size / 2;
242
$rotation = rand(-33, 33);
243
$y = rand($size+3, $height-3);
245
$w1 += rand(-$width/90, $width/40); // @BUG: last char could be +30 pixel outside of image
246
$font = $fonts[rand(0, count($fonts)-1)];
247
$r=rand(30,99); $g=rand(30,99); $b=rand(30,99); // two colors for shadow
248
$c1 = imagecolorallocate($img, $r*1^$R, $g*1^$R, $b*1^$R);
249
$c2 = imagecolorallocate($img, $r*2^$R, $g*2^$R, $b*2^$R);
250
imagettftext($img, $size, $rotation, $x+1, $y, $c2, $font, $letter);
251
imagettftext($img, $size, $rotation, $x, $y-1, $c1, $font, $letter);
254
#-- let JFIF stream be generated
258
ob_start (); ob_implicit_flush(0);
259
imagejpeg($img, "", (int)$quality);
260
$jpeg = ob_get_contents (); ob_end_clean ();
261
$size = strlen($jpeg);
262
$s_debug[] = ((int)($quality*10)/10) . "%=$size";
263
$quality = $quality * ($maxsize/$size) * 0.93 - 1.7; // -($quality/7.222)*
265
while (($size > $maxsize) && ($quality >= 16));
272
function random_color($img, $a,$b) {
273
return imagecolorallocate($img, rand($a,$b), rand($a,$b), rand($a,$b));
277
/* creates temporary file, returns basename */
278
function store_image($data) {
279
$dir = CAPTCHA_TEMP_DIR;
283
if (!file_exists($dir)) {
284
mkdir($dir) && chmod($dir, 0777);
287
#-- remove stale files
288
if ($dh = opendir($dir)) {
289
$t_kill = time () - CAPTCHA_TIMEOUT;
290
while($fn = readdir($dh)) if ($fn[0] != ".") {
291
if (filemtime("$dir/$fn") < $t_kill) {
298
fwrite($f = fopen("$dir/$id.jpg", 'wb'), $data) && fclose($f);
303
function get_image($id) {
304
$dir = CAPTCHA_TEMP_DIR;
305
$fn = "$dir/$id.jpg";
307
if (preg_match('/^\w+$/', $id)) {
308
header('Content-Type: image/jpeg');
315
/* unreversable hash from passphrase, with time () slice encoded */
316
function hash($text, $dtime=0) {
317
$text = strtolower($text);
318
$pfix = (int) (time () / CAPTCHA_TIMEOUT) + $dtime;
319
return md5("captcha::$pfix:$text::".__FILE__.":$_SERVER[SERVER_NAME]:80");
323
/* makes string of random letters for embedding into image and for
324
encoding as hash, later verification
328
for ($n=0; $n<10; $n++) {
329
$s .= chr(rand(0, 255));
331
$s = base64_encode($s); // base64-set, but filter out unwanted chars
332
$s = preg_replace("/[+\/=IG0ODQR]/i", "", $s); // (depends on YOUR font)
333
$s = substr ($s, 0, rand(5,7));
339
if (isset($_REQUEST['_tcf'])) {
340
captcha::get_image($_REQUEST['_tcf']);