|
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
|
|
|
|
|
15
|
|
|
// The correct Captcha challenge answer |
|
16
|
|
|
protected $response; |
|
17
|
|
|
|
|
18
|
|
|
// Image resource identifier and type ("png", "gif" or "jpeg") |
|
19
|
|
|
protected $image; |
|
20
|
|
|
protected $image_type = 'png'; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* Constructs a new challenge. |
|
24
|
|
|
* |
|
25
|
|
|
* @return void |
|
|
|
|
|
|
26
|
|
|
*/ |
|
27
|
|
|
public function __construct() |
|
28
|
|
|
{ |
|
29
|
|
|
// Generate a new challenge |
|
30
|
|
|
$this->response = $this->generate_challenge(); |
|
31
|
|
|
|
|
32
|
|
|
// Store the correct Captcha response in a session |
|
33
|
|
|
Event::add('system.post_controller', array($this, 'update_response_session')); |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* Generate a new Captcha challenge. |
|
38
|
|
|
* |
|
39
|
|
|
* @return string the challenge answer |
|
40
|
|
|
*/ |
|
41
|
|
|
abstract public function generate_challenge(); |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* Output the Captcha challenge. |
|
45
|
|
|
* |
|
46
|
|
|
* @param boolean html output |
|
47
|
|
|
* @return mixed the rendered Captcha (e.g. an image, riddle, etc.) |
|
48
|
|
|
*/ |
|
49
|
|
|
abstract public function render($html); |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Stores the response for the current Captcha challenge in a session so it is available |
|
53
|
|
|
* on the next page load for Captcha::valid(). This method is called after controller |
|
54
|
|
|
* execution (in the system.post_controller event) in order not to overwrite itself too soon. |
|
55
|
|
|
* |
|
56
|
|
|
* @return void |
|
57
|
|
|
*/ |
|
58
|
|
|
public function update_response_session() |
|
59
|
|
|
{ |
|
60
|
|
|
Session::instance()->set('captcha_response', sha1(strtoupper($this->response))); |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* Validates a Captcha response from a user. |
|
65
|
|
|
* |
|
66
|
|
|
* @param string captcha response |
|
67
|
|
|
* @return boolean |
|
68
|
|
|
*/ |
|
69
|
|
|
public function valid($response) |
|
70
|
|
|
{ |
|
71
|
|
|
return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response')); |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* Returns the image type. |
|
76
|
|
|
* |
|
77
|
|
|
* @param string filename |
|
78
|
|
|
* @return string|FALSE image type ("png", "gif" or "jpeg") |
|
79
|
|
|
*/ |
|
80
|
|
|
public function image_type($filename) |
|
81
|
|
|
{ |
|
82
|
|
|
switch (strtolower(substr(strrchr($filename, '.'), 1))) { |
|
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
|
|
|
|
|
114
|
|
|
// Create a new image (black) |
|
115
|
|
|
$this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']); |
|
116
|
|
|
|
|
117
|
|
|
// Use a background image |
|
118
|
|
|
if (! empty($background)) { |
|
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
|
|
|
imagecopyresampled( |
|
127
|
|
|
$this->image, $this->background_image, 0, 0, 0, 0, |
|
128
|
|
|
Captcha::$config['width'], Captcha::$config['height'], |
|
129
|
|
|
imagesx($this->background_image), imagesy($this->background_image) |
|
130
|
|
|
); |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
// Free up resources |
|
134
|
|
|
imagedestroy($this->background_image); |
|
135
|
|
|
} |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* Fills the background with a gradient. |
|
140
|
|
|
* |
|
141
|
|
|
* @param resource gd image color identifier for start color |
|
142
|
|
|
* @param resource gd image color identifier for end color |
|
143
|
|
|
* @param string direction: 'horizontal' or 'vertical', 'random' by default |
|
144
|
|
|
* @param integer $color1 |
|
145
|
|
|
* @param integer $color2 |
|
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
|
|
|
$direction = $directions[array_rand($directions)]; |
|
155
|
|
|
|
|
156
|
|
|
// Switch colors |
|
157
|
|
|
if (mt_rand(0, 1) === 1) { |
|
158
|
|
|
$temp = $color1; |
|
159
|
|
|
$color1 = $color2; |
|
160
|
|
|
$color2 = $temp; |
|
161
|
|
|
} |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
// Extract RGB values |
|
165
|
|
|
$color1 = imagecolorsforindex($this->image, $color1); |
|
166
|
|
|
$color2 = imagecolorsforindex($this->image, $color2); |
|
167
|
|
|
|
|
168
|
|
|
// Preparations for the gradient loop |
|
169
|
|
|
$steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height']; |
|
170
|
|
|
|
|
171
|
|
|
$r1 = ($color1['red'] - $color2['red']) / $steps; |
|
|
|
|
|
|
172
|
|
|
$g1 = ($color1['green'] - $color2['green']) / $steps; |
|
|
|
|
|
|
173
|
|
|
$b1 = ($color1['blue'] - $color2['blue']) / $steps; |
|
|
|
|
|
|
174
|
|
|
|
|
175
|
|
|
if ($direction === 'horizontal') { |
|
176
|
|
|
$x1 =& $i; |
|
|
|
|
|
|
177
|
|
|
$y1 = 0; |
|
|
|
|
|
|
178
|
|
|
$x2 =& $i; |
|
|
|
|
|
|
179
|
|
|
$y2 = Captcha::$config['height']; |
|
|
|
|
|
|
180
|
|
|
} else { |
|
181
|
|
|
$x1 = 0; |
|
182
|
|
|
$y1 =& $i; |
|
|
|
|
|
|
183
|
|
|
$x2 = Captcha::$config['width']; |
|
184
|
|
|
$y2 =& $i; |
|
|
|
|
|
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
// Execute the gradient loop |
|
188
|
|
|
for ($i = 0; $i <= $steps; $i++) { |
|
189
|
|
|
$r2 = $color1['red'] - floor($i * $r1); |
|
|
|
|
|
|
190
|
|
|
$g2 = $color1['green'] - floor($i * $g1); |
|
|
|
|
|
|
191
|
|
|
$b2 = $color1['blue'] - floor($i * $b1); |
|
|
|
|
|
|
192
|
|
|
$color = imagecolorallocate($this->image, $r2, $g2, $b2); |
|
193
|
|
|
|
|
194
|
|
|
imageline($this->image, $x1, $y1, $x2, $y2, $color); |
|
195
|
|
|
} |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
/** |
|
199
|
|
|
* Returns the img html element or outputs the image to the browser. |
|
200
|
|
|
* |
|
201
|
|
|
* @param boolean html output |
|
202
|
|
|
* @return string|null html string or void |
|
203
|
|
|
*/ |
|
204
|
|
|
public function image_render($html) |
|
205
|
|
|
{ |
|
206
|
|
|
// Output html element |
|
207
|
|
|
if ($html) { |
|
208
|
|
|
return '<img alt="Captcha" src="'.url::site('captcha/'.Captcha::$config['group']).'" width="'.Captcha::$config['width'].'" height="'.Captcha::$config['height'].'" />'; |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
// Send the correct HTTP header |
|
212
|
|
|
header('Content-Type: image/'.$this->image_type); |
|
213
|
|
|
|
|
214
|
|
|
// Pick the correct output function |
|
215
|
|
|
$function = 'image'.$this->image_type; |
|
216
|
|
|
$function($this->image); |
|
217
|
|
|
|
|
218
|
|
|
// Free up resources |
|
219
|
|
|
imagedestroy($this->image); |
|
220
|
|
|
} |
|
221
|
|
|
} // End Captcha Driver |
|
222
|
|
|
|
Adding a
@returnannotation to a constructor is not recommended, since a constructor does not have a meaningful return value.Please refer to the PHP core documentation on constructors.