From dd02492f08248bf9b53a20b54a3d49a9f78fc0ad Mon Sep 17 00:00:00 2001 From: xue <> Date: Wed, 29 Aug 2007 19:57:50 +0000 Subject: finished TCaptcha. --- framework/Web/UI/WebControls/TCaptcha.php | 81 +++++++++++- framework/Web/UI/WebControls/assets/captcha.php | 169 +++++++++++++++++++----- 2 files changed, 215 insertions(+), 35 deletions(-) (limited to 'framework/Web/UI/WebControls') diff --git a/framework/Web/UI/WebControls/TCaptcha.php b/framework/Web/UI/WebControls/TCaptcha.php index 17e9ad34..bff04236 100644 --- a/framework/Web/UI/WebControls/TCaptcha.php +++ b/framework/Web/UI/WebControls/TCaptcha.php @@ -18,11 +18,18 @@ Prado::using('System.Web.UI.WebControls.TImage'); * TCaptcha displays a CAPTCHA (a token displayed as an image) that can be used * to determine if the input is entered by a real user instead of some program. * + * Unlike other CAPTCHA scripts, TCaptcha does not need session or cookie. + * * The token (a string consisting of alphanumeric characters) displayed is automatically * generated and can be configured in several ways. To specify the length of characters * in the token, set {@link setMinTokenLength MinTokenLength} and {@link setMaxTokenLength MaxTokenLength}. * To use case-insensitive comparison and generate upper-case-only token, set {@link setCaseSensitive CaseSensitive} - * to false. + * to false. More advanced users can try to set {@link setTokenAlphabet TokenAlphabet}, which + * specifies what characters can appear in tokens. + * + * To specify the appearance of the generated token image, set {@link setTokenImageTheme TokenImageTheme} + * to be an integer between 0 and 31. And to adjust the generated image size, set {@link setTokenFontSize TokenFontSize} + * (you may also set {@link TWebControl::setWidth Width}, but the scaled image may not look good.) * * Upon postback, user input can be validated by calling {@link validate()}. * The {@link TCaptchaValidator} control can also be used to do validation, which provides @@ -30,6 +37,15 @@ Prado::using('System.Web.UI.WebControls.TImage'); * remain the same during multiple postbacks. A new one can be generated by calling * {@link regenerateToken()} manually. * + * The following template shows a typical use of TCaptcha control: + * + * + * + * + * + * * @author Qiang Xue * @version $Id$ * @package System.Web.UI.WebControls @@ -41,12 +57,67 @@ class TCaptcha extends TImage const MAX_TOKEN_LENGTH=40; private $_privateKey; + /** + * Checks the requirements needed for using TCaptcha. + */ public function onInit($param) { parent::onInit($param); $this->checkRequirements(); } + /** + * @return integer the theme of the token image. Defaults to 0. + */ + public function getTokenImageTheme() + { + return $this->getViewState('TokenImageTheme',0); + } + + /** + * Sets the theme of the token image. + * You may test each theme to find out the one you like the most. + * Below is the explanation of the theme value: + * It is treated as a 5-bit integer. Each bit toggles a specific feature of the image. + * Bit 0 (the least significant): whether the image is opaque (1) or transparent (0). + * Bit 1: whether we should add white noise to the image (1) or not (0). + * Bit 2: whether we should add a grid to the image (1) or not (0). + * Bit 3: whether we should add some scribbles to the image (1) or not (0). + * Bit 4: whether the image background should be morphed (1) or not (0). + * @param integer the theme of the token image. It must be an integer between 0 and 31. + */ + public function setTokenImageTheme($value) + { + $value=TPropertyValue::ensureInteger($value); + if($value>=0 && $value<=31) + $this->setViewState('TokenImageTheme',$value,0); + else + throw new TConfigurationException('captcha_tokenimagetheme_invalid',0,31); + } + + /** + * @return integer the font size used for displaying the token in an image. Defaults to 30. + */ + public function getTokenFontSize() + { + return $this->getViewState('TokenFontSize',30); + } + + /** + * Sets the font size used for displaying the token in an image. + * This property affects the generated token image size. + * The image width is proportional to this font size. + * @param integer the font size used for displaying the token in an image. It must be an integer between 20 and 100. + */ + public function setTokenFontSize($value) + { + $value=TPropertyValue::ensureInteger($value); + if($value>=20 && $value<=100) + $this->setViewState('TokenFontSize',$value,30); + else + throw new TConfigurationException('captcha_tokenfontsize_invalid',20,100); + } + /** * @return integer the minimum length of the token. Defaults to 5. */ @@ -104,11 +175,11 @@ class TCaptcha extends TImage } /** - * @return string the characters that may appear in the token. Defaults to '234578adefhijmnrtABDEFGHJLMNQRT'. + * @return string the characters that may appear in the token. Defaults to '234578adefhijmnrtABDEFGHJLMNRT'. */ public function getTokenAlphabet() { - return $this->getViewState('TokenAlphabet','234578adefhijmnrtABDEFGHJLMNQRT'); + return $this->getViewState('TokenAlphabet','234578adefhijmnrtABDEFGHJLMNRT'); } /** @@ -118,7 +189,7 @@ class TCaptcha extends TImage { if(strlen($value)<2) throw new TConfigurationException('captcha_tokenalphabet_invalid'); - $this->setViewState('TokenAlphabet',$value,'234578adefhijmnrtABDEFGHJLMNQRT'); + $this->setViewState('TokenAlphabet',$value,'234578adefhijmnrtABDEFGHJLMNRT'); } /** @@ -241,6 +312,8 @@ class TCaptcha extends TImage $options['tokenLength']=strlen($token); $options['caseSensitive']=$this->getCaseSensitive(); $options['alphabet']=$this->getTokenAlphabet(); + $options['fontSize']=$this->getTokenFontSize(); + $options['theme']=$this->getTokenImageTheme(); $str=serialize($options); return base64_encode(md5($privateKey.$str).$str); } diff --git a/framework/Web/UI/WebControls/assets/captcha.php b/framework/Web/UI/WebControls/assets/captcha.php index 3941eb44..2a4952a9 100644 --- a/framework/Web/UI/WebControls/assets/captcha.php +++ b/framework/Web/UI/WebControls/assets/captcha.php @@ -10,9 +10,17 @@ * @package System.Web.UI.WebControls.assets */ +define('THEME_OPAQUE_BACKGROUND',0x0001); +define('THEME_NOISY_BACKGROUND',0x0002); +define('THEME_HAS_GRID',0x0004); +define('THEME_HAS_SCRIBBLE',0x0008); +define('THEME_MORPH_BACKGROUND',0x0010); + require_once(dirname(__FILE__).'/captcha_key.php'); $token='error'; +$theme=0; + if(isset($_GET['options'])) { $str=base64_decode($_GET['options']); @@ -27,12 +35,14 @@ if(isset($_GET['options'])) $tokenLength=$options['tokenLength']; $caseSensitive=$options['caseSensitive']; $alphabet=$options['alphabet']; + $fontSize=$options['fontSize']; + $theme=$options['theme']; $token=generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive); } } } -displayToken($token); +displayToken($token,$fontSize,$theme); function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive) { @@ -40,10 +50,10 @@ function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensit return $caseSensitive?$token:strtoupper($token); } -function hash2string($hex,$alphabet='') +function hash2string($hex,$alphabet) { if(strlen($alphabet)<2) - $alphabet='234578adefhijmnrtABDEFGHJLMNQRT'; + $alphabet='234578adefhijmnrtABDEFGHJLMNRT'; $hexLength=strlen($hex); $base=strlen($alphabet); $result=''; @@ -59,47 +69,144 @@ function hash2string($hex,$alphabet='') return $result; } -function displayToken($token) +function displayToken($token,$fontSize,$theme) { + if(($fontSize=(int)$fontSize)<22) + $fontSize=22; + if($fontSize>100) + $fontSize=100; $length=strlen($token); - $width=45*$length; - $height=70; + $padding=10; + $fontWidth=$fontSize; + $fontHeight=floor($fontWidth*1.5); + $width=$fontWidth*$length+$padding*2; + $height=$fontHeight; $image=imagecreatetruecolor($width,$height); + + addBackground + ( + $image, $width, $height, + $theme&THEME_OPAQUE_BACKGROUND, + $theme&THEME_NOISY_BACKGROUND, + $theme&THEME_HAS_GRID, + $theme&THEME_HAS_SCRIBBLE, + $theme&THEME_MORPH_BACKGROUND + ); + $font=dirname(__FILE__).DIRECTORY_SEPARATOR.'verase.ttf'; - $vred=rand(0,100); - $vgreen=rand(0,100); - $vblue=rand(0,100); + + if(function_exists('imagefilter')) + imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); + + for($i=0;$i<$length;$i++) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + imagettftext($image,rand($fontWidth-10,$fontWidth),rand(-30, 30),$padding+$i*$fontWidth,rand($fontHeight-15,$fontHeight-10),$color,$font,$token[$i]); + imagecolordeallocate($image,$color); + } + + imagepng($image); + imagedestroy($image); +} + +function addBackground($image,$width,$height,$opaque,$noisy,$hasGrid,$hasScribble,$morph) +{ + $background=imagecreatetruecolor($width*2,$height*2); + $white=imagecolorallocate($background,255,255,255); + imagefill($background,0,0,$white); + + if($opaque) + imagefill($background,0,0,imagecolorallocate($background,100,100,100)); + + if($noisy) + addNoise($background,$width*2,$height*2); + + if($hasGrid) + addGrid($background,$width*2,$height*2); + + if($hasScribble) + addScribble($background,$width*2,$height*2); + + if($morph) + morphImage($background,$width*2,$height*2); + + imagecopy($image,$background,0,0,30,30,$width,$height); + + if(!$opaque) + imagecolortransparent($image,$white); +} + +function addNoise($image,$width,$height) +{ for($x=0;$x<$width;++$x) { for($y=0;$y<$height;++$y) { - $vred+=rand(-2,2); - $vgreen+=rand(-2,2); - $vblue+=rand(-2,2); - if($vred<0) $vred=0; if($vred>150) $vred=75; - if($vgreen<0) $vgreen=0; if($vgreen>150) $vgreen=75; - if($vblue<0) $vblue=0; if($vblue>150) $vblue=75; - $col = imagecolorallocate($image, $vred, $vgreen, $vblue); - imagesetpixel($image, $x, $y, $col); - imagecolordeallocate($image, $col); + if(rand(0,100)<25) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + imagesetpixel($image,$x,$y,$color); + imagecolordeallocate($image,$color); + } } } +} - imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); - for($i=0;$i<$length;$i++) +function addGrid($image,$width,$height) +{ + for($i=0;$i<$width;$i+=rand(15,25)) { - $vred = rand(150, 240); - $vgreen = rand(150, 240); - $vblue = rand(150, 240); - $col = imagecolorallocate($image, $vred, $vgreen, $vblue); - $char = $token[$i]; - imagettftext($image, rand(40, 50), rand(-10, 20), 13 + (40 * $i), rand(50, imagesy($image) - 10), $col, $font, $char); - imagecolordeallocate($image, $col); - } - imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,$i+rand(-10,20),0,$i+rand(-10,20),$height,$color); + imagecolordeallocate($image,$color); + } + for($i=0;$i<$height;$i+=rand(15,25)) + { + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,0,$i+rand(-10,20),$width,$i+rand(-10,20),$color); + imagecolordeallocate($image,$color); + } +} - imagepng($image); - imagedestroy($image); +function addScribble($image,$width,$height) +{ + for($i=0;$i<8;$i++) + { + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + $points=array(); + for($j=1;$j=$height) $y=$height-5; + if($y<0) $y=5; + imagecopy($tempImage,$image,$x,0,$x,$y,$chunk,$height); + } + for($x=$y=0;$y<$height;$y+=$chunk) + { + $chunk=rand(1,5); + $x+=rand(-1,1); + if($x>=$width) $x=$width-5; + if($x<0) $x=5; + imagecopy($image,$tempImage,$x,$y,0,$y,$width,$chunk); + } } ?> \ No newline at end of file -- cgit v1.2.3