comparison sites/all/modules/captcha/image_captcha/image_captcha.module @ 2:b74b41bb73f0

-- Google analytics module
author danieleb <danielebarchiesi@me.com>
date Thu, 22 Aug 2013 17:22:54 +0100
parents
children
comparison
equal deleted inserted replaced
1:67ce89da90df 2:b74b41bb73f0
1 <?php
2
3 /**
4 * @file
5 * Implements image CAPTCHA for use with the CAPTCHA module
6 */
7
8 define('IMAGE_CAPTCHA_ALLOWED_CHARACTERS', 'aAbBCdEeFfGHhijKLMmNPQRrSTtWXYZ23456789');
9
10 // Setup status flags.
11 define('IMAGE_CAPTCHA_ERROR_NO_GDLIB', 1);
12 define('IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT', 2);
13 define('IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM', 4);
14
15 define('IMAGE_CAPTCHA_FILE_FORMAT_JPG', 1);
16 define('IMAGE_CAPTCHA_FILE_FORMAT_PNG', 2);
17 define('IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG', 3);
18
19
20 /**
21 * Implements hook_help().
22 */
23 function image_captcha_help($path, $arg) {
24 switch ($path) {
25 case 'admin/config/people/captcha/image_captcha':
26 $output = '<p>' . t('The image CAPTCHA is a popular challenge where a random textual code is obfuscated in an image. The image is generated on the fly for each request, which is rather CPU intensive for the server. Be careful with the size and computation related settings.') . '</p>';
27 return $output;
28 }
29 }
30
31 /**
32 * Implements hook_menu().
33 */
34 function image_captcha_menu() {
35 $items = array();
36 // add an administration tab for image_captcha
37 $items['admin/config/people/captcha/image_captcha'] = array(
38 'title' => 'Image CAPTCHA',
39 'file' => 'image_captcha.admin.inc',
40 'page callback' => 'drupal_get_form',
41 'page arguments' => array('image_captcha_settings_form'),
42 'access arguments' => array('administer CAPTCHA settings'),
43 'type' => MENU_LOCAL_TASK,
44 );
45 // Menu path for generating font example.
46 $items['admin/config/people/captcha/image_captcha/font_preview'] = array(
47 'title' => 'Font example',
48 'file' => 'image_captcha.admin.inc',
49 'page callback' => 'image_captcha_font_preview',
50 'access arguments' => array('administer CAPTCHA settings'),
51 'type' => MENU_CALLBACK,
52 );
53 // callback for generating an image
54 $items['image_captcha'] = array(
55 'file' => 'image_captcha.user.inc',
56 'page callback' => 'image_captcha_image',
57 'access callback' => TRUE,
58 'type' => MENU_CALLBACK,
59 );
60 return $items;
61 }
62
63 /**
64 * Helper function for getting the fonts to use in the image CAPTCHA.
65 *
66 * @return a list of font paths.
67 */
68 function _image_captcha_get_enabled_fonts() {
69 if (IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT & _image_captcha_check_setup(FALSE)) {
70 return array('BUILTIN');
71 }
72 else {
73 $default = array(
74 drupal_get_path('module', 'image_captcha') . '/fonts/Tesox/tesox.ttf',
75 drupal_get_path('module', 'image_captcha') . '/fonts/Tuffy/Tuffy.ttf',
76 );
77 return variable_get('image_captcha_fonts', $default);
78 }
79 }
80
81 /**
82 * Helper function for checking if the specified fonts are available.
83 *
84 * @param $fonts paths of fonts to check.
85 * @return list($readable_fonts, $problem_fonts)
86 */
87 function _image_captcha_check_fonts($fonts) {
88 $readable_fonts = array();
89 $problem_fonts = array();
90 foreach ($fonts as $font) {
91 if ($font != 'BUILTIN' && (!is_file($font) || !is_readable($font))) {
92 $problem_fonts[] = $font;
93 }
94 else {
95 $readable_fonts[] = $font;
96 }
97 }
98 return array($readable_fonts, $problem_fonts);
99 }
100
101 /**
102 * Helper function for splitting an utf8 string correctly in characters.
103 * Assumes the given utf8 string is well formed.
104 * See http://en.wikipedia.org/wiki/Utf8 for more info
105 */
106 function _image_captcha_utf8_split($str) {
107 $characters = array();
108 $len = strlen($str);
109 for ($i=0; $i < $len; ) {
110 $chr = ord($str[$i]);
111 if (($chr & 0x80) == 0x00) { // one byte character (0zzzzzzz)
112 $width = 1;
113 }
114 else {
115 if (($chr & 0xE0) == 0xC0) { // two byte character (first byte: 110yyyyy)
116 $width = 2;
117 }
118 elseif (($chr & 0xF0) == 0xE0) { // three byte character (first byte: 1110xxxx)
119 $width = 3;
120 }
121 elseif (($chr & 0xF8) == 0xF0) { // four byte character (first byte: 11110www)
122 $width = 4;
123 }
124 else {
125 watchdog('CAPTCHA', 'Encountered an illegal byte while splitting an utf8 string in characters.', array(), WATCHDOG_ERROR);
126 return $characters;
127 }
128 }
129 $characters[] = substr($str, $i, $width);
130 $i += $width;
131 }
132 return $characters;
133 }
134
135 /**
136 * Helper function for checking the setup of the Image CAPTCHA.
137 *
138 * The image CAPTCHA requires at least the GD PHP library.
139 * Support for TTF is recommended and the enabled
140 * font files should be readable.
141 * This functions checks these things.
142 *
143 * @param $check_fonts whether or not the enabled fonts should be checked.
144 *
145 * @return status code: bitwise 'OR' of status flags like
146 * IMAGE_CAPTCHA_ERROR_NO_GDLIB, IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT,
147 * IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM.
148 */
149 function _image_captcha_check_setup($check_fonts=TRUE) {
150 // Start clean.
151 $status = 0;
152 // Check if we can use the GD library.
153 // We need at least the imagepng function (for font previews on the settings page).
154 // Note that the imagejpg function is optionally also used, but not required.
155 if (!function_exists('imagepng')) {
156 $status = $status | IMAGE_CAPTCHA_ERROR_NO_GDLIB;
157 }
158 if (!function_exists('imagettftext')) {
159 $status = $status | IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT;
160 }
161 if ($check_fonts) {
162 // Check availability of enabled fonts.
163 $fonts = _image_captcha_get_enabled_fonts();
164 list($readable_fonts, $problem_fonts) = _image_captcha_check_fonts($fonts);
165 if (count($problem_fonts) != 0) {
166 $status = $status | IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM;
167 }
168 }
169 return $status;
170 }
171
172 /**
173 * Helper function for calculating image height and width
174 * based on given code and current font/spacing settings.
175 *
176 * @return array($width, $heigh)
177 */
178 function _image_captcha_image_size($code) {
179 // Get settings
180 $font_size = (int) variable_get('image_captcha_font_size', 30);
181 $character_spacing = (float) variable_get('image_captcha_character_spacing', '1.2');
182 $characters = _image_captcha_utf8_split($code);
183 $character_quantity = count($characters);
184
185 // Calculate height and width
186 $width = $character_spacing * $font_size * $character_quantity;
187 $height = 2 * $font_size;
188
189 return array($width, $height);
190 }
191
192
193 /**
194 * Implements hook_captcha().
195 */
196 function image_captcha_captcha($op, $captcha_type='', $captcha_sid=NULL) {
197 switch ($op) {
198 case 'list':
199 // Only offer the image CAPTCHA if it is possible to generate an image on this setup.
200 if (!(_image_captcha_check_setup() & IMAGE_CAPTCHA_ERROR_NO_GDLIB)) {
201 return array('Image');
202 }
203 else {
204 return array();
205 }
206 break;
207
208 case 'generate':
209 if ($captcha_type == 'Image') {
210 // In maintenance mode, the image CAPTCHA does not work because the request
211 // for the image itself won't succeed (only ?q=user is permitted for
212 // unauthenticated users). We fall back to the Math CAPTCHA in that case.
213 global $user;
214 if (variable_get('maintenance_mode', 0) && $user->uid == 0) {
215 return captcha_captcha('generate', 'Math');
216 }
217 // generate a CAPTCHA code
218 $allowed_chars = _image_captcha_utf8_split(variable_get('image_captcha_image_allowed_chars', IMAGE_CAPTCHA_ALLOWED_CHARACTERS));
219 $code_length = (int)variable_get('image_captcha_code_length', 5);
220 $code = '';
221 for ($i = 0; $i < $code_length; $i++) {
222 $code .= $allowed_chars[array_rand($allowed_chars)];
223 }
224
225 // build the result to return
226 $result = array();
227
228 $result['solution'] = $code;
229 // Generate image source URL (add timestamp to avoid problems with
230 // client side caching: subsequent images of the same CAPTCHA session
231 // have the same URL, but should display a different code).
232 $options = array(
233 'query' => array(
234 'sid' => $captcha_sid,
235 'ts' => REQUEST_TIME,
236 ),
237 );
238 $img_src = check_url(url("image_captcha", $options));
239 list($width, $height) = _image_captcha_image_size($code);
240 // TODO: start using a theming funtion for generating the image markup?
241 $result['form']['captcha_image'] = array(
242 '#type' => 'markup',
243 '#markup' => '<img src="' . $img_src
244 . '" width="'. $width . '" height="' . $height
245 . '" alt="' . t('Image CAPTCHA') . '" title="' . t('Image CAPTCHA') . '" />',
246 '#weight' => -2,
247 );
248 $result['form']['captcha_response'] = array(
249 '#type' => 'textfield',
250 '#title' => t('What code is in the image?'),
251 '#description' => t('Enter the characters shown in the image.'),
252 '#weight' => 0,
253 '#required' => TRUE,
254 '#size' => 15,
255 );
256
257 // Handle the case insensitive validation option combined with ignoring spaces.
258 switch (variable_get('captcha_default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE)) {
259 case CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE:
260 $result['captcha_validate'] = 'captcha_validate_ignore_spaces';
261 break;
262 case CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE:
263 $result['captcha_validate'] = 'captcha_validate_case_insensitive_ignore_spaces';
264 break;
265 }
266
267 return $result;
268 }
269 break;
270
271 }
272 }