Commit d722740d authored by Arnab Nandi's avatar Arnab Nandi

new version, with challenges implemented as independent modules. Default captcha is now *math*

parent cc6069d9
##Captcha Readme##
To Install:
1. Enable the module, and then go to admin/settings/captcha to enable captchas for various actions and captcha types.
2. Then go to admin/access control to enable the access to the captcha (access captchas) for roles.
Enable the module, and then go to admin/settings/captcha to enable captchas
for various actions and captcha types.
Notes:
- Image Captcha uses GD to draw stuff, emits in PNG
Default, and Image Captchas:
This new version of captcha.module uses a simple math question as the
default captcha challenge. However, this is NOT as powerful as the original
image captcha, in case you are looking for foolproof protection.
The image captcha facility has been shifted to an additional submodule called
"textimage". You will need to install captcha.module AND textimage.module to
get image captchas working.
......@@ -10,11 +10,7 @@ function captcha_help($section = "admin/help#captcha") {
$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;
......@@ -22,56 +18,9 @@ function captcha_help($section = "admin/help#captcha") {
return $output;
}
/**
* Implementation of hook_menu().
*/
function captcha_menu($may_cache) {
$items = array();
$suffix = '';
if ($may_cache) {
if (arg(2)!=null) $suffix='/'.arg(2);
$items[] = array(
'path' => 'captcha/image'.$suffix, 'title' => t('captcha image'),
'callback' => '_captcha_call',
'callback arguments' => array('_captcha_image'),
'access' => user_access('access captchas'),
'type' => MENU_CALLBACK
);
}
return $items;
}
function captcha_perm() {
return array('access captchas');
}
function captcha_settings() {
//check for GD
if (!function_exists(imagecreate))
form_set_error('No GD', t('Image library not available. Captcha needs the GD library extension to be installed. Please install GD.'));
else {
$fonts_path = variable_get("captcha_fonts_path", "");
//check for TTF support
if (!function_exists(imagettftext))
drupal_set_message(t('Your image library does not seem to have TrueType font support. Captcha will work, but will use the default inbuilt font.'),'status');
else {
//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.'));
}
}
//this is where you can add more captcha points
$captcha_points = array(
'comment_form' => t('Comment Form'),
......@@ -100,39 +49,19 @@ function captcha_settings() {
}
}
// preprocess array into a map
foreach(module_implements('captchachallenge') as $module) {
$captchamodules[$module] = $module;
}
$form['captcha_type'] = array(
'#type' => 'select',
'#title' => t('Type of captcha to use'),
'#default_value' => variable_get('captcha_type','math'),
'#options' => _captcha_types(),
'#default_value' => variable_get('captcha_type','captcha'),
'#options' => $captchamodules,
'#description' => t('Select what kind of challenge you want to pose to the user')
);
$form['captcha_fonts_path'] = array(
'#type' => 'textfield',
'#title' => t('TrueType Fonts Path'),
'#default_value' => $fonts_path,
'#size' => 30,
'#maxlength' => 255,
'#description' => t('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 />';
$form['captcha_info'] = array (
'#type' => 'item',
'#title' => t('Image and font information'),
'#value' => $imagefontinfo,
);
return $form;
}
......@@ -142,7 +71,7 @@ function captcha_form_alter($formid, &$form) {
global $user;
$captcha_type = variable_get("captcha_type", NULL);
if (!$captcha_type) return;
$flag = true;
......@@ -158,12 +87,12 @@ function captcha_form_alter($formid, &$form) {
break;
}
}
if ($flag && isset($trigger)) {
if ($captcha_type = _captcha_load()) {
$form['#submit'] = array('captcha_submit' => array()) + $form['#submit'];
if (!_captcha_validate($_POST['edit']['captcha_response'])) {
call_user_func_array('_captcha_'. $captcha_type .'_challenge', array(&$form, &$_SESSION['captcha']));
$form['#submit'] = array('captcha_submit' => array()) + $form['#submit'];
if (!_captcha_validate($_POST['edit']['captcha_response'])) {
//use call_func because module_invoke does not allow call by reference.
if (module_hook($captcha_type, 'captchachallenge')) {
call_user_func_array($captcha_type.'_captchachallenge', array(&$form, &$_SESSION['captcha']));
}
}
}
......@@ -176,7 +105,7 @@ function captcha_form_alter($formid, &$form) {
function captcha_submit() {
if($_SESSION['captcha_correct']) {
unset($_SESSION['captcha_correct']);
unset($_SESSION['captcha']);
unset($_SESSION['captcha']);
}
}
......@@ -188,81 +117,46 @@ function _captcha_validate($captcha_response) {
if (trim($captcha_response) == '') return FALSE;
global $user;
$captcha_type = variable_get("captcha_type", NULL);
$trigger = NULL;
if ($captcha_type = _captcha_load()) {
call_user_func_array('_captcha_'. $captcha_type .'_validate', array(&$captcha_response, &$_SESSION['captcha_correct']));
if (module_hook($captcha_type, 'captchavalidate')) {
call_user_func_array($captcha_type.'_captchavalidate', array(&$captcha_response, &$_SESSION['captcha_correct']));
}
return $_SESSION['captcha_correct'];
}
/**
* Returns an array of files with TTF extensions in the specified directory.
*/
function _captcha_font_list() {
$fontdir = variable_get("captcha_fonts_path", "");
$filelist = array();
if ($handle = opendir($fontdir)) {
while ($file = readdir($handle)) {
if (preg_match("/\.ttf$/i",$file) == 1)
$filelist[] = $fontdir.'/'.$file;
}
closedir($handle);
}
return $filelist;
}
function _captcha_call($func) {
_captcha_load();
if (function_exists($func)) call_user_func_array($func, array());
}
/**
* Loads the current captcha system into memory
*/
function _captcha_load() {
$captcha_type = variable_get("captcha_type", 'math');
$path = drupal_get_path('module', 'captcha');
include_once($path.'/captcha_'.$captcha_type.'.inc');
$function_challenge = '_captcha_'. $captcha_type .'_challenge';
$function_validate = '_captcha_'. $captcha_type .'_validate';
if(function_exists($function_challenge) && function_exists($function_validate)) {
return $captcha_type;
}
else return false;
/*
* Default implementation of the captcha challenge & validation
*/
function captcha_captchachallenge(&$form, &$captcha) {
$x = rand(1,10);
$y = rand(1,10);
$captcha = ($x + $y) . '';
$form['captcha_response'] = array (
'#type' => 'textfield',
'#title' => t('Math Question: What is '. $x .' + '. $y .'?'),
'#defaultvalue' => '',
'#description' => t('Please solve the math problem above and type in the result. e.g. for 1+1, type 2'),
'#weight' => 0,
'#required' => TRUE,
'#validate' => array('_captcha_validate' => array())
);
}
/**
* Generates list of available captcha methods
*/
function _captcha_types() {
static $types;
if (!isset($types)) {
$types = array();
$path = drupal_get_path('module', 'captcha');
$files = file_scan_directory($path, '^captcha_.*\.inc$');
foreach ($files as $filename => $file) {
include_once($filename);
$function_challenge = '_'. $file->name .'_challenge';
$function_validate = '_'. $file->name .'_validate';
if(function_exists($function_challenge) && function_exists($function_validate)) {
$types[] = substr($file->name, 8);
}
}
function captcha_captchavalidate(&$captcha_word, &$correct) {
$captcha_word = drupal_strtolower($captcha_word);
if ($captcha_word == $_SESSION['captcha']) {
$correct = TRUE;
}
//post process types
foreach($types as $type) {
$r_types[$type] = $type;
else {
$correct = FALSE;
form_set_error('captcha_response', t('The answer you entered to the math problem is incorrect.'));
}
return $r_types;
}
?>
<?
function _captcha_image_challenge(&$form) {
$form['captcha_response'] = array (
'#type' => 'textfield',
'#title' => t('Captcha Validation'),
'#defaultvalue' => '',
'#required' => TRUE,
'#validate' => array('_captcha_validate' => array()),
'#description' => t('Please type in the letters/numbers that are shown in the image above.'),
'#prefix' => '<img src="' . url('captcha/image/'.time()) . '" alt="Captcha Image: you will need to recognize the text in it."/>',
);
return $form;
}
function _captcha_image_validate(&$captcha_word, &$correct) {
$captcha_word = drupal_strtolower($captcha_word);
if ($captcha_word == $_SESSION['captcha']) {
$correct = true;
}
else {
$correct = false;
form_set_error('captcha_response', t('The image verification code you entered is incorrect.'));
}
}
/**
* Prints an image containing a captcha code.
*/
function _captcha_image() {
// 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
if (!function_exists('imagecreatetruecolor')) return;
// 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();
// 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
$background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
$foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80));
// fill image with bgcolor
imagefill($im, 0, 0, $background);
// 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<drupal_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 = drupal_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]);
}
}
else {
// write text using a built-in font
$x = 10;
$y = 0;
for ($i=0;$i<drupal_strlen($string);$i++) {
imagestring($im,5,$x,$y,drupal_substr($string,$i,1),$foreground);
$x += rand(10,15);
$y += rand(-4,4);
}
}
// strikethrough
imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground);
// rotate only if function is defined (many PHP installations have this function missing)
if (function_exists('imagerotate')) {
$im2 = imagerotate($im,rand(-20,45),$background);
imagedestroy($im);
$im = $im2;
}
// 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);
}
}
//output to browser
imagepng($im);
imagedestroy($im);
}
/**
* Returns a random string for use in a captcha
*/
function _captcha_code() {
$consts='bcdgjxvmnprst';
$vowels='aeiou';
for ($x=0; $x < 6; $x++) {
mt_srand ((double) microtime() * 1000000);
$const[$x] = drupal_substr($consts,mt_rand(0,drupal_strlen($consts)-1),1);
$vow[$x] = drupal_substr($vowels,mt_rand(0,drupal_strlen($vowels)-1),1);
}
$string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4];
$string = drupal_substr($string,0,rand(5,8));
//everytime we create a new code, we write it to session
$_SESSION['captcha'] = drupal_strtolower($string);
return $string;
}
?>
<?
function _captcha_math_challenge(&$form, &$captcha) {
$x = rand(1,10);
$y = rand(1,10);
$captcha = ($x + $y) . '';
$form['captcha_response'] = array (
'#type' => 'textfield',
'#title' => t('Math Question: What is '. $x .' + '. $y .'?'),
'#defaultvalue' => '',
'#description' => t('Please solve the math problem above and type in the result. e.g. for 1+1, type 2'),
'#weight' => 0,
'#required' => TRUE,
'#validate' => array('_captcha_validate' => array())
);
}
function _captcha_math_validate(&$captcha_word, &$correct) {
$captcha_word = drupal_strtolower($captcha_word);
if ($captcha_word == $_SESSION['captcha']) {
$correct = TRUE;
}
else {
$correct = FALSE;
form_set_error('captcha_response', t('The answer you entered to the math problem is incorrect.'));
}
}
?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment