captcha.module 10.9 KB
Newer Older
1 2 3 4 5 6
<?php

function captcha_help($section = "admin/help#captcha") {
  $output = "";

  switch ($section) {
7 8 9 10 11 12 13 14 15 16 17 18 19
  case 'admin/help#captcha':
    $output .= "<p>Adds a Captcha to the registration form.</p>";
    $output .= "<p>More help needed here.</p>";
    break;
  case 'admin/modules#description':
    $output = t("Adds a Captcha to the registration form.");
    break;
  case 'admin/modules/captcha':
    $output = t("Adds a Captcha to the registration form.");
    break;
  case 'admin/captcha':
    $output = t("Adds a Captcha to the registration form.");
    break;
20 21 22 23
  }
  return $output;
}

24 25 26 27 28 29 30 31 32 33
/**
* Implementation of hook_menu().
*/
function captcha_menu($may_cache) {
  $items = array();
  
  $suffix = '';
  if (arg(2)!=null) $suffix='/'.arg(2);
  
  $items[] = array('path' => 'captcha/image'.$suffix, 'title' => t('captcha image'),
34
    'callback' => '_captcha_image', 'access' => user_access('access captchas'),
35
    'type' => MENU_CALLBACK);
36
    
37
  return $items;
Arnab Nandi's avatar
Arnab Nandi committed
38
}
39

40 41 42 43 44 45 46 47
/**
* Implementation of hook_perm, in response to issue #19254
* This adds yet another value to configure, but it's a safer option.
*/
function captcha_perm() {
  return array('access captchas');
}

48 49
function captcha_settings() {

50
  //check for GD
51
  if (!function_exists(imagecreate))
52 53
    form_set_error('No GD', t('Image library not available. Captcha needs the GD library extension to be installed. Please install GD.'));

Arnab Nandi's avatar
Arnab Nandi committed
54
  else {
55

Arnab Nandi's avatar
Arnab Nandi committed
56
    $fonts_path = variable_get("captcha_fonts_path", "");
57

Arnab Nandi's avatar
Arnab Nandi committed
58
    //check for TTF support
59
    if (!function_exists(imagettftext))
Arnab Nandi's avatar
Arnab Nandi committed
60
      form_set_error('No TTF support', t('Your image library does not seem to have TrueType font support. Captcha will work, but will use the default inbuilt font.'));
61

Arnab Nandi's avatar
Arnab Nandi committed
62
    else {
63

Arnab Nandi's avatar
Arnab Nandi committed
64 65 66 67
      //check for valid font path
      if ($fonts_path!="" && !is_dir($fonts_path))
        form_set_error('Invalid font path', t('The current font path is invalid. The default font will be used.'));
    }
68

Arnab Nandi's avatar
Arnab Nandi committed
69
  }
70

Arnab Nandi's avatar
Arnab Nandi committed
71
  $output .= form_checkbox(t('Check during user registration'), 'captcha_user_register', 'true', _captcha_istrue("captcha_user_register", "true"), 'If enabled, users will be asked to recognize an image during user registration.');
72

Arnab Nandi's avatar
Arnab Nandi committed
73
  $output .= form_checkbox(t('Check during anonymous comments'), 'captcha_comment_anonymous', 'true', _captcha_istrue("captcha_comment_anonymous"), 'If enabled, anonymous users will be asked to recognize an image while posting .');
74

Arnab Nandi's avatar
Arnab Nandi committed
75
  $output .= form_checkbox(t('Check during registered user comments'), 'captcha_comment_registered', 'true', _captcha_istrue("captcha_comment_registered"), 'If enabled, registered users will be asked to recognize an image while posting .');
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

  $output .= form_textfield(t('TrueType Fonts Path'), 'captcha_fonts_path', $fonts_path, 30, 255, 'Location of the directory where the Truetype (.ttf) fonts are stored. If you do not provide any fonts, the module will use the default font for text.');

  if (isset($fonts_path)) {
    $imagefontinfo .= t('Number of fonts found: ').count(_captcha_font_list());
  }

  $gdinfo = gd_info();
  $imagefontinfo .= '<br />'.t('GD Version: ').$gdinfo["GD Version"];
  $imagefontinfo .= '<br />'.t(' FreeType Support: ');
  $imagefontinfo .= ($gdinfo["FreeType Support"]==true) ? 'True' : 'False';
  $imagefontinfo .= '<br />';

  $output .= form_item("Image and font information", '', $imagefontinfo);

91 92 93
  return $output;
}

