From dd02492f08248bf9b53a20b54a3d49a9f78fc0ad Mon Sep 17 00:00:00 2001
From: xue <>
Date: Wed, 29 Aug 2007 19:57:50 +0000
Subject: finished TCaptcha.
---
framework/Exceptions/messages/messages.txt | 12 +-
framework/Web/UI/WebControls/TCaptcha.php | 81 +++++++++++-
framework/Web/UI/WebControls/assets/captcha.php | 169 +++++++++++++++++++-----
3 files changed, 226 insertions(+), 36 deletions(-)
(limited to 'framework')
diff --git a/framework/Exceptions/messages/messages.txt b/framework/Exceptions/messages/messages.txt
index a60b8226..33a6f752 100644
--- a/framework/Exceptions/messages/messages.txt
+++ b/framework/Exceptions/messages/messages.txt
@@ -432,4 +432,14 @@ urlmapping_configfile_invalid = TUrlMapping.ConfigFile '{0}' must point to an
urlmappingpattern_serviceparameter_required = TUrlMappingPattern.ServiceParameter is required for pattern '{0}'.
keyboard_forcontrol_required = TKeyboard.ForControl cannot be empty.
-keyboard_forcontrol_invalid = TKeyboard.ForControl '{0}' is invalid.
\ No newline at end of file
+keyboard_forcontrol_invalid = TKeyboard.ForControl '{0}' is invalid.
+
+captcha_tokenimagetheme_invalid = TCaptcha.TokenImageTheme must be an integer between {0} and {1}.
+captcha_tokenfontsize_invalid = TCaptcha.TokenFontSize must be an integer between {0} and {1}.
+captcha_mintokenlength_invalid = TCaptcha.MinTokenLength must be an integer between {0} and {1}.
+captcha_maxtokenlength_invalid = TCaptcha.MaxTokenLength must be an integer between {0} and {1}.
+captcha_tokenalphabet_invalid = TCaptcha.TokenAlphabet must be a string consisting of at least 2 characters.
+captcha_privatekey_unknown = TCaptcha.PrivateKey is unknown. Please make sure that your assets directory is writable by the Web server process.
+captcha_gd2_required = TCaptcha requires PHP GD2 extension.
+captcha_imagettftext_required = TCaptcha requires PHP GD2 extension with TrueType font support.
+captcha_imagepng_required = TCaptcha requires PHP GD2 extension with PNG image format support.
\ No newline at end of file
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