Projet

Général

Profil

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

root / drupal7 / sites / all / modules / captcha / image_captcha / image_captcha.module @ 66b5cbf6

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
}