94 95 96
/**
* Implementation of hook_menu(), for adding form elements & validation.
*/
Arnab Nandi's avatar
Arnab Nandi committed
97 98
function captcha_user($type, &$edit, &$newuser, $category = NULL) {

Arnab Nandi's avatar
Arnab Nandi committed
99
  global $user;
100 101

  // What type of registration action are we taking?
Arnab Nandi's avatar
Arnab Nandi committed
102 103
  // make sure this is a registration, and captcha is enabled for registration
  if (_captcha_istrue("captcha_user_register") && !$newuser->uid && !$user->uid)
104
  switch ($type) {
105
    case 'register':
106
    // Add two items to the resigtration form.
107

Arnab Nandi's avatar
Arnab Nandi committed
108
    $output .= form_item("", '<img src="'.url('captcha/image/'.time()).'" alt="Captcha Image: you will need to recognize the text in it."/>');
109
    $output .= form_textfield(t('Word'), 'captchaword', NULL, 15, 15, t('Please type in the letters/numbers that are shown in the image above.'), NULL, TRUE);
110 111 112 113

    return array(array('title' => t('Verify Registration'), 'data'=>$output));

    break;
114
    case 'validate':
115
    // The user has filled out the form and checked the "accept" box.
116
    if (strtolower($edit['captchaword']) == strtolower($_SESSION['captcha'])) {
117 118 119 120 121 122 123 124 125 126 127 128
      // on success return the values you want to store
      return array("captcha_correct" => 1);
    }
    else {
      // on error return an error message
      form_set_error('captchaword', t("Please re-recognize the word shown in the image."));
      return $edit;
    }
    break;
  }
}

Arnab Nandi's avatar
Arnab Nandi committed
129 130 131 132 133 134
/**
* Comment callback; adds captcha field to new comment form.
* NOTE: This will work ONLY if the patch at http://drupal.org/node/14708 has been applied. Let's hope the patch makes HEAD!
*/
function captcha_comment($op,$edit) {
  global $user;
135

Arnab Nandi's avatar
Arnab Nandi committed
136
  // check if captcha is enabled for form type
Arnab Nandi's avatar
Arnab Nandi committed
137 138
  if ($user->uid == 0 && !_captcha_istrue("captcha_comment_anonymous")) return;
  if ($user->uid != 0 && !_captcha_istrue("captcha_comment_registered")) return;
Arnab Nandi's avatar
Arnab Nandi committed
139 140 141

  switch ($op) {
    case 'validate':
Arnab Nandi's avatar
Arnab Nandi committed
142 143 144
      // only validate captcha once for a comment.
      // this implementation basically sets a flag when you've successfully validated a captcha;
      // any successive comment inserted uses and invalidates the set flag.
145 146 147
      if ($_SESSION['captcha_comment_correct']!='ok') {
        if (strtolower($edit['captchaword']) != '' && strtolower($edit['captchaword']) == strtolower($_SESSION['captcha'])) {
          $_SESSION['captcha_comment_correct'] = 'ok';
148 149
          //reset captcha variable to prevent session highjacking vulnerability #26741	  
          $_SESSION['captcha']='';
Arnab Nandi's avatar
Arnab Nandi committed
150 151 152 153 154 155 156 157
        }
        else {
          form_set_error('captcha', t('The user verification code you entered is not correct.'));
        }
      }
      break;
    case 'insert':
      //invalidate captcha after one comment insert
158
      $_SESSION['captcha_comment_correct'] = '';
Arnab Nandi's avatar
Arnab Nandi committed
159
      break;
160

Arnab Nandi's avatar
Arnab Nandi committed
161
    case 'form':
162 163
      $form_html = "";
      if ($_SESSION['captcha_comment_correct']!='ok') {
Arnab Nandi's avatar
Arnab Nandi committed
164 165
        $output .= form_item("", '<img src="'.url('captcha/image/'.time()).'" alt="Captcha Image: you will need to recognize the text in it."/>');
        $output .= form_textfield(t('Word'), 'captchaword', NULL, 15, 15, 'Please type in the letters/numbers that are shown in the image above.', NULL, TRUE);
166 167 168
        $form_html = form_group(t('Verify comment authorship'), $output);
      }
      return $form_html;
Arnab Nandi's avatar
Arnab Nandi committed
169
  }
170
}
171 172 173 174 175

/**
* Returns a random string for use in a captcha
*/
function _captcha_code() {
176

177 178 179 180 181 182 183 184
    $consts='bcdgjxvmnprst';
    $vowels='aeiou';

    for ($x=0; $x < 6; $x++) {
      mt_srand ((double) microtime() * 1000000);
      $const[$x] = substr($consts,mt_rand(0,strlen($consts)-1),1);
      $vow[$x] = substr($vowels,mt_rand(0,strlen($vowels)-1),1);
    }
185
    
186 187
    $string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4];
    $string = substr($string,0,rand(5,8));
