includes/captcha/engine_freecap.php
changeset 1227 bdac73ed481e
parent 558 fd082123f2f4
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    27  * A port of the freeCap captcha engine to Enano.
    27  * A port of the freeCap captcha engine to Enano.
    28  */
    28  */
    29 
    29 
    30 class captcha_engine_freecap extends captcha_base
    30 class captcha_engine_freecap extends captcha_base
    31 {
    31 {
    32   
    32 	
    33   var $site_tags = array();
    33 	var $site_tags = array();
    34   var $tag_pos = 0;
    34 	var $tag_pos = 0;
    35   var $rand_func = "mt_rand";
    35 	var $rand_func = "mt_rand";
    36   var $seed_func = "mt_srand";
    36 	var $seed_func = "mt_srand";
    37   var $hash_func = "sha1";
    37 	var $hash_func = "sha1";
    38   var $output = "png";
    38 	var $output = "png";
    39   var $use_dict = false;
    39 	var $use_dict = false;
    40   var $dict_location = "";
    40 	var $dict_location = "";
    41   var $max_word_length = 7;
    41 	var $max_word_length = 7;
    42   var $col_type = 1;
    42 	var $col_type = 1;
    43   var $max_attempts = 20;
    43 	var $max_attempts = 20;
    44   var $font_locations = Array();
    44 	var $font_locations = Array();
    45   var $bg_type = 3;
    45 	var $bg_type = 3;
    46   var $blur_bg = false;
    46 	var $blur_bg = false;
    47   var $bg_images = Array();
    47 	var $bg_images = Array();
    48   var $merge_type = 0;
    48 	var $merge_type = 0;
    49   var $morph_bg = true;
    49 	var $morph_bg = true;
    50   var $im, $im2, $im3;
    50 	var $im, $im2, $im3;
    51   var $font_size = 36;
    51 	var $font_size = 36;
    52   var $debug = false;
    52 	var $debug = false;
    53   
    53 	
    54   function __construct($s, $r = false)
    54 	function __construct($s, $r = false)
    55   {
    55 	{
    56     parent::__construct($s, $r);
    56 		parent::__construct($s, $r);
    57     
    57 		
    58     // try to avoid the 'free p*rn' method of CAPTCHA circumvention
    58 		// try to avoid the 'free p*rn' method of CAPTCHA circumvention
    59     // see en.wikipedia.org/wiki/CAPTCHA for more info
    59 		// see en.wikipedia.org/wiki/CAPTCHA for more info
    60     // $this->site_tags[0] = "To avoid spam, please do NOT enter the text if";
    60 		// $this->site_tags[0] = "To avoid spam, please do NOT enter the text if";
    61     // $this->site_tags[1] = "this site is not puremango.co.uk";
    61 		// $this->site_tags[1] = "this site is not puremango.co.uk";
    62     // or more simply:
    62 		// or more simply:
    63     // $site_tags[0] = "for use only on puremango.co.uk";
    63 		// $site_tags[0] = "for use only on puremango.co.uk";
    64     // reword or add lines as you please
    64 		// reword or add lines as you please
    65     // or if you don't want any text:
    65 		// or if you don't want any text:
    66     $this->site_tags = array();
    66 		$this->site_tags = array();
    67     
    67 		
    68     // where to write the above:
    68 		// where to write the above:
    69     // 0=top
    69 		// 0=top
    70     // 1=bottom
    70 		// 1=bottom
    71     // 2=both
    71 		// 2=both
    72     $this->tag_pos = 1;
    72 		$this->tag_pos = 1;
    73     
    73 		
    74     // functions to call for random number generation
    74 		// functions to call for random number generation
    75     // mt_rand produces 'better' random numbers
    75 		// mt_rand produces 'better' random numbers
    76     // but if your server doesn't support it, it's fine to use rand instead
    76 		// but if your server doesn't support it, it's fine to use rand instead
    77     $this->rand_func = "mt_rand";
    77 		$this->rand_func = "mt_rand";
    78     $this->seed_func = "mt_srand";
    78 		$this->seed_func = "mt_srand";
    79     
    79 		
    80     // which type of hash to use?
    80 		// which type of hash to use?
    81     // possible values: "sha1", "md5", "crc32"
    81 		// possible values: "sha1", "md5", "crc32"
    82     // sha1 supported by PHP4.3.0+
    82 		// sha1 supported by PHP4.3.0+
    83     // md5 supported by PHP3+
    83 		// md5 supported by PHP3+
    84     // crc32 supported by PHP4.0.1+
    84 		// crc32 supported by PHP4.0.1+
    85     $this->hash_func = $this->session_fetch('hash_func', 'sha1');
    85 		$this->hash_func = $this->session_fetch('hash_func', 'sha1');
    86     // store in session so can validate in form processor
    86 		// store in session so can validate in form processor
    87     
    87 		
    88     // image type:
    88 		// image type:
    89     // possible values: "jpg", "png", "gif"
    89 		// possible values: "jpg", "png", "gif"
    90     // jpg doesn't support transparency (transparent bg option ends up white)
    90 		// jpg doesn't support transparency (transparent bg option ends up white)
    91     // png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
    91 		// png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
    92     // gif may not be supported by your GD Lib.
    92 		// gif may not be supported by your GD Lib.
    93     $this->output = "png";
    93 		$this->output = "png";
    94     
    94 		
    95     // 0=generate pseudo-random string, true=use dictionary
    95 		// 0=generate pseudo-random string, true=use dictionary
    96     // dictionary is easier to recognise
    96 		// dictionary is easier to recognise
    97     // - both for humans and computers, so use random string if you're paranoid.
    97 		// - both for humans and computers, so use random string if you're paranoid.
    98     $this->use_dict = false;
    98 		$this->use_dict = false;
    99     // if your server is NOT set up to deny web access to files beginning ".ht"
    99 		// if your server is NOT set up to deny web access to files beginning ".ht"
   100     // then you should ensure the dictionary file is kept outside the web directory
   100 		// then you should ensure the dictionary file is kept outside the web directory
   101     // eg: if www.foo.com/index.html points to c:\website\www\index.html
   101 		// eg: if www.foo.com/index.html points to c:\website\www\index.html
   102     // then the dictionary should be placed in c:\website\dict.txt
   102 		// then the dictionary should be placed in c:\website\dict.txt
   103     // test your server's config by trying to access the dictionary through a web browser
   103 		// test your server's config by trying to access the dictionary through a web browser
   104     // you should NOT be able to view the contents.
   104 		// you should NOT be able to view the contents.
   105     // can leave this blank if not using dictionary
   105 		// can leave this blank if not using dictionary
   106     $this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php";
   106 		$this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php";
   107     
   107 		
   108     // used to calculate image width, and for non-dictionary word generation
   108 		// used to calculate image width, and for non-dictionary word generation
   109     $this->max_word_length = 7;
   109 		$this->max_word_length = 7;
   110     
   110 		
   111     // text colour
   111 		// text colour
   112     // 0=one random colour for all letters
   112 		// 0=one random colour for all letters
   113     // 1=different random colour for each letter
   113 		// 1=different random colour for each letter
   114     $this->col_type = 1;
   114 		$this->col_type = 1;
   115     
   115 		
   116     // maximum times a user can refresh the image
   116 		// maximum times a user can refresh the image
   117     // on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
   117 		// on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
   118     // further notes re: BF attacks in "avoid brute force attacks" section, below
   118 		// further notes re: BF attacks in "avoid brute force attacks" section, below
   119     // on the other hand, those attempting OCR will find the ability to request new images
   119 		// on the other hand, those attempting OCR will find the ability to request new images
   120     // very useful; if they can't crack one, just grab an easier target...
   120 		// very useful; if they can't crack one, just grab an easier target...
   121     // for the ultra-paranoid, setting it to <5 will still work for most users
   121 		// for the ultra-paranoid, setting it to <5 will still work for most users
   122     $this->max_attempts = 20;
   122 		$this->max_attempts = 20;
   123     
   123 		
   124     // list of fonts to use
   124 		// list of fonts to use
   125     // font size should be around 35 pixels wide for each character.
   125 		// font size should be around 35 pixels wide for each character.
   126     // you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
   126 		// you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
   127     // There are other programs to can create GD fonts, but my script allows a greater
   127 		// There are other programs to can create GD fonts, but my script allows a greater
   128     // degree of control over exactly how wide each character is, and is therefore
   128 		// degree of control over exactly how wide each character is, and is therefore
   129     // recommended for 'special' uses. For normal use of GD fonts,
   129 		// recommended for 'special' uses. For normal use of GD fonts,
   130     // the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
   130 		// the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
   131     
   131 		
   132     // the fonts included with freeCap *only* include lowercase alphabetic characters
   132 		// the fonts included with freeCap *only* include lowercase alphabetic characters
   133     // so are not suitable for most other uses
   133 		// so are not suitable for most other uses
   134     // to increase security, you really should add other fonts
   134 		// to increase security, you really should add other fonts
   135     $this->font_locations = Array(
   135 		$this->font_locations = Array(
   136         //ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf",
   136 				//ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf",
   137         //ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf",
   137 				//ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf",
   138         //ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf",
   138 				//ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf",
   139         //ENANO_ROOT . "/includes/captcha/fonts/.ttf",
   139 				//ENANO_ROOT . "/includes/captcha/fonts/.ttf",
   140         //ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf"
   140 				//ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf"
   141         ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf",
   141 				ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf",
   142         ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf",
   142 				ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf",
   143         ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf",
   143 				ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf",
   144       );
   144 			);
   145     
   145 		
   146     // background:
   146 		// background:
   147     // 0=transparent (if jpg, white)
   147 		// 0=transparent (if jpg, white)
   148     // 1=white bg with grid
   148 		// 1=white bg with grid
   149     // 2=white bg with squiggles
   149 		// 2=white bg with squiggles
   150     // 3=morphed image blocks
   150 		// 3=morphed image blocks
   151     // 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts)
   151 		// 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts)
   152     // many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
   152 		// many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
   153     // for jpgs, 'transparent' is white
   153 		// for jpgs, 'transparent' is white
   154     $this->bg_type = 3;
   154 		$this->bg_type = 3;
   155     // should we blur the background? (looks nicer, makes text easier to read, takes longer)
   155 		// should we blur the background? (looks nicer, makes text easier to read, takes longer)
   156     $this->blur_bg = false;
   156 		$this->blur_bg = false;
   157     
   157 		
   158     // for bg_type 3, which images should we use?
   158 		// for bg_type 3, which images should we use?
   159     // if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
   159 		// if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
   160     $this->bg_images = Array(
   160 		$this->bg_images = Array(
   161         ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg",
   161 				ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg",
   162         ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg",
   162 				ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg",
   163         ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg",
   163 				ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg",
   164         ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg",
   164 				ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg",
   165         ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg"
   165 				ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg"
   166       );
   166 			);
   167     
   167 		
   168     // for non-transparent backgrounds only:
   168 		// for non-transparent backgrounds only:
   169     // if 0, merges CAPTCHA with bg
   169 		// if 0, merges CAPTCHA with bg
   170     // if 1, write CAPTCHA over bg
   170 		// if 1, write CAPTCHA over bg
   171     $this->merge_type = 0;
   171 		$this->merge_type = 0;
   172     // should we morph the bg? (recommend yes, but takes a little longer to compute)
   172 		// should we morph the bg? (recommend yes, but takes a little longer to compute)
   173     $this->morph_bg = true;
   173 		$this->morph_bg = true;
   174     
   174 		
   175     // you shouldn't need to edit anything below this, but it's extensively commented if you do want to play
   175 		// you shouldn't need to edit anything below this, but it's extensively commented if you do want to play
   176     // have fun, and email me with ideas, or improvements to the code (very interested in speed improvements)
   176 		// have fun, and email me with ideas, or improvements to the code (very interested in speed improvements)
   177     // hope this script saves some spam :-)
   177 		// hope this script saves some spam :-)
   178   }
   178 	}
   179   
   179 	
   180   //////////////////////////////////////////////////////
   180 	//////////////////////////////////////////////////////
   181   ////// Functions:
   181 	////// Functions:
   182   //////////////////////////////////////////////////////
   182 	//////////////////////////////////////////////////////
   183   function make_seed() {
   183 	function make_seed() {
   184   // from http://php.net/srand
   184 	// from http://php.net/srand
   185       list($usec, $sec) = explode(' ', microtime());
   185 			list($usec, $sec) = explode(' ', microtime());
   186       return (float) $sec + ((float) $usec * 100000);
   186 			return (float) $sec + ((float) $usec * 100000);
   187   }
   187 	}
   188   
   188 	
   189   function rand_color() {
   189 	function rand_color() {
   190     $rf =& $this->rand_func;
   190 		$rf =& $this->rand_func;
   191     if($this->bg_type==3)
   191 		if($this->bg_type==3)
   192     {
   192 		{
   193       // needs darker colour..
   193 			// needs darker colour..
   194       return $rf(10,100);
   194 			return $rf(10,100);
   195     } else {
   195 		} else {
   196       return $rf(60,170);
   196 			return $rf(60,170);
   197     }
   197 		}
   198   }
   198 	}
   199   
   199 	
   200   function myImageBlur($im)
   200 	function myImageBlur($im)
   201   {
   201 	{
   202     // w00t. my very own blur function
   202 		// w00t. my very own blur function
   203     // in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
   203 		// in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
   204   
   204 	
   205     $width = imagesx($im);
   205 		$width = imagesx($im);
   206     $height = imagesy($im);
   206 		$height = imagesy($im);
   207   
   207 	
   208     $temp_im = ImageCreateTrueColor($width,$height);
   208 		$temp_im = ImageCreateTrueColor($width,$height);
   209     $bg = ImageColorAllocate($temp_im,150,150,150);
   209 		$bg = ImageColorAllocate($temp_im,150,150,150);
   210   
   210 	
   211     // preserves transparency if in orig image
   211 		// preserves transparency if in orig image
   212     ImageColorTransparent($temp_im,$bg);
   212 		ImageColorTransparent($temp_im,$bg);
   213   
   213 	
   214     // fill bg
   214 		// fill bg
   215     ImageFill($temp_im,0,0,$bg);
   215 		ImageFill($temp_im,0,0,$bg);
   216   
   216 	
   217     // anything higher than 3 makes it totally unreadable
   217 		// anything higher than 3 makes it totally unreadable
   218     // might be useful in a 'real' blur function, though (ie blurring pictures not text)
   218 		// might be useful in a 'real' blur function, though (ie blurring pictures not text)
   219     $distance = 1;
   219 		$distance = 1;
   220     // use $distance=30 to have multiple copies of the word. not sure if this is useful.
   220 		// use $distance=30 to have multiple copies of the word. not sure if this is useful.
   221   
   221 	
   222     // blur by merging with itself at different x/y offsets:
   222 		// blur by merging with itself at different x/y offsets:
   223     ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70);
   223 		ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70);
   224     ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
   224 		ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
   225     ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70);
   225 		ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70);
   226     ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
   226 		ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
   227     // remove temp image
   227 		// remove temp image
   228     ImageDestroy($temp_im);
   228 		ImageDestroy($temp_im);
   229   
   229 	
   230     return $im;
   230 		return $im;
   231   }
   231 	}
   232   
   232 	
   233   function sendImage($pic)
   233 	function sendImage($pic)
   234   {
   234 	{
   235     // output image with appropriate headers
   235 		// output image with appropriate headers
   236     global $output,$im,$im2,$im3;
   236 		global $output,$im,$im2,$im3;
   237     // ENANO - obfuscation technique disabled
   237 		// ENANO - obfuscation technique disabled
   238     // (this is for ethical reasons - ask dan at enanocms.org for information on why)
   238 		// (this is for ethical reasons - ask dan at enanocms.org for information on why)
   239     // Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary
   239 		// Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary
   240     // header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs="));
   240 		// header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs="));
   241     
   241 		
   242     if ( $this->debug )
   242 		if ( $this->debug )
   243     {
   243 		{
   244       $x = imagesx($pic) - 70;
   244 			$x = imagesx($pic) - 70;
   245       $y = imagesy($pic) - 20;
   245 			$y = imagesy($pic) - 20;
   246       
   246 			
   247       $code = $this->get_code();
   247 			$code = $this->get_code();
   248       $red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72);
   248 			$red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72);
   249       ImageString($pic, 5, $x, $y, $code, $red);
   249 			ImageString($pic, 5, $x, $y, $code, $red);
   250       ImageString($pic, 5, 5, $y, "[debug mode]", $red);
   250 			ImageString($pic, 5, 5, $y, "[debug mode]", $red);
   251     }
   251 		}
   252     
   252 		
   253     switch($this->output)
   253 		switch($this->output)
   254     {
   254 		{
   255       // add other cases as desired
   255 			// add other cases as desired
   256       case "jpg":
   256 			case "jpg":
   257         header("Content-Type: image/jpeg");
   257 				header("Content-Type: image/jpeg");
   258         ImageJPEG($pic);
   258 				ImageJPEG($pic);
   259         break;
   259 				break;
   260       case "gif":
   260 			case "gif":
   261         header("Content-Type: image/gif");
   261 				header("Content-Type: image/gif");
   262         ImageGIF($pic);
   262 				ImageGIF($pic);
   263         break;
   263 				break;
   264       case "png":
   264 			case "png":
   265       default:
   265 			default:
   266         header("Content-Type: image/png");
   266 				header("Content-Type: image/png");
   267         ImagePNG($pic);
   267 				ImagePNG($pic);
   268         break;
   268 				break;
   269     }
   269 		}
   270   
   270 	
   271     // kill GD images (removes from memory)
   271 		// kill GD images (removes from memory)
   272     ImageDestroy($this->im);
   272 		ImageDestroy($this->im);
   273     ImageDestroy($this->im2);
   273 		ImageDestroy($this->im2);
   274     ImageDestroy($pic);
   274 		ImageDestroy($pic);
   275     if(!empty($this->im3))
   275 		if(!empty($this->im3))
   276     {
   276 		{
   277       ImageDestroy($this->im3);
   277 			ImageDestroy($this->im3);
   278     }
   278 		}
   279     exit();
   279 		exit();
   280   }
   280 	}
   281   
   281 	
   282   function make_image()
   282 	function make_image()
   283   {
   283 	{
   284     //////////////////////////////////////////////////////
   284 		//////////////////////////////////////////////////////
   285     ////// Create Images + initialise a few things
   285 		////// Create Images + initialise a few things
   286     //////////////////////////////////////////////////////
   286 		//////////////////////////////////////////////////////
   287     
   287 		
   288     // seed random number generator
   288 		// seed random number generator
   289     // PHP 4.2.0+ doesn't need this, but lower versions will
   289 		// PHP 4.2.0+ doesn't need this, but lower versions will
   290     $this->seed_func($this->make_seed());
   290 		$this->seed_func($this->make_seed());
   291     
   291 		
   292     // how faded should the bg be? (100=totally gone, 0=bright as the day)
   292 		// how faded should the bg be? (100=totally gone, 0=bright as the day)
   293     // to test how much protection the bg noise gives, take a screenshot of the freeCap image
   293 		// to test how much protection the bg noise gives, take a screenshot of the freeCap image
   294     // and take it into a photo editor. play with contrast and brightness.
   294 		// and take it into a photo editor. play with contrast and brightness.
   295     // If you can remove most of the bg, then it's not a good enough percentage
   295 		// If you can remove most of the bg, then it's not a good enough percentage
   296     switch($this->bg_type)
   296 		switch($this->bg_type)
   297     {
   297 		{
   298       case 0:
   298 			case 0:
   299         break;
   299 				break;
   300       case 1:
   300 			case 1:
   301       case 2:
   301 			case 2:
   302         $bg_fade_pct = 65;
   302 				$bg_fade_pct = 65;
   303         break;
   303 				break;
   304       case 3:
   304 			case 3:
   305         $bg_fade_pct = 50;
   305 				$bg_fade_pct = 50;
   306         break;
   306 				break;
   307     }
   307 		}
   308     // slightly randomise the bg fade
   308 		// slightly randomise the bg fade
   309     $bg_fade_pct += $this->rand_func(-2,2);
   309 		$bg_fade_pct += $this->rand_func(-2,2);
   310     
   310 		
   311     // read each font and get font character widths
   311 		// read each font and get font character widths
   312     // $font_widths = Array();
   312 		// $font_widths = Array();
   313     // for($i=0 ; $i<sizeof($this->font_locations) ; $i++)
   313 		// for($i=0 ; $i<sizeof($this->font_locations) ; $i++)
   314     // {
   314 		// {
   315     //   $handle = fopen($this->font_locations[$i],"r");
   315 		//   $handle = fopen($this->font_locations[$i],"r");
   316     //   // read header of GD font, up to char width
   316 		//   // read header of GD font, up to char width
   317     //   $c_wid = fread($handle,15);
   317 		//   $c_wid = fread($handle,15);
   318     //   $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11});
   318 		//   $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11});
   319     //   fclose($handle);
   319 		//   fclose($handle);
   320     // }
   320 		// }
   321     
   321 		
   322     // modify image width depending on maximum possible length of word
   322 		// modify image width depending on maximum possible length of word
   323     // you shouldn't need to use words > 6 chars in length really.
   323 		// you shouldn't need to use words > 6 chars in length really.
   324     $width = ($this->max_word_length*($this->font_size+10)+75);
   324 		$width = ($this->max_word_length*($this->font_size+10)+75);
   325     $height = 90;
   325 		$height = 90;
   326     
   326 		
   327     $this->im = ImageCreateTrueColor($width, $height);
   327 		$this->im = ImageCreateTrueColor($width, $height);
   328     $this->im2 = ImageCreateTrueColor($width, $height);
   328 		$this->im2 = ImageCreateTrueColor($width, $height);
   329     
   329 		
   330     ////////////////////////////////////////////////////////
   330 		////////////////////////////////////////////////////////
   331     // GENERATE IMAGE                                     //
   331 		// GENERATE IMAGE                                     //
   332     ////////////////////////////////////////////////////////
   332 		////////////////////////////////////////////////////////
   333     
   333 		
   334     $word = $this->get_code();
   334 		$word = $this->get_code();
   335     
   335 		
   336     // save hash of word for comparison
   336 		// save hash of word for comparison
   337     // using hash so that if there's an insecurity elsewhere (eg on the form processor),
   337 		// using hash so that if there's an insecurity elsewhere (eg on the form processor),
   338     // an attacker could only get the hash
   338 		// an attacker could only get the hash
   339     // also, shared servers usually give all users access to the session files
   339 		// also, shared servers usually give all users access to the session files
   340     // echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
   340 		// echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
   341     // so even if your site is 100% secure, someone else's site on your server might not be
   341 		// so even if your site is 100% secure, someone else's site on your server might not be
   342     // hence, even if attackers can read the session file, they can't get the freeCap word
   342 		// hence, even if attackers can read the session file, they can't get the freeCap word
   343     // (though most hashes are easy to brute force for simple strings)
   343 		// (though most hashes are easy to brute force for simple strings)
   344     
   344 		
   345     //////////////////////////////////////////////////////
   345 		//////////////////////////////////////////////////////
   346     ////// Fill BGs and Allocate Colours:
   346 		////// Fill BGs and Allocate Colours:
   347     //////////////////////////////////////////////////////
   347 		//////////////////////////////////////////////////////
   348     
   348 		
   349     // set tag colour
   349 		// set tag colour
   350     // have to do this before any distortion
   350 		// have to do this before any distortion
   351     // (otherwise colour allocation fails when bg type is 1)
   351 		// (otherwise colour allocation fails when bg type is 1)
   352     $tag_col = ImageColorAllocate($this->im,10,10,10);
   352 		$tag_col = ImageColorAllocate($this->im,10,10,10);
   353     $site_tag_col2 = ImageColorAllocate($this->im2,0,0,0);
   353 		$site_tag_col2 = ImageColorAllocate($this->im2,0,0,0);
   354     
   354 		
   355     // set debug colours (text colours are set later)
   355 		// set debug colours (text colours are set later)
   356     $debug = ImageColorAllocate($this->im, 255, 0, 0);
   356 		$debug = ImageColorAllocate($this->im, 255, 0, 0);
   357     $debug2 = ImageColorAllocate($this->im2, 255, 0, 0);
   357 		$debug2 = ImageColorAllocate($this->im2, 255, 0, 0);
   358     
   358 		
   359     // set background colour (can change to any colour not in possible $text_col range)
   359 		// set background colour (can change to any colour not in possible $text_col range)
   360     // it doesn't matter as it'll be transparent or coloured over.
   360 		// it doesn't matter as it'll be transparent or coloured over.
   361     // if you're using bg_type 3, you might want to try to ensure that the color chosen
   361 		// if you're using bg_type 3, you might want to try to ensure that the color chosen
   362     // below doesn't appear too much in any of your background images.
   362 		// below doesn't appear too much in any of your background images.
   363     $bg = ImageColorAllocate($this->im, 254, 254, 254);
   363 		$bg = ImageColorAllocate($this->im, 254, 254, 254);
   364     $bg2 = ImageColorAllocate($this->im2, 254, 254, 254);
   364 		$bg2 = ImageColorAllocate($this->im2, 254, 254, 254);
   365     
   365 		
   366     // set transparencies
   366 		// set transparencies
   367     ImageColorTransparent($this->im,$bg);
   367 		ImageColorTransparent($this->im,$bg);
   368     // im2 transparent to allow characters to overlap slightly while morphing
   368 		// im2 transparent to allow characters to overlap slightly while morphing
   369     ImageColorTransparent($this->im2,$bg2);
   369 		ImageColorTransparent($this->im2,$bg2);
   370     
   370 		
   371     // fill backgrounds
   371 		// fill backgrounds
   372     ImageFill($this->im,0,0,$bg);
   372 		ImageFill($this->im,0,0,$bg);
   373     ImageFill($this->im2,0,0,$bg2);
   373 		ImageFill($this->im2,0,0,$bg2);
   374     
   374 		
   375     if($this->bg_type!=0)
   375 		if($this->bg_type!=0)
   376     {
   376 		{
   377       // generate noisy background, to be merged with CAPTCHA later
   377 			// generate noisy background, to be merged with CAPTCHA later
   378       // any suggestions on how best to do this much appreciated
   378 			// any suggestions on how best to do this much appreciated
   379       // sample code would be even better!
   379 			// sample code would be even better!
   380       // I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
   380 			// I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
   381       // so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
   381 			// so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
   382       // ideally, the character obfuscation would be strong enough not to need additional background noise
   382 			// ideally, the character obfuscation would be strong enough not to need additional background noise
   383       // in any case, I hope at least one of the options given here provide some extra security!
   383 			// in any case, I hope at least one of the options given here provide some extra security!
   384     
   384 		
   385       $this->im3 = ImageCreateTrueColor($width,$height);
   385 			$this->im3 = ImageCreateTrueColor($width,$height);
   386       $temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5);
   386 			$temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5);
   387       $bg3 = ImageColorAllocate($this->im3,255,255,255);
   387 			$bg3 = ImageColorAllocate($this->im3,255,255,255);
   388       ImageFill($this->im3,0,0,$bg3);
   388 			ImageFill($this->im3,0,0,$bg3);
   389       $temp_bg_col = ImageColorAllocate($temp_bg,255,255,255);
   389 			$temp_bg_col = ImageColorAllocate($temp_bg,255,255,255);
   390       ImageFill($temp_bg,0,0,$temp_bg_col);
   390 			ImageFill($temp_bg,0,0,$temp_bg_col);
   391       
   391 			
   392       // we draw all noise onto temp_bg
   392 			// we draw all noise onto temp_bg
   393       // then if we're morphing, merge from temp_bg to im3
   393 			// then if we're morphing, merge from temp_bg to im3
   394       // or if not, just copy a $widthx$height portion of $temp_bg to $this->im3
   394 			// or if not, just copy a $widthx$height portion of $temp_bg to $this->im3
   395       // temp_bg is much larger so that when morphing, the edges retain the noise.
   395 			// temp_bg is much larger so that when morphing, the edges retain the noise.
   396     
   396 		
   397       if($this->bg_type==1)
   397 			if($this->bg_type==1)
   398       {
   398 			{
   399         // grid bg:
   399 				// grid bg:
   400     
   400 		
   401         // draw grid on x
   401 				// draw grid on x
   402         for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25))
   402 				for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25))
   403         {
   403 				{
   404           ImageSetThickness($temp_bg,$this->rand_func(2,6));
   404 					ImageSetThickness($temp_bg,$this->rand_func(2,6));
   405           $text_r = $this->rand_func(100,150);
   405 					$text_r = $this->rand_func(100,150);
   406           $text_g = $this->rand_func(100,150);
   406 					$text_g = $this->rand_func(100,150);
   407           $text_b = $this->rand_func(100,150);
   407 					$text_b = $this->rand_func(100,150);
   408           $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   408 					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   409     
   409 		
   410           ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3);
   410 					ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3);
   411         }
   411 				}
   412         // draw grid on y
   412 				// draw grid on y
   413         for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25))
   413 				for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25))
   414         {
   414 				{
   415           ImageSetThickness($temp_bg,$this->rand_func(2,6));
   415 					ImageSetThickness($temp_bg,$this->rand_func(2,6));
   416           $text_r = $this->rand_func(100,150);
   416 					$text_r = $this->rand_func(100,150);
   417           $text_g = $this->rand_func(100,150);
   417 					$text_g = $this->rand_func(100,150);
   418           $text_b = $this->rand_func(100,150);
   418 					$text_b = $this->rand_func(100,150);
   419           $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   419 					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   420     
   420 		
   421           ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3);
   421 					ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3);
   422         }
   422 				}
   423       } else if($this->bg_type==2) {
   423 			} else if($this->bg_type==2) {
   424         // draw squiggles!
   424 				// draw squiggles!
   425     
   425 		
   426         $bg3 = ImageColorAllocate($this->im3,255,255,255);
   426 				$bg3 = ImageColorAllocate($this->im3,255,255,255);
   427         ImageFill($this->im3,0,0,$bg3);
   427 				ImageFill($this->im3,0,0,$bg3);
   428         ImageSetThickness($temp_bg,4);
   428 				ImageSetThickness($temp_bg,4);
   429     
   429 		
   430         for($i=0 ; $i<strlen($word)+1 ; $i++)
   430 				for($i=0 ; $i<strlen($word)+1 ; $i++)
   431         {
   431 				{
   432           $text_r = $this->rand_func(100,150);
   432 					$text_r = $this->rand_func(100,150);
   433           $text_g = $this->rand_func(100,150);
   433 					$text_g = $this->rand_func(100,150);
   434           $text_b = $this->rand_func(100,150);
   434 					$text_b = $this->rand_func(100,150);
   435           $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   435 					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
   436     
   436 		
   437           $points = Array();
   437 					$points = Array();
   438           // draw random squiggle for each character
   438 					// draw random squiggle for each character
   439           // the longer the loop, the more complex the squiggle
   439 					// the longer the loop, the more complex the squiggle
   440           // keep random so OCR can't say "if found shape has 10 points, ignore it"
   440 					// keep random so OCR can't say "if found shape has 10 points, ignore it"
   441           // each squiggle will, however, be a closed shape, so OCR could try to find
   441 					// each squiggle will, however, be a closed shape, so OCR could try to find
   442           // line terminations and start from there. (I don't think they're that advanced yet..)
   442 					// line terminations and start from there. (I don't think they're that advanced yet..)
   443           for($j=1 ; $j<$this->rand_func(5,10) ; $j++)
   443 					for($j=1 ; $j<$this->rand_func(5,10) ; $j++)
   444           {
   444 					{
   445             $points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1)));
   445 						$points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1)));
   446             $points[] = $this->rand_func(30,$height+30);
   446 						$points[] = $this->rand_func(30,$height+30);
   447           }
   447 					}
   448     
   448 		
   449           ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3);
   449 					ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3);
   450         }
   450 				}
   451     
   451 		
   452       } else if($this->bg_type==3) {
   452 			} else if($this->bg_type==3) {
   453         // take random chunks of $this->bg_images and paste them onto the background
   453 				// take random chunks of $this->bg_images and paste them onto the background
   454     
   454 		
   455         for($i=0 ; $i<sizeof($this->bg_images) ; $i++)
   455 				for($i=0 ; $i<sizeof($this->bg_images) ; $i++)
   456         {
   456 				{
   457           // read each image and its size
   457 					// read each image and its size
   458           $temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]);
   458 					$temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]);
   459           $temp_width[$i] = imagesx($temp_im[$i]);
   459 					$temp_width[$i] = imagesx($temp_im[$i]);
   460           $temp_height[$i] = imagesy($temp_im[$i]);
   460 					$temp_height[$i] = imagesy($temp_im[$i]);
   461         }
   461 				}
   462         
   462 				
   463         $blocksize = $this->rand_func(20,60);
   463 				$blocksize = $this->rand_func(20,60);
   464         for($i=0 ; $i<$width*2 ; $i+=$blocksize)
   464 				for($i=0 ; $i<$width*2 ; $i+=$blocksize)
   465         {
   465 				{
   466           // could randomise blocksize here... hardly matters
   466 					// could randomise blocksize here... hardly matters
   467           for($j=0 ; $j<$height*2 ; $j+=$blocksize)
   467 					for($j=0 ; $j<$height*2 ; $j+=$blocksize)
   468           {
   468 					{
   469             $this->image_index = $this->rand_func(0,sizeof($temp_im)-1);
   469 						$this->image_index = $this->rand_func(0,sizeof($temp_im)-1);
   470             $cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize);
   470 						$cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize);
   471             $cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize);
   471 						$cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize);
   472             ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
   472 						ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
   473           }
   473 					}
   474         }
   474 				}
   475         for($i=0 ; $i<sizeof($temp_im) ; $i++)
   475 				for($i=0 ; $i<sizeof($temp_im) ; $i++)
   476         {
   476 				{
   477           // remove bgs from memory
   477 					// remove bgs from memory
   478           ImageDestroy($temp_im[$i]);
   478 					ImageDestroy($temp_im[$i]);
   479         }
   479 				}
   480     
   480 		
   481         // for debug:
   481 				// for debug:
   482         //sendImage($temp_bg);
   482 				//sendImage($temp_bg);
   483       }
   483 			}
   484     
   484 		
   485       // for debug:
   485 			// for debug:
   486       //sendImage($this->im3);
   486 			//sendImage($this->im3);
   487     
   487 		
   488       if($this->morph_bg)
   488 			if($this->morph_bg)
   489       {
   489 			{
   490         // morph background
   490 				// morph background
   491         // we do this separately to the main text morph because:
   491 				// we do this separately to the main text morph because:
   492         // a) the main text morph is done char-by-char, this is done across whole image
   492 				// a) the main text morph is done char-by-char, this is done across whole image
   493         // b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA
   493 				// b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA
   494         // hence bg is morphed differently to text
   494 				// hence bg is morphed differently to text
   495         // why do we morph it at all? it might make it harder for an attacker to remove the background
   495 				// why do we morph it at all? it might make it harder for an attacker to remove the background
   496         // morph_chunk 1 looks better but takes longer
   496 				// morph_chunk 1 looks better but takes longer
   497     
   497 		
   498         // this is a different and less perfect morph than the one we do on the CAPTCHA
   498 				// this is a different and less perfect morph than the one we do on the CAPTCHA
   499         // occasonally you get some dark background showing through around the edges
   499 				// occasonally you get some dark background showing through around the edges
   500         // it doesn't need to be perfect as it's only the bg.
   500 				// it doesn't need to be perfect as it's only the bg.
   501         $morph_chunk = $this->rand_func(1,5);
   501 				$morph_chunk = $this->rand_func(1,5);
   502         $morph_y = 0;
   502 				$morph_y = 0;
   503         for($x=0 ; $x<$width ; $x+=$morph_chunk)
   503 				for($x=0 ; $x<$width ; $x+=$morph_chunk)
   504         {
   504 				{
   505           $morph_chunk = $this->rand_func(1,5);
   505 					$morph_chunk = $this->rand_func(1,5);
   506           $morph_y += $this->rand_func(-1,1);
   506 					$morph_y += $this->rand_func(-1,1);
   507           ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
   507 					ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
   508         }
   508 				}
   509     
   509 		
   510         ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height);
   510 				ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height);
   511     
   511 		
   512         $morph_x = 0;
   512 				$morph_x = 0;
   513         for($y=0 ; $y<=$height; $y+=$morph_chunk)
   513 				for($y=0 ; $y<=$height; $y+=$morph_chunk)
   514         {
   514 				{
   515           $morph_chunk = $this->rand_func(1,5);
   515 					$morph_chunk = $this->rand_func(1,5);
   516           $morph_x += $this->rand_func(-1,1);
   516 					$morph_x += $this->rand_func(-1,1);
   517           ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk);
   517 					ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk);
   518     
   518 		
   519         }
   519 				}
   520       } else {
   520 			} else {
   521         // just copy temp_bg onto im3
   521 				// just copy temp_bg onto im3
   522         ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height);
   522 				ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height);
   523       }
   523 			}
   524     
   524 		
   525       ImageDestroy($temp_bg);
   525 			ImageDestroy($temp_bg);
   526     
   526 		
   527       if($this->blur_bg)
   527 			if($this->blur_bg)
   528       {
   528 			{
   529         $this->myImageBlur($this->im3);
   529 				$this->myImageBlur($this->im3);
   530       }
   530 			}
   531     }
   531 		}
   532     // for debug:
   532 		// for debug:
   533     //sendImage($this->im3);
   533 		//sendImage($this->im3);
   534     
   534 		
   535     //////////////////////////////////////////////////////
   535 		//////////////////////////////////////////////////////
   536     ////// Write Word
   536 		////// Write Word
   537     //////////////////////////////////////////////////////
   537 		//////////////////////////////////////////////////////
   538     
   538 		
   539     // write word in random starting X position
   539 		// write word in random starting X position
   540     $word_start_x = $this->rand_func(5,32);
   540 		$word_start_x = $this->rand_func(5,32);
   541     // y positions jiggled about later
   541 		// y positions jiggled about later
   542     $word_start_y = 50;
   542 		$word_start_y = 50;
   543     
   543 		
   544     // use last pixelwidth
   544 		// use last pixelwidth
   545     $font_pixelwidth = $this->font_size + 10;
   545 		$font_pixelwidth = $this->font_size + 10;
   546     
   546 		
   547     if($this->col_type==0)
   547 		if($this->col_type==0)
   548     {
   548 		{
   549       $text_r = $this->rand_color();
   549 			$text_r = $this->rand_color();
   550       $text_g = $this->rand_color();
   550 			$text_g = $this->rand_color();
   551       $text_b = $this->rand_color();
   551 			$text_b = $this->rand_color();
   552       $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
   552 			$text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
   553     }
   553 		}
   554     
   554 		
   555     // write each char in different font
   555 		// write each char in different font
   556     for($i=0 ; $i<strlen($word) ; $i++)
   556 		for($i=0 ; $i<strlen($word) ; $i++)
   557     {
   557 		{
   558       if($this->col_type==1)
   558 			if($this->col_type==1)
   559       {
   559 			{
   560         $text_r = $this->rand_color();
   560 				$text_r = $this->rand_color();
   561         $text_g = $this->rand_color();
   561 				$text_g = $this->rand_color();
   562         $text_b = $this->rand_color();
   562 				$text_b = $this->rand_color();
   563         $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
   563 				$text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
   564       }
   564 			}
   565     
   565 		
   566       $j = $this->rand_func(0,sizeof($this->font_locations)-1);
   566 			$j = $this->rand_func(0,sizeof($this->font_locations)-1);
   567       // $font = ImageLoadFont($this->font_locations[$j]);
   567 			// $font = ImageLoadFont($this->font_locations[$j]);
   568       // ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2);
   568 			// ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2);
   569       ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i});
   569 			ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i});
   570     }
   570 		}
   571     
   571 		
   572     // for debug:
   572 		// for debug:
   573     // $this->sendImage($this->im2);
   573 		// $this->sendImage($this->im2);
   574     
   574 		
   575     //////////////////////////////////////////////////////
   575 		//////////////////////////////////////////////////////
   576     ////// Morph Image:
   576 		////// Morph Image:
   577     //////////////////////////////////////////////////////
   577 		//////////////////////////////////////////////////////
   578     
   578 		
   579     // calculate how big the text is in pixels
   579 		// calculate how big the text is in pixels
   580     // (so we only morph what we need to)
   580 		// (so we only morph what we need to)
   581     $word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth);
   581 		$word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth);
   582     
   582 		
   583     // firstly move each character up or down a bit:
   583 		// firstly move each character up or down a bit:
   584     $y_pos = 0;
   584 		$y_pos = 0;
   585     for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth)
   585 		for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth)
   586     {
   586 		{
   587       // move on Y axis
   587 			// move on Y axis
   588       // deviates at least 4 pixels between each letter
   588 			// deviates at least 4 pixels between each letter
   589       $prev_y = $y_pos;
   589 			$prev_y = $y_pos;
   590       do{
   590 			do{
   591         $y_pos = $this->rand_func(-5,5);
   591 				$y_pos = $this->rand_func(-5,5);
   592       } while($y_pos<$prev_y+2 && $y_pos>$prev_y-2);
   592 			} while($y_pos<$prev_y+2 && $y_pos>$prev_y-2);
   593       ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height);
   593 			ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height);
   594     
   594 		
   595       // for debug:
   595 			// for debug:
   596       // ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug);
   596 			// ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug);
   597     }
   597 		}
   598     
   598 		
   599     // for debug:
   599 		// for debug:
   600     // $this->sendImage($this->im);
   600 		// $this->sendImage($this->im);
   601     
   601 		
   602     ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
   602 		ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
   603     
   603 		
   604     // randomly morph each character individually on x-axis
   604 		// randomly morph each character individually on x-axis
   605     // this is where the main distortion happens
   605 		// this is where the main distortion happens
   606     // massively improved since v1.2
   606 		// massively improved since v1.2
   607     $y_chunk = 1;
   607 		$y_chunk = 1;
   608     $morph_factor = 1;
   608 		$morph_factor = 1;
   609     $morph_x = 0;
   609 		$morph_x = 0;
   610     for($j=0 ; $j<strlen($word) ; $j++)
   610 		for($j=0 ; $j<strlen($word) ; $j++)
   611     {
   611 		{
   612       $y_pos = 0;
   612 			$y_pos = 0;
   613       for($i=0 ; $i<=$height; $i+=$y_chunk)
   613 			for($i=0 ; $i<=$height; $i+=$y_chunk)
   614       {
   614 			{
   615         $orig_x = $word_start_x+($j*$font_pixelwidth);
   615 				$orig_x = $word_start_x+($j*$font_pixelwidth);
   616         // morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
   616 				// morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
   617         // get it? instead of a zig zag, we get more of a sine wave.
   617 				// get it? instead of a zig zag, we get more of a sine wave.
   618         // I wish we could deviate more but it looks crap if we do.
   618 				// I wish we could deviate more but it looks crap if we do.
   619         $morph_x += $this->rand_func(-$morph_factor,$morph_factor);
   619 				$morph_x += $this->rand_func(-$morph_factor,$morph_factor);
   620         // had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
   620 				// had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
   621         // according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
   621 				// according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
   622         // but this is NOT true when dealing with transparencies...
   622 				// but this is NOT true when dealing with transparencies...
   623         ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100);
   623 				ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100);
   624     
   624 		
   625         // for debug:
   625 				// for debug:
   626         //ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2);
   626 				//ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2);
   627         //ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2);
   627 				//ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2);
   628       }
   628 			}
   629     }
   629 		}
   630     
   630 		
   631     // for debug:
   631 		// for debug:
   632     //sendImage($this->im2);
   632 		//sendImage($this->im2);
   633     
   633 		
   634     ImageFilledRectangle($this->im,0,0,$width,$height,$bg);
   634 		ImageFilledRectangle($this->im,0,0,$width,$height,$bg);
   635     // now do the same on the y-axis
   635 		// now do the same on the y-axis
   636     // (much easier because we can just do it across the whole image, don't have to do it char-by-char)
   636 		// (much easier because we can just do it across the whole image, don't have to do it char-by-char)
   637     $y_pos = 0;
   637 		$y_pos = 0;
   638     $x_chunk = 1;
   638 		$x_chunk = 1;
   639     for($i=0 ; $i<=$width ; $i+=$x_chunk)
   639 		for($i=0 ; $i<=$width ; $i+=$x_chunk)
   640     {
   640 		{
   641       // can result in image going too far off on Y-axis;
   641 			// can result in image going too far off on Y-axis;
   642       // not much I can do about that, apart from make image bigger
   642 			// not much I can do about that, apart from make image bigger
   643       // again, I wish I could do 1.5 pixels
   643 			// again, I wish I could do 1.5 pixels
   644       $y_pos += $this->rand_func(-1,1);
   644 			$y_pos += $this->rand_func(-1,1);
   645       ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height);
   645 			ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height);
   646     
   646 		
   647       // for debug:
   647 			// for debug:
   648       //ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug);
   648 			//ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug);
   649       //ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug);
   649 			//ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug);
   650     }
   650 		}
   651     
   651 		
   652     // for debug:
   652 		// for debug:
   653     //sendImage($this->im);
   653 		//sendImage($this->im);
   654     
   654 		
   655     // blur edges:
   655 		// blur edges:
   656     // doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
   656 		// doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
   657     // for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
   657 		// for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
   658     // (and if you do, let me know why)
   658 		// (and if you do, let me know why)
   659     $this->myImageBlur($this->im);
   659 		$this->myImageBlur($this->im);
   660     
   660 		
   661     // for debug:
   661 		// for debug:
   662     //sendImage($this->im);
   662 		//sendImage($this->im);
   663     
   663 		
   664     if($this->output!="jpg" && $this->bg_type==0)
   664 		if($this->output!="jpg" && $this->bg_type==0)
   665     {
   665 		{
   666       // make background transparent
   666 			// make background transparent
   667       ImageColorTransparent($this->im,$bg);
   667 			ImageColorTransparent($this->im,$bg);
   668     }
   668 		}
   669     
   669 		
   670     
   670 		
   671     
   671 		
   672     
   672 		
   673     
   673 		
   674     //////////////////////////////////////////////////////
   674 		//////////////////////////////////////////////////////
   675     ////// Try to avoid 'free p*rn' style CAPTCHA re-use
   675 		////// Try to avoid 'free p*rn' style CAPTCHA re-use
   676     //////////////////////////////////////////////////////
   676 		//////////////////////////////////////////////////////
   677     // ('*'ed to stop my site coming up for certain keyword searches on google)
   677 		// ('*'ed to stop my site coming up for certain keyword searches on google)
   678     
   678 		
   679     // can obscure CAPTCHA word in some cases..
   679 		// can obscure CAPTCHA word in some cases..
   680     
   680 		
   681     // write site tags 'shining through' the morphed image
   681 		// write site tags 'shining through' the morphed image
   682     ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
   682 		ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
   683     if(is_array($this->site_tags))
   683 		if(is_array($this->site_tags))
   684     {
   684 		{
   685       for($i=0 ; $i<sizeof($this->site_tags) ; $i++)
   685 			for($i=0 ; $i<sizeof($this->site_tags) ; $i++)
   686       {
   686 			{
   687         // ensure tags are centered
   687 				// ensure tags are centered
   688         $tag_width = strlen($this->site_tags[$i])*6;
   688 				$tag_width = strlen($this->site_tags[$i])*6;
   689         // write tag is chosen position
   689 				// write tag is chosen position
   690         if($this->tag_pos==0 || $this->tag_pos==2)
   690 				if($this->tag_pos==0 || $this->tag_pos==2)
   691         {
   691 				{
   692           // write at top
   692 					// write at top
   693           ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2);
   693 					ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2);
   694         }
   694 				}
   695         if($this->tag_pos==1 || $this->tag_pos==2)
   695 				if($this->tag_pos==1 || $this->tag_pos==2)
   696         {
   696 				{
   697           // write at bottom
   697 					// write at bottom
   698           ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2);
   698 					ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2);
   699         }
   699 				}
   700       }
   700 			}
   701     }
   701 		}
   702     ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80);
   702 		ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80);
   703     ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height);
   703 		ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height);
   704     // for debug:
   704 		// for debug:
   705     //sendImage($this->im);
   705 		//sendImage($this->im);
   706     
   706 		
   707     
   707 		
   708     
   708 		
   709     
   709 		
   710     //////////////////////////////////////////////////////
   710 		//////////////////////////////////////////////////////
   711     ////// Merge with obfuscated background
   711 		////// Merge with obfuscated background
   712     //////////////////////////////////////////////////////
   712 		//////////////////////////////////////////////////////
   713     
   713 		
   714     if($this->bg_type!=0)
   714 		if($this->bg_type!=0)
   715     {
   715 		{
   716       // merge bg image with CAPTCHA image to create smooth background
   716 			// merge bg image with CAPTCHA image to create smooth background
   717     
   717 		
   718       // fade bg:
   718 			// fade bg:
   719       if($this->bg_type!=3)
   719 			if($this->bg_type!=3)
   720       {
   720 			{
   721         $temp_im = ImageCreateTrueColor($width,$height);
   721 				$temp_im = ImageCreateTrueColor($width,$height);
   722         $white = ImageColorAllocate($temp_im,255,255,255);
   722 				$white = ImageColorAllocate($temp_im,255,255,255);
   723         ImageFill($temp_im,0,0,$white);
   723 				ImageFill($temp_im,0,0,$white);
   724         ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct);
   724 				ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct);
   725         // for debug:
   725 				// for debug:
   726         //sendImage($this->im3);
   726 				//sendImage($this->im3);
   727         ImageDestroy($temp_im);
   727 				ImageDestroy($temp_im);
   728         $c_fade_pct = 50;
   728 				$c_fade_pct = 50;
   729       } else {
   729 			} else {
   730         $c_fade_pct = $bg_fade_pct;
   730 				$c_fade_pct = $bg_fade_pct;
   731       }
   731 			}
   732     
   732 		
   733       // captcha over bg:
   733 			// captcha over bg:
   734       // might want to not blur if using this method
   734 			// might want to not blur if using this method
   735       // otherwise leaves white-ish border around each letter
   735 			// otherwise leaves white-ish border around each letter
   736       if($this->merge_type==1)
   736 			if($this->merge_type==1)
   737       {
   737 			{
   738         ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100);
   738 				ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100);
   739         ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height);
   739 				ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height);
   740       } else {
   740 			} else {
   741         // bg over captcha:
   741 				// bg over captcha:
   742         ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct);
   742 				ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct);
   743       }
   743 			}
   744     }
   744 		}
   745     // for debug:
   745 		// for debug:
   746     //sendImage($this->im);
   746 		//sendImage($this->im);
   747     
   747 		
   748     
   748 		
   749     //////////////////////////////////////////////////////
   749 		//////////////////////////////////////////////////////
   750     ////// Write tags, remove variables and output!
   750 		////// Write tags, remove variables and output!
   751     //////////////////////////////////////////////////////
   751 		//////////////////////////////////////////////////////
   752     
   752 		
   753     // tag it
   753 		// tag it
   754     // feel free to remove/change
   754 		// feel free to remove/change
   755     // but if it's not essential I'd appreciate you leaving it
   755 		// but if it's not essential I'd appreciate you leaving it
   756     // after all, I've put a lot of work into this and am giving it away for free
   756 		// after all, I've put a lot of work into this and am giving it away for free
   757     // the least you could do is give me credit (or buy me stuff from amazon!)
   757 		// the least you could do is give me credit (or buy me stuff from amazon!)
   758     // but I understand that in professional environments, your boss might not like this tag
   758 		// but I understand that in professional environments, your boss might not like this tag
   759     // so that's cool.
   759 		// so that's cool.
   760     $tag_str = "";
   760 		$tag_str = "";
   761     // for debug:
   761 		// for debug:
   762     //$tag_str = "[".$word."]";
   762 		//$tag_str = "[".$word."]";
   763     
   763 		
   764     // ensure tag is right-aligned
   764 		// ensure tag is right-aligned
   765     $tag_width = strlen($tag_str)*6;
   765 		$tag_width = strlen($tag_str)*6;
   766     // write tag
   766 		// write tag
   767     ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col);
   767 		ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col);
   768     
   768 		
   769     // unset all sensetive vars
   769 		// unset all sensetive vars
   770     // in case someone include()s this file on a shared server
   770 		// in case someone include()s this file on a shared server
   771     // you might think this unneccessary, as it exit()s
   771 		// you might think this unneccessary, as it exit()s
   772     // but by using register_shutdown_function
   772 		// but by using register_shutdown_function
   773     // on a -very- insecure shared server, they -might- be able to get the word
   773 		// on a -very- insecure shared server, they -might- be able to get the word
   774     unset($word);
   774 		unset($word);
   775     // the below aren't really essential, but might aid an OCR attack if discovered.
   775 		// the below aren't really essential, but might aid an OCR attack if discovered.
   776     // so we unset them
   776 		// so we unset them
   777     
   777 		
   778     // output final image :-)
   778 		// output final image :-)
   779     $this->sendImage($this->im);
   779 		$this->sendImage($this->im);
   780     // (sendImage also destroys all used images)
   780 		// (sendImage also destroys all used images)
   781   }
   781 	}
   782   
   782 	
   783   function rand_func($s, $m)
   783 	function rand_func($s, $m)
   784   {
   784 	{
   785     global $_starttime;
   785 		global $_starttime;
   786     $tn = microtime_float() - $_starttime;
   786 		$tn = microtime_float() - $_starttime;
   787     if ( $tn > 5 )
   787 		if ( $tn > 5 )
   788     {
   788 		{
   789       echo '<pre>';
   789 			echo '<pre>';
   790       enano_debug_print_backtrace();
   790 			enano_debug_print_backtrace();
   791       echo '</pre>';
   791 			echo '</pre>';
   792       exit;
   792 			exit;
   793     }
   793 		}
   794     $rf =& $this->rand_func;
   794 		$rf =& $this->rand_func;
   795     return $rf($s, $m);
   795 		return $rf($s, $m);
   796   }
   796 	}
   797   
   797 	
   798   function seed_func($s)
   798 	function seed_func($s)
   799   {
   799 	{
   800     $rf =& $this->seed_func;
   800 		$rf =& $this->seed_func;
   801     return $rf($s);
   801 		return $rf($s);
   802   }
   802 	}
   803   
   803 	
   804   function hash_func($s)
   804 	function hash_func($s)
   805   {
   805 	{
   806     $rf =& $this->hash_func;
   806 		$rf =& $this->hash_func;
   807     return $rf($s);
   807 		return $rf($s);
   808   }
   808 	}
   809   
   809 	
   810 }
   810 }