captcha.module 10.6 KB
Newer Older
Arnab Nandi's avatar
 
Arnab Nandi committed
1 2 3 4 5 6 7
<?php
// $Id$

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

  switch ($section) {
Arnab Nandi's avatar
 
Arnab Nandi committed
8 9 10 11 12 13 14 15 16 17 18 19 20
  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;
Arnab Nandi's avatar
 
Arnab Nandi committed
21 22 23 24
  }
  return $output;
}

25 26 27 28 29 30 31 32 33 34 35 36 37 38
/**
* 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'),
    'callback' => '_captcha_image', 'access' => user_access('access content'),
    'type' => MENU_CALLBACK);
  
  return $items;
Arnab Nandi's avatar
Arnab Nandi committed
39
}
Arnab Nandi's avatar
 
Arnab Nandi committed
40 41 42

function captcha_settings() {

43
  //check for GD
44
  if (!function_exists(imagecreate))
45 46
    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
47
  else {
48

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

Arnab Nandi's avatar
Arnab Nandi committed
51
    //check for TTF support
52
    if (!function_exists(imagettftext))
Arnab Nandi's avatar
Arnab Nandi committed
53
      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.'));
54

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

Arnab Nandi's avatar
Arnab Nandi committed
57 58 59 60
      //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.'));
    }
61

Arnab Nandi's avatar
Arnab Nandi committed
62
  }
63

Arnab Nandi's avatar
Arnab Nandi committed
64
  $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.');
65

Arnab Nandi's avatar
Arnab Nandi committed
66
  $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 .');
67

Arnab Nandi's avatar
Arnab Nandi committed
68
  $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 .');
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

  $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);

Arnab Nandi's avatar
 
Arnab Nandi committed
84 85 86
  return $output;
}

87 88 89
/**
* Implementation of hook_menu(), for adding form elements & validation.
*/
Arnab Nandi's avatar
Arnab Nandi committed
90 91
function captcha_user($type, &$edit, &$newuser, $category = NULL) {

Arnab Nandi's avatar
Arnab Nandi committed
92
  global $user;
Arnab Nandi's avatar
 
Arnab Nandi committed
93 94

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

Arnab Nandi's avatar
Arnab Nandi committed
101
    $output .= form_item("", '<img src="'.url('captcha/image/'.time()).'" alt="Captcha Image: you will need to recognize the text in it."/>');
102
    $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);
103 104 105 106

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

    break;
107
    case 'validate':
108
    // The user has filled out the form and checked the "accept" box.
109
    if (strtolower($edit['captchaword']) == strtolower($_SESSION['captcha'])) {
110 111 112 113 114 115 116 117 118 119 120 121
      // 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
122 123 124 125 126 127
/**
* 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;
128

Arnab Nandi's avatar
Arnab Nandi committed
129
  // check if captcha is enabled for form type
Arnab Nandi's avatar
Arnab Nandi committed
130 131
  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
132 133 134

  switch ($op) {
    case 'validate':
Arnab Nandi's avatar
Arnab Nandi committed
135 136 137
      // 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.
138 139 140
      if ($_SESSION['captcha_comment_correct']!='ok') {
        if (strtolower($edit['captchaword']) != '' && strtolower($edit['captchaword']) == strtolower($_SESSION['captcha'])) {
          $_SESSION['captcha_comment_correct'] = 'ok';
Arnab Nandi's avatar
Arnab Nandi committed
141 142 143 144 145 146 147 148
        }
        else {
          form_set_error('captcha', t('The user verification code you entered is not correct.'));
        }
      }
      break;
    case 'insert':
      //invalidate captcha after one comment insert
149
      $_SESSION['captcha_comment_correct'] = '';
Arnab Nandi's avatar
Arnab Nandi committed
150
      break;
151

Arnab Nandi's avatar
Arnab Nandi committed
152
    case 'form':
153 154
      $form_html = "";
      if ($_SESSION['captcha_comment_correct']!='ok') {
Arnab Nandi's avatar
Arnab Nandi committed
155 156
        $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);
157 158 159
        $form_html = form_group(t('Verify comment authorship'), $output);
      }
      return $form_html;
Arnab Nandi's avatar
Arnab Nandi committed
160
  }
161
}
162 163 164 165 166

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

Arnab Nandi's avatar
 
Arnab Nandi committed
168 169 170 171 172 173 174 175
    $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);
    }
176
    
Arnab Nandi's avatar
 
Arnab Nandi committed
177 178
    $string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4];
    $string = substr($string,0,rand(5,8));
179 180
    
    //everytime we create a new code, we write it to session
181
    $_SESSION['captcha'] = $string;
182 183
    
    return $string;
Arnab Nandi's avatar
 
Arnab Nandi committed
184

185
}
Arnab Nandi's avatar
 
Arnab Nandi committed
186

Arnab Nandi's avatar
Arnab Nandi committed
187 188 189 190 191 192 193 194 195 196 197 198
/**
* 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", "");
199

Arnab Nandi's avatar
Arnab Nandi committed
200 201 202 203 204 205 206 207 208 209
  $filelist = array();
  if ($handle = opendir($fontdir)) {
    while ($file = readdir($handle)) {
      if (preg_match("/\.ttf$/i",$file) == 1)
        $filelist[] = $fontdir.'/'.$file;
    }
    closedir($handle);
  }

  return $filelist;
210
}
Arnab Nandi's avatar
Arnab Nandi committed
211

212 213 214 215
/**
* Prints an image containing a captcha code.
*/
function _captcha_image() {
216 217 218 219 220

    // 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
221
    if (!function_exists('imagecreatetruecolor')) return;
222

223 224 225 226 227 228 229 230 231
    // 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();
232 233 234 235 236

    // 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
Arnab Nandi's avatar
 
Arnab Nandi committed
237 238 239
    $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
    $foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80));

240
    // fill image with bgcolor
Arnab Nandi's avatar
 
Arnab Nandi committed
241 242
    imagefill($im, 0, 0, $background);

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    // 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
273 274
      }
    }
275

Arnab Nandi's avatar
Arnab Nandi committed
276
    else {
277 278 279 280 281 282 283 284 285 286
      // 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
287
    }
Arnab Nandi's avatar
 
Arnab Nandi committed
288

289
    // strikethrough
Arnab Nandi's avatar
 
Arnab Nandi committed
290 291
    imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground);

292
    // rotate only if function is defined (many PHP installations have this function missing)
293 294 295 296 297
    if (function_exists('imagerotate')) {
      $im2 = imagerotate($im,rand(-20,45),$background);
      imagedestroy($im);
      $im = $im2;
    }
298 299 300 301 302 303 304 305 306 307
    
    // 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);
      }
Arnab Nandi's avatar
 
Arnab Nandi committed
308
    }
309
    
310
    //output to browser
311 312
    imagepng($im);
    imagedestroy($im);
Arnab Nandi's avatar
 
Arnab Nandi committed
313

Arnab Nandi's avatar
Arnab Nandi committed
314 315
}

316
?>