Development

/plugins/sfCryptoCaptchaPlugin/trunk/lib/sfCryptoCaptcha.class.php

You must first sign up to be able to contribute.

root/plugins/sfCryptoCaptchaPlugin/trunk/lib/sfCryptoCaptcha.class.php

Revision 19570, 39.8 kB (checked in by henearkrxern, 4 years ago)

Update to version 0.0.6

Line 
1 <?php
2
3 /**
4  * Captacha generation based on the Cryptographp script (www.captcha.fr)
5  *
6  * Usage: $captcha = new sfCryptoCaptcha();
7  *        $captcha->getCaptchaImage();
8  *
9  * @package    sfCryptoCaptchaPlugin
10  * @subpackage lib
11  * @author     HeNeArKrXeRn <henearkrxern [at] hotmail.fr>
12  * @version    1.0
13  */
14 class sfCryptoCaptcha
15 {
16   //config holder
17   private $config = array();
18  
19   //image resource
20   private $image;
21   //image resource used when adding stuff or deleting stuff (used as a temporary image resource container)
22   private $temp_image;
23  
24   //captcha array (word, fonts, colors etc.)
25   private $captcha = array();
26  
27   //the sfUser local object
28   private $sf_user;
29  
30  
31   public function __construct($test_queries_flood=true)
32   {
33     $this->getConfiguration();
34     
35     //get sfUser
36     $user = sfContext::getInstance()->getUser();
37     //test if sfUser ok
38     $test_value = 'done';
39     $user->setAttribute('test',$test_value);
40     
41     if($user->getAttribute('test') == $test_value)
42     {
43       //remove the test and add the user object into the class
44       $user->getAttributeHolder()->remove('test');
45       $this->sf_user = $user;
46       
47       if($test_queries_flood == true)
48       {
49         //test the number of queries and other stuff
50         if($this->testQueries() && $this->testLastRequest())
51         {
52           //all good to go :)
53           $this->config['constructor_test'] = true;
54           return true;
55         }
56         else
57         {
58           $this->config['constructor_test'] = false;
59           if(!$this->testQueries()) {
60             $this->config['constructor_error_reason'] = 'Error - too many queries';
61             $this->config['constructor_error_message'] = 'too_many';
62           }
63           elseif(!$this->testLastRequest())
64           {
65             $this->config['constructor_error_reason'] = 'Error - refreshing too fast';
66             $this->config['constructor_error_message'] = 'refresh';
67           }
68           else
69           {
70             $this->config['constructor_error_reason'] = 'Error - unknown reason';
71             $this->config['constructor_error_message'] = 'unknown';
72           }
73           return false;
74         }
75       }
76       else
77       {
78         //no testing (for captcha test - tests manually disabled because no need to double check)
79         $this->config['constructor_test']= true;
80         return true;
81       }
82     }
83   }
84  
85   /**
86    * Tests the number of queries done
87    *
88    * Tests the number of queries done by the user
89    * to block flood attempts
90    *
91    * @return bool Ok if the number of queries is under the limit per session
92    */
93   private function testQueries()
94   {
95     if($this->sf_user->getAttribute('queries', null, 'captcha') == '' || $this->sf_user->getAttribute('queries', null, 'captcha') == 0)
96     {
97       $this->sf_user->setAttribute('queries',1, 'captcha');
98       return true;
99     }
100     elseif($this->sf_user->getAttribute('queries', null, 'captcha') >= $this->config['max_refresh'])
101     {
102       return false;
103     }
104     else
105     {
106       $this->sf_user->setAttribute('queries',$this->sf_user->getAttribute('queries', null, 'captcha')+1, 'captcha');
107       return true;
108     }   
109   }
110  
111   /**
112    * Tests the last request time
113    *
114    * Tests the last request time done by the user to block
115    * too fast refreshes
116    *
117    * @return bool Ok if the last request was not too soon from now
118    */
119   private function testLastRequest()
120   {
121     if($this->sf_user->getAttribute('last_request', null, 'captcha') == '' || $this->sf_user->getAttribute('last_request', null, 'captcha') == 0)
122     {
123       $this->sf_user->setAttribute('last_request',time(), 'captcha');
124       return true;
125     }
126     else
127     {
128       $delay = time() - $this->sf_user->getAttribute('last_request', null, 'captcha');
129       if($this->config['flood_timer'] != 0 && $this->config['flood_timer'] > $delay)
130       {
131         //this user is flooding.
132         return false;
133       }
134       else
135       {
136         $this->sf_user->setAttribute('last_request',time(), 'captcha');
137         return true;
138       }
139     }
140   }
141  
142   /**
143    * Generates the captcha image
144    *
145    * This function generates the captcha variables and
146    * creates the image.
147    *
148    * @return resource Returns the image to the browser of the user
149    */
150   public function getCaptchaImage()
151   {
152     if($this->config['constructor_test']===false) {
153       //generate error image
154       if(!$this->sendErrorImage($this->config['constructor_error_message']))
155       {
156         if (sfConfig::get('sf_logging_enabled'))
157         {
158           $message = '{sfCaptcha}';
159           $message .= ' - Critical Image Error - ';
160           $message .= 'The ERROR message(['.$this->config['constructor_error_reason'].']) image could not be sent to the user.';
161           $message .= ' Possible problem with file access/path.';
162           sfContext::getInstance()->getLogger()->crit($message);
163         }
164       }
165       elseif(!$this->generateErrorImage($this->config['constructor_error_reason']))
166       {
167         if (sfConfig::get('sf_logging_enabled'))
168         {
169           $message = '{sfCaptcha}';
170           $message .= ' - Critical Image Error - ';
171           $message .= 'The ERROR message(['.$this->config['constructor_error_reason'].']) image encoutered an error when sending to user.';
172           $message .= ' Possible problem with GD2 library.';
173           sfContext::getInstance()->getLogger()->crit($message);
174         }
175       }
176       //finish the operation: delete the error image
177       imagedestroy($this->image);
178       //clear attributes
179       $config = array();
180       $captcha = array();
181       $image = ''; //clear image
182       $temp_image = ''; //clear temporary image
183       return true;
184     }
185     //generate the captcha attributes
186     $this->generateCaptcha();
187     
188     //align and adjust captcha
189     $this->adjustCaptcha();
190     
191     //destroy the temporary image
192     imagedestroy ($this->temp_image);
193     
194     //select a background image (from the file) or set background color
195     $this->selectBackground();
196     
197     //create the image (starting by the background)
198     $this->createCaptchaImageAndBackground();
199     
200     //select if noise or chars first and add them
201     $this->setNoiseParameters();
202     if($this->config['noise_on_top'])
203     {
204       $this->addLettersToImage();
205       $this->addNoiseToImage();
206     }
207     else
208     {
209       $this->addNoiseToImage();
210       $this->addLettersToImage();
211     }
212     $this->clearBrush(); //destroys the brush (EXTREMELY IMPORTANT FOR THE NOISE BRUSH TO FUNCTION PROPERLY)
213     
214     $this->addBorder();
215     $this->addEffect(); //blur, grayscale
216     
217     //set user attributes (word into session etc.)
218     $this->setUserAttributes();
219     
220     if(!$this->sendImageToBrowser())
221     {
222       if (sfConfig::get('sf_logging_enabled'))
223       {
224         $message = '{seCaptcha}';
225         $message .= ' - Critical Image Error - ';
226         $message .= 'The NORMAL image encoutered an error when sending to user.';
227         $message .= ' Possbile problem with GD2 library.';
228         sfContext::getInstance()->getLogger()->crit($message);
229       }
230     }
231     
232     
233     //finish the operation: delete image and user unused attributes
234     imagedestroy($this->image);
235     //clear attributes
236     $config = array();
237     $captcha = array();
238     $image = ''; //clear image
239     $temp_image = ''; //clear temporary image
240     
241     return true;
242   }
243  
244   /**
245    * Tests the user's captcha
246    *
247    * Tests if the captcha given by the user is correct.
248    *
249    * @return bool Ok if the captcha is correct
250    */
251   public function testCaptcha($input)
252   {
253     if(!$this->config['case_sensitive'])
254     {
255       $input = strtoupper($input);
256     }
257     
258     //hash the input
259     if(empty($this->config['hash_algo']))
260     {
261       $input_hash = hash('sha1',$input);
262     }
263     else
264     {
265       $input_hash = hash($this->config['hash_algo'],$input);
266     }
267     
268     //compare the two hashes (the one in memory and the one with the captcha)
269     $saved_captcha = $this->sf_user->getAttribute('captcha_code', null, 'captcha');
270     if($input_hash == $saved_captcha)
271     {
272       //clear all and return good :)
273       $this->clearUserCaptchaAttributes();
274       return true;
275     }
276     else
277     {
278       //no good! return bad!
279       return false;
280     }
281   }
282  
283   /**
284    * Clears all the user captcha attributes (last_request, queries, ...)
285    *
286    * @return bool Always true
287    */
288   public function clearUserCaptchaAttributes()
289   {
290     $this->sf_user->getAttributeHolder()->removeNamespace('captcha');
291     return true;
292   }
293  
294   /**
295    * Generates the captcha
296    *
297    * It generates the captcha atributes and other stuff
298    * and saves it in the class $captcha attribute
299    *
300    * @return bool Ok if the captcha has been correctly generated.
301    */
302   private function generateCaptcha()
303   {
304     $this->captcha['word'] = '';
305     $this->captcha['chars'] = rand($this->config['min_chars'],$this->config['max_chars']);
306     
307     $x_coord = 10; //starting position of the captcha on the X axis.
308     //generate the captcha
309     for($i=1; $i<= $this->captcha['chars']; $i++)
310     {
311       //font
312       $this->captcha['letters'][$i]['font'] = $this->getRandomFont();
313       $this->captcha['letters'][$i]['font_path'] = $this->config['char_fonts_dir'].$this->captcha['letters'][$i]['font'];
314       
315       //ink color
316       $this->captcha['letters'][$i]['ink_type'] = $this->getInkType();
317       $this->captcha['letters'][$i]['ink_colors'] = $this->getInkColors();
318       
319       //rotation
320       $this->captcha['letters'][$i]['rotation'] = $this->getRandomLetterRotation();
321
322       //character
323       $this->captcha['letters'][$i]['char'] = $this->getRandomCharacter();
324       
325       //size
326       $this->captcha['letters'][$i]['size'] = $this->getRandomCharacterSize();
327       
328       //vertical offset of the letter
329       $this->captcha['letters'][$i]['y_coord'] = $this->getRandomVerticalOffset();
330       
331       //Add the letter to the complete word
332       $this->captcha['word'] .= $this->captcha['letters'][$i]['char'];
333       
334       //save the letter coordinate and increase the counter
335       $this->captcha['letters'][$i]['x_coord'] = $x_coord;
336       $x_coord += $this->config['char_px_spacing'];
337     }
338     
339     return true;
340   }
341  
342   /**
343    * Adjusts the captcha to the image
344    *
345    * It adapts the characters to the image so none is out of bounds
346    * and saved the x offset coordinate
347    *
348    * @return bool Ok if the captcha has been correctly aligned.
349    */
350   private function adjustCaptcha()
351   {
352     //generate temporary image
353     $this->generateRawTempImage();
354     
355     //add chars into the image
356     $this->addCaptchaOnTempImage();
357     
358     //adjust the temp captcha and get the offset
359     $x_coord_adjust = $this->getAdjustmentOffset();
360     if(!empty($x_coord_adjust))
361     {
362       $this->captcha['x_coord_adjust'] = $x_coord_adjust;
363       
364       //update characters x coord with the new $xadjustment
365       $this->updateCaptchaXCoord();
366       
367       return true;
368     }
369     else
370     {
371       return false;
372     }
373   }
374  
375  
376   /**
377    * Randomly selects a font in the available fonts
378    *
379    * @return string The randomly selected font
380    */
381   private function getRandomFont()
382   {
383     return $this->config['char_fonts'][array_rand($this->config['char_fonts'],1)];
384   }
385  
386   /**
387    * Randomly generates an angle
388    *
389    * @return string The randomly generated angle
390    */
391   private function getRandomLetterRotation()
392   {
393     if(rand(0,1))
394     {
395       return rand(0, $this->config['char_max_rot_angle']);
396     }
397     else
398     {
399       return rand(360-$this->config['char_max_rot_angle'],360);
400     }
401   }
402  
403   /**
404    * Randomly selects a character
405    *
406    * The function randomly selects characters to make the captcha
407    * word. If the easy captcha is activated, it alternates
408    * between vowels and consonants.
409    *
410    * @return string The randomly selected letter
411    */
412   private function getRandomCharacter()
413   {
414     //test if easy captcha activated   
415     if($this->config['easy_captcha'])
416     {
417       //select if vowel or consonant
418       if($this->config['easy_captcha_bool'] == 1)
419       {
420         //invert for next letter (vowel/consonant)
421         $this->config['easy_captcha_bool'] = 0;
422         return $this->config['easy_captcha_consonants']{rand(0,strlen($this->config['easy_captcha_consonants'])-1)};
423       }
424       else
425       {
426         //invert for next letter (vowel/consonant)
427         $this->config['easy_captcha_bool'] = 1;
428         return $this->config['easy_captcha_vowels']{rand(0,strlen($this->config['easy_captcha_vowels'])-1)};
429       }
430     }
431     else
432     {
433       //random character in the authorized characters
434       return $this->config['chars_used']{rand(0,strlen($this->config['chars_used'])-1)};
435     }
436   }
437  
438   /**
439    * Randomly selects a character size
440    *
441    * @return string The randomly selected size
442    */
443   private function getRandomCharacterSize()
444   {
445     return rand($this->config['char_min_size'],$this->config['char_max_size']);
446   }
447  
448   /**
449    * Randomly generates a vertical offset
450    *
451    * @return string The randomly generated offset
452    */
453   private function getRandomVerticalOffset()
454   {
455     if($this->config['char_vertical_offset'])
456     {
457       $vertical_offset = $this->config['height']/2;
458       $vertical_offset += rand(0,round($this->config['height']/5));
459     }
460     else
461     {
462       $vertical_offset = round($this->config['height']/1.5);
463     }
464     return $vertical_offset;
465   }
466  
467   /**
468    * Generates an image with a white background in the $temp_image class attribute
469    *
470    * @return bool True if the generation is successfull.
471    */
472   private function generateRawTempImage()
473   {
474     $this->temp_image = imagecreatetruecolor($this->config['width'],$this->config['height']);
475     $white = imagecolorallocate($this->temp_image,255,255,255);
476     
477     if(!imagefill($this->temp_image,0,0,$white))
478     {
479       return false;
480     }
481     else
482     {
483       return true;
484     }
485   }
486  
487   /**
488    * Adds the captcha letters on the temporary image
489    *
490    * @return bool True if the adding is successfull
491    */
492   private function addCaptchaOnTempImage()
493   {
494     //add each character
495     for($i=1; $i<= $this->captcha['chars']; $i++)
496     {
497       $black_ink = imagecolorallocate($this->temp_image,0,0,0);
498       //add letter to image
499       if(!imagettftext($this->temp_image,
500                        $this->captcha['letters'][$i]['size'],
501                        $this->captcha['letters'][$i]['rotation'],
502                        $this->captcha['letters'][$i]['x_coord'],
503                        $this->captcha['letters'][$i]['y_coord'],
504                        $black_ink,
505                        $this->captcha['letters'][$i]['font_path'],
506                        $this->captcha['letters'][$i]['char']))
507       {
508         return false;
509       }
510     }
511     return true;
512   }
513  
514   /**
515    * Computes the x coordinate offset
516    *
517    * @return int The x coordiante offset
518    */
519   private function getAdjustmentOffset()
520   {
521     $white = imagecolorallocate($this->temp_image,255,255,255);
522     
523     //Adjust the X begin coordinate
524     $xbegin = 0;
525     $x = 0;
526     while ($x < $this->config['width'] && !$xbegin )
527     {
528       $y = 0;
529       while ($y < $this->config['height'] && !$xbegin )
530       {
531         if(imagecolorat($this->temp_image, $x, $y) != $white )
532         {
533           $xbegin = $x;
534         }
535         $y++;
536       }
537       $x++;
538     }
539     $xend = 0;
540     
541     //Adjust the X end coordinate
542     $x = $this->config['width'] - 1;
543     while($x > 0 && !$xend)
544     {
545       $y = 0;
546       while ($y < $this->config['height'] && !$xend)
547       {
548         if(imagecolorat($this->temp_image, $x, $y) != $white)
549         {
550           $xend = $x;
551         }
552         $y++;
553       }
554       $x--;
555     }
556     
557     //Compute the adjustment
558     $xadjustment = round(($this->config['width']/2)-($xend-$xbegin)/2);
559     
560     return $xadjustment;
561   }
562  
563   /**
564    * Selects the final captcha background. Random image if file in configuration
565    *
566    * In all cases, it defines also the background color.
567    *
568    * @return bool True if image correctly selected and set
569    */
570   private function selectBackground()
571   {
572     if($this->config['bg_img'] && is_dir($this->config['bg_img']))
573     {
574       $pointer = opendir($this->config['bg_img']);
575       while(false !== ($filename = readdir($pointer)))
576       {
577         if(eregi('.[gif|jpg|jpeg|png]$', $filename))
578         {
579           $files[] = $filename;
580         }
581       }
582       closedir($pointer);
583       $this->captcha['bg_img'] = $this->config['bg_img'].'/'.$files[array_rand($files, 1)];
584     }
585     elseif($this->config['bg_img'] && file_exists($this->config['bg_img']))
586     {
587       //use the file specified
588       $this->captcha['bg_img'] = $this->config['bg_img'];
589     }
590     else
591     {
592       $this->captcha['bg_img'] = '';
593     }
594     $this->captcha['bg_colors'] = array('red'=>$this->config['bg_red'], 'green'=>$this->config['bg_green'], 'blue'=>$this->config['bg_blue']);
595     
596     if(!empty($this->captcha['bg_colors']))
597     {
598       return true;
599     }
600     else
601     {
602       return false;
603     }
604   }
605  
606   /**
607    * Selects the ink type (transparent or normal)
608    *
609    * @return bool The ink type (true=alpha, false=opaque)
610    */
611   private function getInkType()
612   {
613     //select if transparent or "normal"
614     if(function_exists('imagecolorallocatealpha') && $this->config['char_transparent'] != 0)
615     {
616       return true;
617     }
618     else
619     {
620       return false;
621     }
622   }
623  
624   /**
625    * Selects the ink colors
626    *
627    * @return array The ink colors
628    */
629   private function getInkColors()
630   {
631     //random color or configured colors
632     if($this->config['char_random_color'])
633     {
634       $ok = false;
635       do {
636         $rand_red = rand(0,255);
637         $rand_green = rand(0,255);
638         $rand_blue = rand(0,255);
639         $random_color_sum = $rand_red + $rand_green + $rand_blue;
640         switch ($this->config['char_random_color_lvl'])
641         {
642           case : if ($random_color_sum<200) $ok=true; break; // very dark
643           case : if ($random_color_sum<400) $ok=true; break; // dark
644           case : if ($random_color_sum>500) $ok=true; break; // bright
645           case : if ($random_color_sum>650) $ok=true; break; // very bright
646           default : $ok=true;               
647         }
648       }while($ok == false);
649       
650       $ink['red']=$rand_red;
651       $ink['green']=$rand_green;
652       $ink['blue']=$rand_blue;
653     }
654     else
655     {
656       $ink['red']=$this->config['char_red'];
657       $ink['green']=$this->config['char_green'];
658       $ink['blue']=$this->config['char_blue'];
659     }
660     
661     return $ink;
662   }
663  
664   /**
665    * Defines the captcha noise parameters
666    *
667    * @return bool Always true
668    */
669   private function setNoiseParameters()
670   {
671     switch($this->config['noise_color'])
672     {
673       case : $rand_letter = rand(1,$this->captcha['chars']);
674                 $this->captcha['noise_color'] = $this->captcha['letters'][$rand_letter]['ink_colors']; break; //color of the writing
675       case : $this->captcha['noise_color'] = $this->captcha['bg_colors']; break; //color of the background
676       case :
677       default : $this->captcha['noise_color'] = array('red'=>rand(0,255),'green'=>rand(0,255),'blue'=>rand(0,255)); break; //random color
678     }
679     
680     $this->captcha['noise_px'] = rand($this->config['noise_min_px'],$this->config['noise_max_px']);
681     $this->captcha['noise_lines'] = rand($this->config['noise_min_lines'],$this->config['noise_max_lines']);
682     $this->captcha['noise_circles'] = rand($this->config['noise_min_circles'],$this->config['noise_max_circles']);
683     
684     return true;
685   }
686  
687   /**
688    * Generates the final captcha image with the background
689    *
690    * @return bool Always true
691    */
692   private function createCaptchaImageAndBackground()
693   {
694     $this->image = imagecreatetruecolor($this->config['width'],$this->config['height']);
695     //add background
696     if(!empty($this->config['bg_img']))
697     {
698       $this->addImageBackgroundImage();
699     }
700     else
701     {
702       $this->addImageBackgroundColor();
703     }
704     
705     return true;
706   }
707  
708   /**
709    * Adds the background image to the $image attribute
710    *
711    * @return bool Always true
712    */
713   private function addImageBackgroundImage()
714   {
715     list($bg_width, $bg_height, $bg_type, $bg_attributes) = getimagesize($this->captcha['bg_img']);
716     if($bg_type == '1')
717     {
718       $img_read = imagecreatefromgif($this->captcha['bg_img']);
719     }
720     elseif($bg_type == '2')
721     {
722       $img_read = imagecreatefromjpeg($this->captcha['bg_img']);
723     }
724     elseif($bg_type == '3')
725     {
726       $img_read = imagecreatefrompng($this->captcha['bg_img']);
727     }
728     else
729     {
730       return false;
731     }
732     imagecopyresized($this->image, $img_read, 0, 0, 0, 0, $this->config['width'], $this->config['height'], $bg_width, $bg_height );
733     imagedestroy($img_read);
734     
735     return true;
736   }
737  
738   /**
739    * Adds the background color to the $image attribute
740    *
741    * @return bool Always true
742    */
743   private function addImageBackgroundColor()
744   {
745     $bg = imagecolorallocate($this->image, $this->captcha['bg_colors']['red'], $this->captcha['bg_colors']['green'], $this->captcha['bg_colors']['blue']);
746     imagefill($this->image, 0, 0, $bg);
747     
748     if($this->config['bg_transparent'] && strtoupper($this->config['format'])== 'PNG')
749     {
750       imagecolortransparent($this->image, $bg);
751     }
752     
753     return true;
754   }
755  
756   /**
757    * Adds the noise to the $image attribute
758    *
759    * @return bool Always true
760    */
761   private function addNoiseToImage()
762   {
763     //add pixels
764     for($i = 1; $i < $this->captcha['noise_px']; $i++)
765     {
766       imagesetpixel($this->image, rand(0, $this->config['width']-1), rand(0, $this->config['height']-1), $this->getNoiseBrush() );
767     }
768     
769     for($j = 1; $j < $this->captcha['noise_lines']; $j++)
770     {
771       imageline($this->image, rand(0, $this->config['width']-1), rand(0, $this->config['height']-1), rand(0, $this->config['width']-1), rand(0, $this->config['height']-1), $this->getNoiseBrush());
772     }
773     
774     for($k = 1; $k < $this->captcha['noise_circles']; $k++)
775     {
776       $radius = rand(5,$this->config['width']/3);
777       imagearc($this->image, rand(0, $this->config['width']-1), rand(0, $this->config['height']-1), $radius, $radius, 0, 360, $this->getNoiseBrush());
778     }
779     
780     return true;
781   }
782  
783   /**
784    * Updates the captcha letters x_coord value
785    *
786    * @return bool Always true
787    */
788   private function updateCaptchaXCoord()
789   {
790     //for each letter, redefine the x_coord
791     $x_coord = $this->captcha['x_coord_adjust']; //starting position of the captcha on the X axis - adapted with the x offset.
792     
793     for($i=1; $i<= $this->captcha['chars']; $i++)
794     {
795       //update the letter coordinate and increase the counter
796       $this->captcha['letters'][$i]['x_coord'] = $x_coord;
797       $x_coord += $this->config['char_px_spacing'];
798     }
799     
800     return true;
801   }
802  
803   /**
804    * Adds the actual captcha letters to the $image attribute
805    *
806    * @return bool Always true
807    */
808   private function addLettersToImage()
809   {
810     //add each character to the image :D
811     for($i=1; $i<= $this->captcha['chars']; $i++)
812     {
813       //create ink
814       if($this->captcha['letters'][$i]['ink_type'])
815       {
816         //alpha active
817         $ink = imagecolorallocatealpha($this->image, $this->captcha['letters'][$i]['ink_colors']['red'],
818                                               $this->captcha['letters'][$i]['ink_colors']['green'],
819                                               $this->captcha['letters'][$i]['ink_colors']['blue'],
820                                               $this->config['char_transparent']);
821       }
822       else
823       {
824         //normal/opaque ink
825         $ink = imagecolorallocatealpha($this->image, $this->captcha['letters'][$i]['ink_colors']['red'],
826                                               $this->captcha['letters'][$i]['ink_colors']['green'],
827                                               $this->captcha['letters'][$i]['ink_colors']['blue']);
828       }
829       
830       //add character
831       imagettftext($this->image,
832                    $this->captcha['letters'][$i]['size'],
833                    $this->captcha['letters'][$i]['rotation'],
834                    $this->captcha['letters'][$i]['x_coord'],
835                    $this->captcha['letters'][$i]['y_coord'],
836                    $ink,
837                    $this->captcha['letters'][$i]['font_path'],
838                    $this->captcha['letters'][$i]['char']);
839       //char added :)
840     }
841     
842     return true;
843   }
844  
845   /**
846    * Defines the brush used on the $image attribute
847    *
848    * @return bool Always true
849    */
850   private function setBrush()
851   {
852     $noise_color = imagecolorallocate ($this->image, $this->captcha['noise_color']['red'], $this->captcha['noise_color']['green'], $this->captcha['noise_color']['blue']);
853     if($this->config['brush_size'] && $this->config['brush_size']>1 && function_exists('imagesetbrush'))
854     {
855       $brush = imagecreatetruecolor($this->config['brush_size'], $this->config['brush_size']);
856       imagefill($brush, 0, 0, $noise_color);
857       imagesetbrush($this->image, $brush);
858       $this->captcha['noise_brush'] = IMG_COLOR_BRUSHED;
859       $this->captcha['brush'] = $brush;
860     }
861     else
862     {
863       $this->captcha['noise_brush'] = $noise_color;
864     }
865     
866     return true;
867   }
868  
869   /**
870    * Deletes the brush image so it can be reused!
871    *
872    * @return bool Always returns true
873    */
874   private function clearBrush()
875   {
876     if(isset($this->captcha['brush']) && !empty($this->captcha['brush']))
877     {
878       imagedestroy($this->captcha['brush']);
879       return true;
880     }
881     else
882     {
883       return false;
884     }
885   }
886  
887   /**
888    * Refreshes the captcha noise parameters if the random option is selected
889    *
890    * @return bool If changed, returns true
891    */
892   private function refreshNoiseColor()
893   {
894     if($this->config['noise_color'] != 1 && $this->config['noise_color'] != 2)
895     {
896       $this->captcha['noise_color'] = array('red'=>rand(0,255),'green'=>rand(0,255),'blue'=>rand(0,255));
897       return true;
898     }
899     return false;
900   }
901  
902   /**
903    * Refreshes the noise and makes it into a brush for direct use
904    *
905    * @return bool Always true
906    */
907   private function getNoiseBrush()
908   {
909     //refresh the color if random type selected
910     if($this->refreshNoiseColor())
911     {
912       //brush updated, regenerate brush
913       $this->setBrush();
914     }
915     else
916     {
917       if(empty($this->captcha['noise_brush']) || !isset($this->captcha['noise_brush']))
918       {
919         $this->setBrush();
920       }
921       else
922       {
923         //no need to do anything (color not updated and brush set)
924       }
925     }
926     return $this->captcha['noise_brush'];
927   }
928  
929   /**
930    * Adds a border around the image
931    *
932    * @return bool Always true
933    */
934   private function addBorder()
935   {
936     if($this->config['bg_border'])
937     {
938       $border_color = imagecolorallocate($this->image, ($this->config['bg_red']*3+$this->config['char_red'])/4,
939                                                  ($this->config['bg_green']*3+$this->config['char_green'])/4,
940                                                  ($this->config['bg_blue']*3+$this->config['char_blue'])/4);
941       imagerectangle($this->image, 0, 0, $this->config['width']-1, $this->config['height']-1, $border_color);
942     }
943     return true;
944   }
945  
946   /**
947    * Aplies effects to the image
948    *
949    * @return bool Always true
950    */
951   private function addEffect()
952   {
953     if(function_exists('imagefilter'))
954     {
955       if($this->config['effect_greyscale'])
956       {
957         imagefilter($this->image, IMG_FILTER_GRAYSCALE);
958       }
959       if($this->config['effect_blur'])
960       {
961         imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
962       }
963     }
964     return true;
965   }
966  
967   /**
968    * Saves the captcha word into the user session
969    *
970    * @return bool Always true
971    */
972   private function setUserAttributes()
973   {
974     if(!$this->config['case_sensitive'])
975     {
976       $this->captcha['word'] = strtoupper($this->captcha['word']);
977     }
978     
979     //save the captcha into the session in hashed form
980     if(empty($this->config['hash_algo']))
981     {
982       $this->sf_user->setAttribute('captcha_code', hash('sha1',$this->captcha['word']), 'captcha');
983     }
984     else
985     {
986       $this->sf_user->setAttribute('captcha_code', hash($this->config['hash_algo'],$this->captcha['word']), 'captcha');
987     }
988     
989     return true;
990   }
991  
992   /**
993    * Sends the final image to the browser!
994    *
995    * @return bool Returns false if the format specified does not exist.
996    */
997   private function sendImageToBrowser()
998   {
999     //send the finished image in JPG, GIF or PNG format
1000     if(strtoupper($this->config['format']) == 'JPG' || strtoupper($this->config['format']) == 'JPEG')
1001     {
1002       if(imagetypes() & IMG_JPG)
1003       {
1004         header("Content-type: image/jpeg");
1005         imagejpeg($this->image, '', 80);
1006       }
1007       else
1008       {
1009         return false;
1010       }
1011     }
1012     
1013     if(strtoupper($this->config['format']) == 'GIF')
1014     {
1015       if(imagetypes() & IMG_GIF)
1016       {
1017         header("Content-type: image/gif");
1018         imagegif($this->image);
1019       }
1020       else
1021       {
1022         return false;
1023       }
1024     }
1025     
1026     if(strtoupper($this->config['format']) == 'PNG')
1027     {
1028       if(imagetypes() & IMG_PNG)
1029       {
1030         header("Content-type: image/png");
1031         imagepng($this->image);
1032       }
1033       else
1034       {
1035         return false;
1036       }
1037     }
1038     
1039     return true;
1040   }
1041  
1042   private function generateErrorImage($error_text)
1043   {
1044     $this->captcha['bg_img'] = false;
1045     $this->config['bg_red']=255;
1046     $this->config['bg_green']=255;
1047     $this->config['bg_blue']=255;
1048     $this->selectBackground();
1049     
1050     //create the image (starting by the background)
1051     $this->createCaptchaImageAndBackground();
1052     
1053     //generate error letters and add them
1054     $this->captcha['word'] = $error_text;
1055     $this->captcha['chars'] = strlen($this->captcha['word']);
1056     
1057     $x_coord = 5; //starting position of the captcha on the X axis.
1058     $new_line = false;
1059     //generate the letters
1060     for($i=1; $i<= $this->captcha['chars']; $i++)
1061     {
1062       //font
1063       $this->captcha['letters'][$i]['font'] = $this->config['error_font'];
1064       $this->captcha['letters'][$i]['font_path'] = $this->config['char_fonts_dir'].$this->captcha['letters'][$i]['font'];
1065       
1066       //ink color
1067       $this->captcha['letters'][$i]['ink_type'] = $this->getInkType();
1068       $this->captcha['letters'][$i]['ink_colors'] = array('red'=>255,'green'=>0, 'blue'=>0);
1069       
1070       //rotation
1071       $this->captcha['letters'][$i]['rotation'] = 0;
1072
1073       //character
1074       $this->captcha['letters'][$i]['char'] = $this->captcha['word']{$i-1};
1075       
1076       //size
1077       $this->captcha['letters'][$i]['size'] = '8';
1078       
1079       //X spacing:
1080       $this->config['char_px_spacing'] = 7;
1081       //make message on two lines - test if $i above half the message
1082       if($i >= round($this->captcha['chars'])/2)
1083       {
1084         if($this->captcha['letters'][$i]['char'] == ' ' && !$new_line)
1085         {
1086           //reset X coordinates
1087           $x_coord = 10;
1088           $new_line = true;
1089         }
1090         if($new_line == true)
1091         {
1092           //vertical offset of the letter
1093           $this->captcha['letters'][$i]['y_coord'] = $this->config['height']/1.3;
1094         }
1095         else
1096         {
1097           //vertical offset of the letter
1098           $this->captcha['letters'][$i]['y_coord'] = $this->config['height']/2.5;
1099         }
1100       }
1101       else
1102       {
1103         if(($i >= (round($this->captcha['chars'])/2 - 1) || $i >= (round($this->captcha['chars'])/2 - 2)) && $this->captcha['letters'][$i]['char'] == ' ' && !$new_line)
1104         {
1105           //reset X coordinates
1106           $x_coord = 5;
1107           $new_line = true;
1108         }
1109         if($new_line == true)
1110         {
1111           //vertical offset of the letter
1112           $this->captcha['letters'][$i]['y_coord'] = $this->config['height']/1.3;
1113         }
1114         else
1115         {
1116           //vertical offset of the letter
1117           $this->captcha['letters'][$i]['y_coord'] = $this->config['height']/2.5;
1118         }
1119       }
1120           
1121       //save the letter coordinate and increase the counter
1122       $this->captcha['letters'][$i]['x_coord'] = $x_coord;
1123       $x_coord += $this->config['char_px_spacing'];
1124     }
1125     $this->addLettersToImage();
1126     
1127     $this->addBorder();
1128     
1129     
1130     return $this->sendImageToBrowser();
1131   }
1132  
1133   private function sendErrorImage($error_message)
1134   {
1135     // 1) read the image depending on the sf_user culture
1136     // 2) send it to the browser
1137     
1138     //get culture
1139     $culture = $this->sf_user->getCulture();
1140       
1141     //set image path
1142     if($this->config['use_i18n'] == true)
1143     {
1144       $image_path = $this->config['error_images_dir'].DIRECTORY_SEPARATOR.$culture.DIRECTORY_SEPARATOR.$error_message.'.'.$this->config['format'];
1145     }
1146     else
1147     {
1148       $image_path = $this->config['error_images_dir'].DIRECTORY_SEPARATOR.$error_message.'.'.$this->config['format'];
1149     }
1150     //send the finished image in JPG, GIF or PNG format
1151     if(strtoupper($this->config['format']) == 'JPG' || strtoupper($this->config['format']) == 'JPEG')
1152     {
1153       header("Content-type: image/jpeg");
1154       readfile($image_path);
1155       exit;
1156     }
1157     
1158     if(strtoupper($this->config['format']) == 'GIF')
1159     {
1160       header("Content-type: image/gif");
1161       readfile($image_path);
1162       exit;
1163     }
1164     
1165     if(strtoupper($this->config['format']) == 'PNG')
1166     {
1167       header("Content-type: image/png");
1168       readfile($image_path);
1169       exit;
1170     }
1171     
1172     return true;
1173   }
1174  
1175  
1176   /**
1177    * Captcha configuration getter from the app.yml
1178    *
1179    * This function gets the configuration values
1180    * from the configuration file.
1181    *
1182    * @return bool Always true
1183    */
1184   private function getConfiguration()
1185   {
1186     $root_dir = sfConfig::get('sf_root_dir');
1187     $web_dir = sfConfig::get('sf_web_dir');
1188     //Setting image size
1189     $this->config['width'] =  sfConfig::get('app_sf_crypto_captcha_width', 130); // width of generated image
1190     $this->config['height'] = sfConfig::get('app_sf_crypto_captcha_height', 40); // height of generated image
1191     
1192     //Setting background
1193     $this->config['bg_red'] = sfConfig::get('app_sf_crypto_captcha_bg_red', 238); // quantity of ref (0->255)
1194     $this->config['bg_green'] = sfConfig::get('app_sf_crypto_captcha_bg_green', 255); // quantity of green (0->255)
1195     $this->config['bg_blue'] = sfConfig::get('app_sf_crypto_captcha_bg_blue', 255); // quantity of blue (0->255)
1196     $this->config['bg_transparent'] = sfConfig::get('app_sf_crypto_captcha_bg_transparent', false); // transparent backround, only for PNG
1197     $this->config['bg_img'] = sfConfig::get('app_sf_crypto_captcha_bg_img', false); // boolean(false) or image(path) or file (random image from file path)
1198     if($this->config['bg_img'] != false) { $this->config['bg_img'] = $root_dir.$this->config['bg_img']; } //The background image file must be a path from the symfony root dir
1199     
1200     $this->config['bg_border'] = sfConfig::get('app_sf_crypto_captcha_bg_border', true); //border or not
1201     
1202     //Setting characters
1203     $this->config['char_red'] = sfConfig::get('app_sf_crypto_captcha_char_red', 0); // quantity of ref (0->255)
1204     $this->config['char_green'] = sfConfig::get('app_sf_crypto_captcha_char_green', 0); // quantity of green (0->255)
1205     $this->config['char_blue'] = sfConfig::get('app_sf_crypto_captcha_char_blue', 0); // quantity of blue (0->255)
1206     $this->config['char_random_color'] = sfConfig::get('app_sf_crypto_captcha_char_random_color', true); // random color choice
1207     $this->config['char_random_color_lvl'] = sfConfig::get('app_sf_crypto_captcha_char_random_color_lvl', 1); // if the color is random, test it's "brightness"
1208     $this->config['char_transparent'] = sfConfig::get('app_sf_crypto_captcha_char_transparent', 10); // intensity of transparency (0->127)
1209     $this->config['char_px_spacing'] = sfConfig::get('app_sf_crypto_captcha_char_px_spacing', 20); // number of pixels between each letter
1210     $this->config['char_min_size'] = sfConfig::get('app_sf_crypto_captcha_char_min_size', 16); // minimum character size
1211     $this->config['char_max_size'] = sfConfig::get('app_sf_crypto_captcha_char_max_size', 20); // maximum character size
1212     $this->config['char_max_rot_angle'] = sfConfig::get('app_sf_crypto_captcha_char_max_rot_angle', 30); // maximum rotation angle of the characters (0->360)
1213     $this->config['char_vertical_offset'] = sfConfig::get('app_sf_crypto_captcha_char_vertical_offset', true); // random vertical offset of letters
1214     
1215     //Setting fonts
1216     $this->config['char_fonts'] = sfConfig::get('app_sf_crypto_captcha_char_fonts', array('luggerbu.ttf')); // the fonts used randomly to generate the characters
1217     $this->config['char_fonts_dir'] = sfConfig::get('app_sf_crypto_captcha_char_fonts_dir', '/plugins/sfCryptoCaptchaPlugin/media/fonts/'); // directory with the fonts
1218     $this->config['char_fonts_dir'] = $root_dir.$this->config['char_fonts_dir'];
1219     $this->config['error_font'] = sfConfig::get('app_sf_crypto_captcha_error_font', 'arial.ttf');
1220     
1221     
1222     //Setting authorized characters
1223     $this->config['chars_used'] = sfConfig::get('app_sf_crypto_captcha_chars_used', 'ABCDEFGHKLMNPRTWXYZ234569'); // characters used for the captchas
1224     
1225     //Setting easy captchas
1226     $this->config['easy_captcha'] = sfConfig::get('app_sf_crypto_captcha_easy_captcha', true); // make easy readable captachas (alternate vowels/consonant)
1227     $this->config['easy_captcha_vowels'] = sfConfig::get('app_sf_crypto_captcha_easy_captcha_vowels', 'AEIOUY'); // vowels used in the easy captchas
1228     $this->config['easy_captcha_consonants'] = sfConfig::get('app_sf_crypto_captcha_easy_captcha_consonants', 'BCDFGHKLMNPRTVWXZ'); // consonant used in the easy captchas
1229     $this->config['easy_captcha_bool'] = rand(0,1);    //create random bool for easy captcha (first letter vowel or consonant)
1230     
1231     //Setting parameters
1232     $this->config['case_sensitive'] = sfConfig::get('app_sf_crypto_captcha_case_sensitive', false); // differentiate between letters (M and m)
1233     $this->config['min_chars'] = sfConfig::get('app_sf_crypto_captcha_min_chars', 4); // minimum characters in the captcha
1234     $this->config['max_chars'] = sfConfig::get('app_sf_crypto_captcha_max_chars', 6); // maximum characters in the captcha
1235     $this->config['brush_size'] = sfConfig::get('app_sf_crypto_captcha_brush_size', 1); // noise brush size (1->25)
1236     $this->config['format'] = sfConfig::get('app_sf_crypto_captcha_format', 'png'); // image format (png, gif, jpg)
1237     $this->config['hash_algo'] = sfConfig::get('app_sf_crypto_captcha_hash_algo', 'sha1'); // hashing used
1238     $this->config['flood_timer'] = sfConfig::get('app_sf_crypto_captcha_flood_timer', 0); // time (seconds) between each refresh of image
1239     //$this->config['flood_error'] = sfConfig::get('app_sf_crypto_captcha_flood_error', 3); // what happens if flood (1=no image, 2=error image, 3=pause)
1240     $this->config['max_refresh'] = sfConfig::get('app_sf_crypto_captcha_max_refresh',1000); // maximum refreshes the user can do for one session
1241     
1242     //Setting effects
1243     $this->config['effect_blur'] = sfConfig::get('app_sf_crypto_captcha_effect_blur', false); // adds gaussian blur to the image
1244     $this->config['effect_greyscale'] = sfConfig::get('app_sf_crypto_captcha_effect_greyscale', false); // makes the captacha in grayscale (only if PHP >= 5.0.0)
1245     
1246     //Setting noise
1247     $this->config['noise_min_px'] = sfConfig::get('app_sf_crypto_captcha_noise_min_px', 200); // minimum noise pixels
1248     $this->config['noise_max_px'] = sfConfig::get('app_sf_crypto_captcha_noise_max_px', 400); // maximum noise pixels
1249     $this->config['noise_min_lines'] = sfConfig::get('app_sf_crypto_captcha_noise_min_lines', 2); // minimum noise lines
1250     $this->config['noise_max_lines'] = sfConfig::get('app_sf_crypto_captcha_noise_max_lines', 3); // maximum noise lines
1251     $this->config['noise_min_circles'] = sfConfig::get('app_sf_crypto_captcha_noise_min_circles', 2); // minimum noise circles
1252     $this->config['noise_max_circles'] = sfConfig::get('app_sf_crypto_captcha_noise_max_circles', 3); // maximum noise circles
1253     $this->config['noise_color'] = sfConfig::get('app_sf_crypto_captcha_noise_color', 3); // noise color (1= character color, 2= background, 3= random)
1254     $this->config['noise_on_top'] = sfConfig::get('app_sf_crypto_captcha_noise_on_top', false); // the noise is on the top layer
1255     
1256     //error config
1257     $this->config['use_i18n'] = sfConfig::get('sf_i18n', false);
1258     $this->config['error_images_dir'] = sfConfig::get('app_sf_crypto_captcha_error_images_dir', '/plugins/sfCryptoCaptchaPlugin/media/error/'); //the dir where the images are - from symfony root dir
1259     $this->config['error_images_dir'] = $root_dir.$this->config['error_images_dir'];
1260     
1261     return true;
1262   }
1263  
1264 }
1265
1266
Note: See TracBrowser for help on using the browser.