Initial Kohana install
[speedfreak] / Server / system / libraries / drivers / Captcha.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * Captcha driver class.
4  *
5  * $Id: Captcha.php 3769 2008-12-15 00:48:56Z zombor $
6  *
7  * @package    Captcha
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 abstract class Captcha_Driver {
13
14         // The correct Captcha challenge answer
15         protected $response;
16
17         // Image resource identifier and type ("png", "gif" or "jpeg")
18         protected $image;
19         protected $image_type = 'png';
20
21         /**
22          * Constructs a new challenge.
23          *
24          * @return  void
25          */
26         public function __construct()
27         {
28                 // Generate a new challenge
29                 $this->response = $this->generate_challenge();
30
31                 // Store the correct Captcha response in a session
32                 Event::add('system.post_controller', array($this, 'update_response_session'));
33         }
34
35         /**
36          * Generate a new Captcha challenge.
37          *
38          * @return  string  the challenge answer
39          */
40         abstract public function generate_challenge();
41
42         /**
43          * Output the Captcha challenge.
44          *
45          * @param   boolean  html output
46          * @return  mixed    the rendered Captcha (e.g. an image, riddle, etc.)
47          */
48         abstract public function render($html);
49
50         /**
51          * Stores the response for the current Captcha challenge in a session so it is available
52          * on the next page load for Captcha::valid(). This method is called after controller
53          * execution (in the system.post_controller event) in order not to overwrite itself too soon.
54          *
55          * @return  void
56          */
57         public function update_response_session()
58         {
59                 Session::instance()->set('captcha_response', sha1(strtoupper($this->response)));
60         }
61
62         /**
63          * Validates a Captcha response from a user.
64          *
65          * @param   string   captcha response
66          * @return  boolean
67          */
68         public function valid($response)
69         {
70                 return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response'));
71         }
72
73         /**
74          * Returns the image type.
75          *
76          * @param   string        filename
77          * @return  string|FALSE  image type ("png", "gif" or "jpeg")
78          */
79         public function image_type($filename)
80         {
81                 switch (strtolower(substr(strrchr($filename, '.'), 1)))
82                 {
83                         case 'png':
84                                 return 'png';
85
86                         case 'gif':
87                                 return 'gif';
88
89                         case 'jpg':
90                         case 'jpeg':
91                                 // Return "jpeg" and not "jpg" because of the GD2 function names
92                                 return 'jpeg';
93
94                         default:
95                                 return FALSE;
96                 }
97         }
98
99         /**
100          * Creates an image resource with the dimensions specified in config.
101          * If a background image is supplied, the image dimensions are used.
102          *
103          * @throws  Kohana_Exception  if no GD2 support
104          * @param   string  path to the background image file
105          * @return  void
106          */
107         public function image_create($background = NULL)
108         {
109                 // Check for GD2 support
110                 if ( ! function_exists('imagegd2'))
111                         throw new Kohana_Exception('captcha.requires_GD2');
112
113                 // Create a new image (black)
114                 $this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']);
115
116                 // Use a background image
117                 if ( ! empty($background))
118                 {
119                         // Create the image using the right function for the filetype
120                         $function = 'imagecreatefrom'.$this->image_type($background);
121                         $this->background_image = $function($background);
122
123                         // Resize the image if needed
124                         if (imagesx($this->background_image) !== Captcha::$config['width']
125                             OR imagesy($this->background_image) !== Captcha::$config['height'])
126                         {
127                                 imagecopyresampled
128                                 (
129                                         $this->image, $this->background_image, 0, 0, 0, 0,
130                                         Captcha::$config['width'], Captcha::$config['height'],
131                                         imagesx($this->background_image), imagesy($this->background_image)
132                                 );
133                         }
134
135                         // Free up resources
136                         imagedestroy($this->background_image);
137                 }
138         }
139
140         /**
141          * Fills the background with a gradient.
142          *
143          * @param   resource  gd image color identifier for start color
144          * @param   resource  gd image color identifier for end color
145          * @param   string    direction: 'horizontal' or 'vertical', 'random' by default
146          * @return  void
147          */
148         public function image_gradient($color1, $color2, $direction = NULL)
149         {
150                 $directions = array('horizontal', 'vertical');
151
152                 // Pick a random direction if needed
153                 if ( ! in_array($direction, $directions))
154                 {
155                         $direction = $directions[array_rand($directions)];
156
157                         // Switch colors
158                         if (mt_rand(0, 1) === 1)
159                         {
160                                 $temp = $color1;
161                                 $color1 = $color2;
162                                 $color2 = $temp;
163                         }
164                 }
165
166                 // Extract RGB values
167                 $color1 = imagecolorsforindex($this->image, $color1);
168                 $color2 = imagecolorsforindex($this->image, $color2);
169
170                 // Preparations for the gradient loop
171                 $steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height'];
172
173                 $r1 = ($color1['red'] - $color2['red']) / $steps;
174                 $g1 = ($color1['green'] - $color2['green']) / $steps;
175                 $b1 = ($color1['blue'] - $color2['blue']) / $steps;
176
177                 if ($direction === 'horizontal')
178                 {
179                         $x1 =& $i;
180                         $y1 = 0;
181                         $x2 =& $i;
182                         $y2 = Captcha::$config['height'];
183                 }
184                 else
185                 {
186                         $x1 = 0;
187                         $y1 =& $i;
188                         $x2 = Captcha::$config['width'];
189                         $y2 =& $i;
190                 }
191
192                 // Execute the gradient loop
193                 for ($i = 0; $i <= $steps; $i++)
194                 {
195                         $r2 = $color1['red'] - floor($i * $r1);
196                         $g2 = $color1['green'] - floor($i * $g1);
197                         $b2 = $color1['blue'] - floor($i * $b1);
198                         $color = imagecolorallocate($this->image, $r2, $g2, $b2);
199
200                         imageline($this->image, $x1, $y1, $x2, $y2, $color);
201                 }
202         }
203
204         /**
205          * Returns the img html element or outputs the image to the browser.
206          *
207          * @param   boolean  html output
208          * @return  mixed    html string or void
209          */
210         public function image_render($html)
211         {
212                 // Output html element
213                 if ($html)
214                         return '<img alt="Captcha" src="'.url::site('captcha/'.Captcha::$config['group']).'" width="'.Captcha::$config['width'].'" height="'.Captcha::$config['height'].'" />';
215
216                 // Send the correct HTTP header
217                 header('Content-Type: image/'.$this->image_type);
218
219                 // Pick the correct output function
220                 $function = 'image'.$this->image_type;
221                 $function($this->image);
222
223                 // Free up resources
224                 imagedestroy($this->image);
225         }
226
227 } // End Captcha Driver