188 189
    
    //everytime we create a new code, we write it to session
190
    $_SESSION['captcha'] = $string;
191 192
    
    return $string;
193

194
}
195

Arnab Nandi's avatar
Arnab Nandi committed
196 197 198 199 200 201 202 203 204 205 206 207
/**
* Returns a random string for use in a captcha
*/
function _captcha_istrue($variable, $default="true") {
    return (variable_get($variable, "")=="true");
}

/**
* Returns an array of files with TTF extensions in the specified directory.
*/
function _captcha_font_list() {
  $fontdir = variable_get("captcha_fonts_path", "");
208

Arnab Nandi's avatar
Arnab Nandi committed
209 210 211 212 213 214 215 216 217 218
  $filelist = array();
  if ($handle = opendir($fontdir)) {
    while ($file = readdir($handle)) {
      if (preg_match("/\.ttf$/i",$file) == 1)
        $filelist[] = $fontdir.'/'.$file;
    }
    closedir($handle);
  }

  return $filelist;
219
}
Arnab Nandi's avatar
Arnab Nandi committed
220

221 222 223 224
/**
* Prints an image containing a captcha code.
*/
function _captcha_image() {
225 226 227 228 229

    // there are a few hard coded functions I'd like to eliminate here,
    // but for the time being we'll let them be.

    //if we don't have GD2 functions, we can't generate the image
Arnab Nandi's avatar
Arnab Nandi committed
230
    if (!function_exists('imagecreatetruecolor')) return;
231

232 233 234 235 236 237 238 239 240
    // Set headers
    header('Expires: Mon, 01 Jan 1997 05:00:00 GMT');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Cache-Control: post-check=0, pre-check=0', false);
    header('Pragma: no-cache');
    
    header('Content-type: image/png');

    $string = _captcha_code();
241 242 243 244 245

    // set up image, the first number is the width and the second is the height
    $im = imagecreatetruecolor(180, 80);

    // creates two variables to store color
246 247 248
    $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
    $foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80));

249
    // fill image with bgcolor
250 251
    imagefill($im, 0, 0, $background);

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    // Get truetype font list
    $fonts = _captcha_font_list();

    // writes string
    if (function_exists(imagettftext) && count($fonts) > 0) {

      // write text using a truetype font
      $charSize = 24;        // font size
      $charWidth = 0;        // width of previous character
      $x = 10;               // initial x position
      $y = 30;

      // iterate over characters
      for ($i=0;$i<strlen($string);$i++) {
        // define angle and position of character based on previous character dimension
        $x += ($charWidth * rand(1.0, 1.6));
        $y += rand(-5,5);
        $charAngle = rand(-5,5);
        $charSize += rand(-2,2);
        $char = substr($string,$i,1);

        // select random font
        $font = $fonts[rand(0,count($fonts)-1)];

        // draw character
        imagettftext($im,$charSize,$charAngle,$x,$y,$foreground,$font,$char);

        // capture character dimensions to increment x position
        $bbox = imagettfbbox($charSize,$charAngle,$font,$char);
        $charWidth = max($bbox[0],$bbox[2],$bbox[4],$bbox[6]) - min($bbox[0],$bbox[2],$bbox[4],$bbox[6]);
Arnab Nandi's avatar
Arnab Nandi committed
282 283
      }
    }
284

Arnab Nandi's avatar
Arnab Nandi committed
285
    else {
286 287 288 289 290 291 292 293 294 295
      // write text using a built-in font
      $x = 10;
      $y = 0;

      for ($i=0;$i<strlen($string);$i++) {
        imagestring($im,5,$x,$y,substr($string,$i,1),$foreground);
        $x += rand(10,15);
        $y += rand(-4,4);
      }

Arnab Nandi's avatar
Arnab Nandi committed
296
    }
297

298
    // strikethrough
299 300
    imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground);

301
    // rotate only if function is defined (many PHP installations have this function missing)
302 303 304 305 306
    if (function_exists('imagerotate')) {
      $im2 = imagerotate($im,rand(-20,45),$background);
      imagedestroy($im);
      $im = $im2;
    }
307 308 309 310 311 312 313 314 315 316
    
    // add cloud only if function is defined (many PHP installations have this function missing)
    if (function_exists('imagecolorallocatealpha')) {
      $middleground = imagecolorallocatealpha($im, rand(160, 200), rand(160, 200), rand(160, 200), 80);

      // random shapes
      for ($x=0; $x<50;$x++) {
        imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $middleground);
        imageellipse($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $middleground);
      }
317
    }
318
    
319
    //output to browser
320 321
    imagepng($im);
    imagedestroy($im);
322

Arnab Nandi's avatar
Arnab Nandi committed
323 324
}

325
?>