Projet

Général

Profil

Paste
Télécharger (9,06 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / captcha / image_captcha / image_captcha.module @ 7547bb19

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