corz.org uses cookies to remember that you've seen this notice explaining that corz.org uses cookies, okay!
<?php // ۞// text { encoding:utf-8 ; bom:no ; linebreaks:unix ; tabs:4sp ; }
/*
corz.org gd verify v0.7
This script handles your entire human-verification process.
It uses php+gd to generate a CAPTHCA-type image that users need to submit to
verify their human-ness. In the event it detects non-image capable clients,
or if the user just prefers it that way, a unique text-based "sounds-like"
verification system is also provided.
Examples are available, here:
https://corz.org/source/php/imaging/
;o)
(c) 2004->tomorrow! ~ cor + corz.org ;o)
Please view the license for this free software, here:
https://corz.org/free-scripts-licence.nfo
Note:
If you use this script to protect, say, a comment facility, and a user
successfully passes the verfication process, they would then be allowed
direct access to any other resources you are similarly protecting, for
example an email form. In other words, they only have to prove they are
human ONE TIME for your entire site.
*/
// a two-in-one page..
if (!empty($_GET['do_img'])) {
$verify = new verificationImage();
$verify->outputImage();
die;
}
class verificationImage {
// / / / / / / / / /
// public properties..
// width of image..
var $_width = 150;
// that other dimension.
var $_height = 90;
/*
Truecolor Image
Probably requires GD2 to use truecolor, but looks better.
having said that, the jaggedy lines of an 8-bit image may
actually be preferable, here. Your call..
*/
var $_truecolor = true;
/*
Image Format
Chose from 'png', 'jpeg', or 'gif'. clearly png is superior,
though, as above, this may not be what you want.
*/
var $_img_format = 'png';
/*
colors for the semi-transparent foiling strings..
use regular HTML hex values. the "#" is optional.
*/
var $_color_bg = '#ffffff'; // better to use use something that aids obfuscation
var $_front_col = '#414141'; // a dark dark grey
var $_color_1 = '#d500d5'; // magenta '#FF80FF'
var $_color_2 = '#f5a703'; // yellow '#FFFF00'
var $_color_3 = '#00cece'; // cyan '#80FFFF'
/*
translucency of foiling strings..
0-127 (127 is completely transparent)
I like 42, for obvious reasons..
*/
var $_translucency = 20;
/*
truetype font
this must live in the same folder as this script
or else be in some way accessible to php.
(see ampsig.com] default scheme for more details)
default is 'profont.ttf', a neat, mono, free truetype font
based on Monaco, designed especially for coding. I use it all over.
A more fancy font may be preferable (harder to machine-read)..
*/
//var $_font_face = 'profont.ttf'; // sometimes this fails :/
var $_font_face = 'profont.ttf'; // if so, do this. assuming profont.ttf is in the same directory!
// point size of tt font. 20-30 is good.
var $_font_size = 27;
// skew angle of text. 20 works well.
var $_angle = 20;
// how long a string?
// (default: 5)..
var $_text_length = 5;
/*
Flavour of string generation
Choose from 'md5', 'masher', or 'mish-mash'..
'masher' creates a random string of lower-case letters.
'md5' plucks a short segment from a random MD5 hash, a mix of lower-case
letters a-f, plus numbers, e.g. 'ac5e3b'.
'mish-mash' is my favourite, creating a nice mix of alpha-numerics, e.g.
'mG2tU'..
*/
var $_verify_flavour = 'mish-mash';
/*
Verification Method
Choose from 'image', 'text', or 'auto'.
Non image-capable browsers will certainly prefer text-based
authentication, though you may wish to use that all the time.
"auto" is best; gd-verify will query the browser capabilities and decide
for you.
Remember, in your calling script, you will need to either have an <img>
or <div> (or whatever text block) tag. Of course, in auto mode, you'll
need to know in advance which one to use..
Do $no_images = $verify->imageCapable(); $no_images will be true if they can
handle images, or false, if not. Now you know which kind of tag to use; <img>,
or <div>.
Note: you can also override this default by setting a $_GET or $_POST
variable, "text-verify" to true.
*/
var $_verify_method = 'auto';
// / / / / / / / /
// private..
var $_version = '0.3.2';
var $_text = '';
var $_img = null;
var $_is_verified = null;
var $_imageCapable = true;
// da constructor..
function verificationImage() {
// just in case..
@session_start();
if (isset($_SESSION['gdv']['gd_verified'])) {
$this->_is_verified = $_SESSION['gdv']['gd_verified'];
}
}
function getVerification() {
// already verified?
if ($this->_is_verified == true) {
return true;
}
if (isset($_POST['gd_string'])) {
if ($this->verify_string($_POST['gd_string']) == true) {
$_SESSION['gdv']['gd_verified'] = $this->_is_verified = true;
return true;
}
}
$_SESSION['gdv']['gd_verified'] = null;
return false;
}
// do they match?..
function verify_string($img_str) {
if (isset($_SESSION['gdv']['img_str']) and ($_SESSION['gdv']['img_str'] === $img_str)) {
return true;
} else {
return false;
}
}
function make_string() {
$string = '';
switch($this->_verify_flavour) {
case 'md5':
$string = substr(md5(mt_rand()), 12, $this->_text_length);
break;
case 'masher':
for($i = 0; $i < $this->_text_length ; $i++) {
$string .= chr(mt_rand(97,122)); // lower-case
}
break;
default: // 'mish-mash':
$strings = array();
for($i = 0; $i < $this->_text_length ; $i++) {
$strings[] = chr(mt_rand(97,122)); // lower case
$strings[] = chr(mt_rand(65,90)); // UPPER CASE
$strings[] = chr(mt_rand(48,57)); // 0123456789
}
$pluck = array_rand($strings, $this->_text_length);
foreach ($pluck as $key => $value) {
$string .= $strings[$key];
}
}
$_SESSION['gdv']['img_str'] = $string;
return $string;
}
function doInput() {
return '<input type="text" id="gd_string" name="gd_string" title="Enter Code Here" autofocus="autofocus" />';
}
function outputImage() {
// some text..
$this->_text = $this->make_string();
$do_text_auth = false;
switch ($this->_verify_method) {
case 'image':
break;
case 'text':
$do_text_auth = true;
break;
case 'auto':
if (!$this->imageCapable()) { $do_text_auth = true; }
}
// $_GET override..
//
if (isset($_REQUEST['text-verify']) and $_REQUEST['text-verify'] == 1) { $do_text_auth = true; }
// assign colors from _GET variables..
if (isset($_GET['bg']) and $_GET['bg'] != '') { $this->_color_bg = $_GET['bg']; }
if (isset($_GET['txtcol']) and $_GET['txtcol'] != '') { $this->_front_col = $_GET['txtcol']; }
if (isset($_GET['col1']) and $_GET['col1'] != '') { $this->_color_1 = $_GET['col1']; }
if (isset($_GET['col2']) and $_GET['col2'] != '') { $this->_color_2 = $_GET['col2']; }
if (isset($_GET['col3']) and $_GET['col3'] != '') { $this->_color_3 = $_GET['col3']; }
if (isset($_GET['width']) and $_GET['width'] != '') { $this->_width = $_GET['width']; }
if (isset($_GET['height']) and $_GET['height'] != '') { $this->_height = $_GET['height']; }
if (isset($_GET['font_size']) and $_GET['font_size'] != '') { $this->_font_size = $_GET['font_size']; }
if ($this->anywhere_in_array('jpg', $_GET)) { $this->_img_format = 'jpeg'; }
if ($this->anywhere_in_array('png', $_GET)) { $this->_img_format = 'png'; } // could roll into one efficient format override function
// spit out the image and die..
// we'll send them a text-based verification..
//
if ($do_text_auth) {
$translate_array = array(
// we could also make this a multi-dimensional array, and use one of a
// *selection* of, say, "won, one, w0n, number one, wun, numero uno, etc."
// custom captchas are the way to go.
"0" => "zeehrow",
"1" => "wun",
"2" => "too!",
"3" => "thuree",
"4" => "fore",
"5" => "f-hive",
"6" => "sicks",
"7" => "s-heaven",
"8" => "ate",
"9" => "nein",
"a" => "lower-case ay",
"b" => "lower-case bee",
"c" => "lower-case see",
"d" => "lower-case dee",
"e" => "lower-case ee",
"f" => "lower-case eff",
"g" => "lower-case gee",
"h" => "lower-case aitch",
"i" => "lower-case aye",
"j" => "lower-case jay",
"k" => "lower-case kay",
"l" => "lower-case elle",
"m" => "lower-case em",
"n" => "lower-case en",
"o" => "lower-case oh",
"p" => "lower-case pee",
"q" => "lower-case queue",
"r" => "lower-case arrgh",
"s" => "lower-case ess",
"t" => "lower-case tee",
"u" => "lower-case you",
"v" => "lower-case vee",
"w" => "lower-case dbl-u",
"x" => "lower-case ex",
"y" => "lower-case why",
"z" => "lower-case zed",
"A" => "Upper-Case Ay",
"B" => "Upper-Case Bee",
"C" => "Upper-Case See",
"D" => "Upper-Case Dee",
"E" => "Upper-Case Ee",
"F" => "Upper-Case Eff",
"G" => "Upper-Case Gee",
"H" => "Upper-Case Aitch",
"I" => "Upper-Case Aye",
"J" => "Upper-Case Jay",
"K" => "Upper-Case Kay",
"L" => "Upper-Case Elle",
"M" => "Upper-Case Em",
"N" => "Upper-Case En",
"O" => "Upper-Case Oh",
"P" => "Upper-Case Pee",
"Q" => "Upper-Case Queue",
"R" => "Upper-Case Arrgh",
"S" => "Upper-Case Ess",
"T" => "Upper-Case Tee",
"U" => "Upper-Case You",
"V" => "Upper-Case Vee",
"W" => "Upper-Case Dbl-U",
"X" => "Upper-Case Ex",
"Y" => "Upper-Case Why",
"Z" => "Upper-Case Zed"
);
$newtext_array = str_split($this->_text);
$tmp_str = '';
$ret_string = '<div class="verify-title">Enter the '.strlen($_SESSION['gdv']['img_str']).'-digit code this text <em>sounds like</em> :</div><br />
<div class="verify-text">';
foreach ($newtext_array as $convert) {
$tmp_str .= $translate_array[$convert].", ";
}
return $ret_string.substr($tmp_str, 0, strlen($tmp_str)-2).'</div>';
} else {
// image-based verification..
//
// start with a blank image..
if ($this->_truecolor) {
$this->_img = imagecreatetruecolor($this->_width, $this->_height);
} else {
$this->_img = imagecreate($this->_width, $this->_height);
}
// colours we shall use..
$white = imagecolorallocate($this->_img, 255, 255, 255);
$get_col = $this->hex2dec($this->_color_bg);
$this->_background_color = imagecolorallocate($this->_img, $get_col[0], $get_col[1], $get_col[2]);
$get_col = $this->hex2dec($this->_front_col);
$front_col = imagecolorallocatealpha($this->_img, $get_col[0], $get_col[1], $get_col[2], $this->_translucency);
$get_col = $this->hex2dec($this->_color_1);
$color_1 = imagecolorallocatealpha($this->_img, $get_col[0], $get_col[1], $get_col[2], $this->_translucency);
$get_col = $this->hex2dec($this->_color_2);
$color_2 = imagecolorallocatealpha($this->_img, $get_col[0], $get_col[1], $get_col[2], $this->_translucency);
$get_col = $this->hex2dec($this->_color_3);
$color_3 = imagecolorallocatealpha($this->_img, $get_col[0], $get_col[1], $get_col[2], $this->_translucency);
// background..
// probably we want to put shapes and shit in here..
imagefilledrectangle($this->_img, 0, 0, $this->_width, $this->_height, $this->_background_color);
// rotate the text around its centre..
// yes, we could simply calculate the centre point and lay it there, but this is not only a
// wee bit more clever, but introduces nice unpredictable displacement based on letter size.
$x = $this->_width / 2;
$y = $this->_height / 2; // ttf's calculate from baseline, this sorta compensates. a tweaker!
// get the boundingbox..
$bbox = imagettfbbox($this->_font_size, $this->_angle, $this->_font_face, $this->_text);
// calculate deviation..
$dx = (($bbox[2]-$bbox[0]) / 2) - (($bbox[2]-$bbox[4]) / 2);
$dy = (($bbox[3]-$bbox[1]) / 2) + (($bbox[7]-$bbox[1]) / 2);
// our new pivot points..
$px = $x - $dx;
$py = $y - $dy;
// we only need a small variation here
$var_x = rand(4, 7);
$var_y = rand(4, 7);
// put the text string onto the image in a few different colours..
imagettftext($this->_img, $this->_font_size + 2, $this->_angle, $px-$var_x, $py - ($this->_height / 18) - $var_y, $color_2, $this->_font_face, $this->_text);
imagettftext($this->_img, $this->_font_size + 3, $this->_angle, $px - ($this->_width / 12.5) - $var_x, $py, $color_1, $this->_font_face, $this->_text);
imagettftext($this->_img, $this->_font_size + 4, $this->_angle, $px + ($this->_width / 50) + $var_x, $py + $var_y, $color_3, $this->_font_face, $this->_text);
// now the *real* text..
imagettftext($this->_img, $this->_font_size, $this->_angle, $px, $py, $front_col, $this->_font_face, $this->_text);
// border..
imagefilledrectangle($this->_img, 0, 0, $this->_width, 0, $front_col);
imagefilledrectangle($this->_img, $this->_width - 1, 0, $this->_width - 1, $this->_height - 1, $front_col);
imagefilledrectangle($this->_img, 0, 0, 0, $this->_height - 1, $front_col);
imagefilledrectangle($this->_img, 0, $this->_height - 1, $this->_width, $this->_height - 1, $front_col);
// finally, spit it out..
header('Content-type: image/'.$this->_img_format);
switch ($this->_img_format) {
case 'png':
imagepng($this->_img);
break;
case 'jpeg':
imagejpeg($this->_img);
break;
case 'jpg':
imagejpeg($this->_img);
break;
case 'gif':
imagegif ($this->_img);
}
imagedestroy($this->_img);
}
}
// determine whether the browser can handle images, or not..
//
function imageCapable() {
// regardless of capabilities, we're sending text..
if ($this->_verify_method == 'text') { return false; }
// okay, let's check what browser we're dealing with..
$user_agent = strtolower(@$_SERVER['HTTP_USER_AGENT']);
switch (true) {
case (stristr($user_agent, 'gecko')):
break;
case (stristr($user_agent, 'opera')):
break;
case (stristr($user_agent, 'MSIE')):
break;
default:
// Last Chance!
if (!stristr(@$_SERVER['HTTP_ACCEPT'], 'image/')) {
$this->_imageCapable = false;
}
}
return $this->_imageCapable;
}
/*
convert an HTML #hex colour to decimal colour levels.. */
function hex2dec($rgb) {
if (substr($rgb, 0, 1) == '#') {
$rgb = substr($rgb, 1);
}
if (strlen($rgb) == 6) {
$r = hexdec(substr($rgb, 0, 2));
$g = hexdec(substr($rgb, 2, 2));
$b = hexdec(substr($rgb, 4, 2));
} elseif (strlen($rgb) == 3) {
$r = hexdec(str_repeat(substr($rgb, 0, 1), 2));
$g = hexdec(str_repeat(substr($rgb, 1, 1), 2));
$b = hexdec(str_repeat(substr($rgb, 2, 1), 2));
} else { return; }
return array($r, $g, $b);
}
// dammit, php needs this function!
function anywhere_in_array($my_string, $array) {
foreach ($array as $k=>$v) {
if (stristr($k, $my_string)) { return true; }
if (stristr($v, $my_string)) { return true; }
}
}
}
?>