Commit 3304e584 authored by RobLoach's avatar RobLoach
Browse files

Moved to DRUPAL-6--1 branch.

parent 27f29200
$Id$
### CAPTCHA Readme
Please checkout the branch according to the version of Drupal you're running...
captcha.module is the basic CAPTCHA module that offers general CAPTCHA
administration and a simple math challenge.
Drupal 4:
DRUPAL-4-7
text_captcha offers another simple text based challenge.
Drupal 5:
DRUPAL-5--3
image_captcha offers an image based challenge.
Drupal 6:
DRUPAL-6--1
Installation:
Installation is like all normal modules (e.g. extract in the directory sites/all/modules)
The basic CAPTCHA module has no dependencies, so nothing special is required.
Configuration:
The configuration page is at admin/user/captcha, here you can configure
the CAPTCHA module and enable challenges for the desired forms.
Using the Image CAPTCHA:
If you plan on using image_captcha, you will have to install TTF fonts into
the image_captcha/fonts or files directory. You can find free fonts
available at a number of different locations:
- DejaVu:
http://dejavu.sourceforge.net/wiki/index.php/Download
- Gentium:
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=Gentium
- Redhat:
https://www.redhat.com/promo/fonts
- Matt's Free Fonts Page:
http://www.theory.org/~matt/strthrwr/fonts/free
<?php
// $Id$
/**
* Return an array with the available CAPTCHA types, for use as options array
* for a select form elements.
* The array is an associative array mapping "$module/$type" to
* "$type ($module)" with $module the module name implementing the CAPTCHA
* and $type the name of the CAPTCHA type.
* (It also includes a 'none' => '<none>' option)
*/
function _captcha_available_challenge_types() {
$captcha_types['none'] = '<'. t('none') .'>';
foreach (module_implements('captcha') as $module) {
$result = call_user_func_array($module .'_captcha', 'list');
if (is_array($result)) {
foreach ($result as $type) {
$captcha_types["$module/$type"] = "$type ($module)";
}
}
}
return $captcha_types;
}
/**
* General CAPTCHA settings handler. Main entry point for CAPTCHA management.
*
* If arguments are given: first argument is used as form_id, the second one
* is interpreted as action (such as disable, delete and enable) to execute on
* the form_id.
* Otherwise: returns the general CAPTCHA configuration form.
*/
function captcha_admin($form_id = '', $op = '') {
// if $form_id and action $op given: do the action
if ($form_id) {
switch ($op) {
case 'disable':
// disable the CAPTCHA for the form: set the module and type to NULL
db_query("UPDATE {captcha_points} SET module = NULL, type = NULL WHERE form_id = '%s'", $form_id);
drupal_set_message(t('Disabled CAPTCHA for form %form_id.', array('%form_id' => $form_id)));
// goto the CAPTCHA administration or alternative destination if present in URI
drupal_goto('admin/user/captcha');
break;
case 'delete':
db_query("DELETE FROM {captcha_points} WHERE form_id = '%s'", $form_id);
drupal_set_message(t('Deleted CAPTCHA for form %form_id.', array('%form_id' => $form_id)));
// goto the CAPTCHA administration or alternative destination if present in URI
drupal_goto('admin/user/captcha');
break;
case 'enable':
db_query("DELETE FROM {captcha_points} WHERE form_id = '%s'", $form_id);
db_query("INSERT INTO {captcha_points} (form_id, module, type) VALUES ('%s', NULL, NULL)", $form_id);
// No drupal_goto() call because we have to go to the CAPTCHA administration
// form and not a different destination if that would be present in the
// URI. So we call this form explicitly. The destination will be preserved
// so after completing the form, the user will still be redirected.
return drupal_get_form('captcha_captcha_point_settings_form', $form_id);
break;
}
// return edit form for individual form (aka CAPTCHA point)
return drupal_get_form('captcha_captcha_point_settings_form', $form_id);
}
// no $form_id or legal action given: generate general CAPTCHA settings form
return drupal_get_form('captcha_admin_settings');
}
/**
* Form builder function for the general CAPTCHA configuration
*/
function captcha_admin_settings() {
// field for the CAPTCHA administration mode
$form['captcha_administration_mode'] = array(
'#type' => 'checkbox',
'#title' => t('Add CAPTCHA administration links to forms'),
'#default_value' => variable_get('captcha_administration_mode', FALSE),
'#description' => t('This option is very helpful to enable/disable challenges on forms. When enabled, users with the "%admincaptcha" permission will see CAPTCHA administration links on all forms (except on administrative pages, which shouldn\'t be accessible to untrusted users in the first place). These links make it possible to enable a challenge of the desired type or disable it.', array('%admincaptcha' => 'administer CAPTCHA settings')),
);
// field set with form_id -> CAPTCHA type configuration
$form['captcha_types'] = array(
'#type' => 'fieldset',
'#title' => t('Challenge type per form'),
'#description' => t('Select the challenge type you want for each of the listed forms (identified by their so called <em>form_id</em>\'s). You can easily add arbitrary forms with the help of the \'%CAPTCHA_admin_links\' option.', array('%CAPTCHA_admin_links' => t('Add CAPTCHA administration links to forms'))),
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#theme' => 'captcha_admin_settings_captcha_points',
);
// list all possible form_id's
$captcha_types = _captcha_available_challenge_types();
$result = db_query("SELECT * FROM {captcha_points} ORDER BY form_id");
while ($captcha_point = db_fetch_object($result)) {
$form['captcha_types'][$captcha_point->form_id]['form_id'] = array(
'#value' => $captcha_point->form_id,
);
// select widget for CAPTCHA type
$form['captcha_types'][$captcha_point->form_id]['captcha_type'] = array(
'#type' => 'select',
'#default_value' => "{$captcha_point->module}/{$captcha_point->type}",
'#options' => $captcha_types,
);
// additional operations
$form['captcha_types'][$captcha_point->form_id]['operations'] = array(
'#value' => implode(", ", array(
l(t('delete'), "admin/user/captcha/{$captcha_point->form_id}/delete"),
))
);
}
// field(s) for setting the additional CAPTCHA description
if (module_exists('locale')) {
$langs = locale_language_list();
$form['captcha_descriptions'] = array(
'#type' => 'fieldset',
'#title' => t('Challenge description'),
'#description' => t('With this description you can explain the purpose of the challenge to the user.'),
);
foreach ($langs as $lang_code => $lang_name) {
$form['captcha_descriptions']["captcha_description_$lang_code"] = array(
'#type' => 'textfield',
'#title' => t('For language %lang_name (code %lang_code)', array('%lang_name' => $lang_name, '%lang_code' => $lang_code)),
'#default_value' => _captcha_get_description($lang_code),
'#maxlength' => 256,
);
}
}
else {
$form['captcha_description'] = array(
'#type' => 'textfield',
'#title' => t('Challenge description'),
'#description' => t('With this description you can explain the purpose of the challenge to the user.'),
'#default_value' => _captcha_get_description(),
'#maxlength' => 256,
);
}
// field for CAPTCHA persistence
$form['captcha_persistence'] = array(
'#type' => 'radios',
'#title' => t('Persistence'),
'#default_value' => variable_get('captcha_persistence', CAPTCHA_PERSISTENCE_SHOW_ALWAYS),
'#options' => array(
CAPTCHA_PERSISTENCE_SHOW_ALWAYS => t('Always add a challenge.'),
CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM => t('Omit challenges for a form once the user has successfully responded to a challenge for that form.'),
CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL => t('Omit challenges for all forms once the user has successfully responded to a challenge.'),
),
'#description' => t('Define if challenges should be omitted during the rest of a session once the user successfully responses to a challenge.'),
);
// option for logging wrong responses
$form['captcha_log_wrong_responses'] = array(
'#type' => 'checkbox',
'#title' => t('Log wrong responses'),
'#description' => t('Report information about wrong responses to the !log.', array('!log' => l('log', 'admin/reports/dblog'))),
'#default_value' => variable_get('captcha_log_wrong_responses', FALSE),
);
// submit button
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Custom theme function for a table of (form_id -> CAPTCHA type) settings
*/
function theme_captcha_admin_settings_captcha_points($form) {
foreach (element_children($form) as $key) {
$row = array();
$row[] = drupal_render($form[$key]['form_id']);
$row[] = drupal_render($form[$key]['captcha_type']);
$row[] = drupal_render($form[$key]['operations']);
$rows[] = $row;
}
$header = array('form_id', t('Challenge type (module)'), t('Operations'));
$output = theme('table', $header, $rows);
return $output;
}
/**
* Submission function for captcha_admin_settings form
*/
function captcha_admin_settings_submit($form, &$form_state) {
if ($form['form_id']['#value'] == 'captcha_admin_settings') {
variable_set('captcha_administration_mode', $form_state['values']['captcha_administration_mode']);
foreach ($form_state['values']['captcha_types'] as $captcha_point_form_id => $data) {
if ($data['captcha_type'] == 'none') {
db_query("UPDATE {captcha_points} SET module = NULL, type = NULL WHERE form_id = '%s'", $captcha_point_form_id);
}
else {
list($module, $type) = explode('/', $data['captcha_type']);
db_query("UPDATE {captcha_points} SET module = '%s', type = '%s' WHERE form_id = '%s'", $module, $type, $captcha_point_form_id);
}
}
// description stuff
if (module_exists('locale')) {
$langs = locale_language_list();
foreach ($langs as $lang_code => $lang_name) {
variable_set("captcha_description_$lang_code", $form_state['values']["captcha_description_$lang_code"]);
}
}
else {
variable_set('captcha_description', $form_state['values']['captcha_description']);
}
variable_set('captcha_persistence', $form_state['values']['captcha_persistence']);
variable_set('captcha_log_wrong_responses', $form_state['values']['captcha_log_wrong_responses']);
drupal_set_message(t('The CAPTCHA settings were saved.'), 'status');
}
}
/**
* form generating function for CAPTCHA point settings
*/
function captcha_captcha_point_settings_form($form_state, $form_id) {
$result = db_query("SELECT * FROM {captcha_points} WHERE form_id = '%s'", $form_id);
$captcha_point = db_fetch_object($result);
if ($captcha_point) {
$form = array();
$form['captcha_point_form_id'] = array(
'#type' => 'hidden',
'#value' => $captcha_point->form_id,
);
// select widget for CAPTCHA type
$form['captcha_type'] = array(
'#type' => 'select',
'#title' => t('Select the challenge for @form_id', array('@form_id' => $form_id)),
'#default_value' => "{$captcha_point->module}/{$captcha_point->type}",
'#options' => _captcha_available_challenge_types(),
);
// submit button
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
// cancel button
$form['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
);
return $form;
}
else {
// illegal form_id
drupal_set_message(t('Unavailable form_id %form_id ', array('%form_id' => $form_id)), 'error');
// goto the CAPTCHA administration or alternative destination if present in URI
drupal_goto('admin/user/captcha');
}
}
/**
* submit function for captcha_captcha_point_settings_form
*/
function captcha_captcha_point_settings_form_submit($form, &$form_state) {
if ($form_state['clicked_button']['#value'] == t('Save')) {
$captcha_point_form_id = $form_state['values']['captcha_point_form_id'];
$captcha_type = $form_state['values']['captcha_type'];
if ($captcha_type == 'none') {
db_query("UPDATE {captcha_points} SET module = NULL, type = NULL WHERE form_id = '%s'", $captcha_point_form_id);
}
else {
list($module, $type) = explode('/', $captcha_type);
db_query("UPDATE {captcha_points} SET module = '%s', type = '%s' WHERE form_id = '%s'", $module, $type, $captcha_point_form_id);
}
drupal_set_message(t('Saved CAPTCHA settings.'), 'status');
}
}
/**
* Funtion for generating a page with CAPTCHA examples
* If the arguments $module and $challenge are not set, generate a list with
* examples of the available CAPTCHA types.
* If $module and $challenge are set, generate 10 examples of the concerning
* CAPTCHA.
*/
function captcha_examples($form_state, $module, $challenge) {
$form = array();
if ($module && $challenge) {
// generate 10 examples
for ($i=0; $i<10; $i++) {
// generate CAPTCHA
$captcha = call_user_func_array($module .'_captcha', array('generate', $challenge));
// add form elements
$form["challenge_{$i}"] = $captcha['form'];
}
}
else {
// generate a list with examples of the available CAPTCHA types
$form['info'] = array(
'#value' => t('This page gives an overview of all available challenge types, generated with their current settings.'),
);
foreach (module_implements('captcha') as $mkey => $module) {
$challenges = call_user_func_array($module .'_captcha', 'list');
if ($challenges) {
foreach ($challenges as $ckey => $challenge) {
// generate CAPTCHA
$captcha = call_user_func_array($module .'_captcha', array('generate', $challenge));
// build form
$form["captcha_{$mkey}_{$ckey}"] = array(
'#type' => 'fieldset',
'#title' => t('Challenge "%challenge" by module "%module"', array('%challenge' => $challenge, '%module' => $module)),
'challenge' => $captcha['form'],
'more_examples' => array(
'#value' => l(t('10 more examples of this challenge.'), "admin/user/captcha/captcha/examples/$module/$challenge"),
),
);
}
}
}
}
return $form;
}
; $Id$
name = CAPTCHA
description = Base CAPTCHA module for adding challenges to arbitrary forms.
package = "Spam control"
core = 6.x
<?php
// $Id$
/**
* Implementation of hook_schema().
*/
function captcha_schema() {
$schema['captcha_points'] = array(
'fields' => array(
'form_id' => array(
'type' => 'varchar',
'length' => 128,
'not null' => TRUE
),
'module' => array(
'type' => 'varchar',
'length' => 64,
'default' => NULL
),
'type' => array(
'type' => 'varchar',
'length' => 64,
'default' => NULL
),
),
'primary key' => array('form_id'),
);
return $schema;
}
/**
* Implementation of hook_install().
*/
function captcha_install() {
drupal_install_schema('captcha');
// insert some defaults
$form_ids = array('comment_form', 'contact_mail_user', 'contact_mail_page',
'user_register', 'user_pass', 'user_login', 'user_login_block');
foreach ($form_ids as $form_id) {
db_query("INSERT INTO {captcha_points} (form_id, module, type) VALUES ('%s', NULL, NULL)", $form_id);
}
}
/**
* Implementation of hook_uninstall().
*/
function captcha_uninstall() {
drupal_uninstall_schema('captcha');
db_query("DELETE FROM {variable} WHERE name LIKE 'captcha_%'");
cache_clear_all('variables', 'cache');
}
/**
* Implementation of hook_update_N()
*/
function captcha_update_1() {
$items = array();
switch ($GLOBALS['db_type']) {
case 'mysql':
case 'mysqli':
$items[] = update_sql("CREATE TABLE {captcha_points} (
form_id varchar(128) NOT NULL,
module varchar(64) default NULL,
type varchar(64) default NULL,
PRIMARY KEY (form_id)
) /*!40100 DEFAULT CHARACTER SET utf8 */;"
);
$succes = TRUE;
break;
case 'pgsql':
$items[] = update_sql("CREATE TABLE {captcha_points} (
form_id varchar(128) NOT NULL,
module varchar(64) default NULL,
type varchar(64) default NULL,
PRIMARY KEY (form_id)
);"
);
$succes = TRUE;
break;
default:
drupal_set_message(t('Unsupported database.'), 'error');
$succes = FALSE;
break;
}
if ($succes) {
// insert some defaults
$form_ids = array('comment_form', 'contact_mail_user', 'contact_mail_page',
'user_register', 'user_pass');
foreach ($form_ids as $form_id) {
$items[] = update_sql("INSERT INTO {captcha_points} (form_id, module, type) VALUES ('$form_id', NULL, NULL)");
}
}
return $items;
}
/**
* Implementation of hook_update_N()
*/
function captcha_update_2() {
$items = array();
// insert some defaults
$form_ids = array('user_login', 'user_login_block');
foreach ($form_ids as $form_id) {
$items[] = update_sql("INSERT INTO {captcha_points} (form_id, module, type) VALUES ('$form_id', NULL, NULL)");
}
return $items;
}
<?php
// $Id$
/**
* @file
* This module enables basic CAPTCHA functionality:
* administrators can add a CAPTCHA to desired forms that users without
* the 'skip CAPTCHA' permission (typically anonymous visitors) have
* to solve.
*
*/
define('CAPTCHA_UNSOLVED_CHALLENGES_MAX', 20);
define('CAPTCHA_PERSISTENCE_SHOW_ALWAYS', 1);
define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM', 2);
define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL', 3);
/**
* Implementation of hook_help().
*/
function captcha_help($path, $arg) {
switch ($path) {
case 'admin/help#captcha':
$output = '<p>'. t('"CAPTCHA" is an acronym for "Completely Automated Public Turing test to tell Computers and Humans Apart". It is typically a challenge-response test to determine whether the user is human. The CAPTCHA module is a tool to fight automated submission by malicious users (spamming) of for example comments forms, user registration forms, guestbook forms, etc. You can extend the desired forms with an additional challenge, which should be easy for a human to solve correctly, but hard enough to keep automated scripts and spam bots out.') .'</p>';
$output .= '<p>'. t('Note that the CAPTCHA module interacts with page caching (see <a href="!performancesettings">performance settings</a>). Because the challenge should be unique for each generated form, the caching of the page it appears on is prevented. Make sure that these forms do not appear on too many pages or you will lose much caching efficiency. For example, if you put a CAPTCHA on the user login block, which typically appears on each page for anonymous visitors, caching will practically be disabled. The comment submission forms are another example. In this case you should set the "%commentlocation" to "%separatepage" in the comment settings of the relevant <a href="!contenttypes">content types</a> for better caching efficiency.' ,
array(
'!performancesettings' => url('admin/settings/performance'),
'%commentlocation' => t('Location of comment submission form'),
'%separatepage' => t('Display on separate page'),
'!contenttypes' => url('admin/content/types'),
)
) .'</p>';
$output .= '<p>'. t('CAPTCHA is a trademark of Carnegie Mellon University.') .'</p>';
return $output;
case 'admin/user/captcha':
case 'admin/user/captcha/captcha':
case 'admin/user/captcha/captcha/settings':
$output = '<p>'. t('A CAPTCHA can be added to virtually each Drupal form. Some default forms are already provided in the form list, but arbitrary forms can be easily added and managed when the option "%adminlinks" is enabled.',
array('%adminlinks' => t('Add CAPTCHA administration links to forms'))) .'</p>';
$output .= '<p>' . t('Users with the "%skipcaptcha" <a href="@perm">permission</a> won\'t be offered a challenge. Be sure to grant this permission to the trusted users (e.g. site administrators). If you want to test a protected form, be sure to do it as a user without the "%skipcaptcha" permission (e.g. as anonymous user).',
array('%skipcaptcha' => 'skip CAPTCHA', '@perm' => url('admin/user/permissions'))) .'</p>';
return $output;
}
}
/**
* Implementation of hook_menu().
*/
function captcha_menu() {
$items = array();
// main configuration page of the basic CAPTCHA module
$items['admin/user/captcha'] = array(
'title' => 'CAPTCHA',
'description' => 'Administer how and where CAPTCHAs are used.',
'file' => 'captcha.admin.inc',
'page callback' => 'captcha_admin',
'page arguments' => array(3, 4),
'access arguments' => array('administer CAPTCHA settings'),
'type' => MENU_NORMAL_ITEM,
);
// the default local task (needed when other modules want to offer
// alternative CAPTCHA types and their own configuration page as local task)
$items['admin/user/captcha/captcha'] = array(
'title' => 'CAPTCHA',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -20,
);
$items['admin/user/captcha/captcha/settings'] = array(
'title' => 'General settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/user/captcha/captcha/examples'] = array(
'title' => 'Examples',
'description' => 'An overview of the available challenge types with examples.',
'file' => 'captcha.admin.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array('captcha_examples', 5, 6),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
return $items;
}
/**
* Implementation of hook_perm().
*/
function captcha_perm() {
return array('administer CAPTCHA settings', 'skip CAPTCHA');
}
/**
* Implementation of hook_requirements().
*/
function captcha_requirements($phase) {
$requirements = array();
$t = get_t();
if ($phase == 'runtime') {
// show the wrong response counter in the status report
$requirements['captcha_wrong_response_counter'] = array(
'title' => $t('CAPTCHA'),
'value' => $t('Already @counter blocked form submissions', array('@counter' => variable_get('captcha_wrong_response_counter', 0))),
'severity' => REQUIREMENT_INFO,
);
}
return $requirements;
}
/**
* Implementation of hook_theme().
*/
function captcha_theme() {
return array(
'captcha_admin_settings_captcha_points' => array(
'arguments' => array('form' => NULL)
)
);
}
/**
* Get the description which appears above the CAPTCHA in forms.
* If the locale module is enabled, an optional language code can be given
*/
function _captcha_get_description($lang_code=NULL) {
$default = t('This question is for testing whether you are a human visitor and to prevent automated spam submissions.');
if (module_exists('locale')) {
if ($lang_code == NULL) {
global $language;
$lang_code = $language->language;
}
$description = variable_get("captcha_description_$lang_code", $default);
}
else {
$description = variable_get('captcha_description', $default);
}
return $description;
}
/**
* Helper function for checking if the CAPTCHA for the given form_id should
* be skipped because of CAPTCHA persistence.
*/
function _captcha_persistence_skip($form_id) {
switch (variable_get('captcha_persistence', CAPTCHA_PERSISTENCE_SHOW_ALWAYS)) {
case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL:
return isset($_SESSION['captcha']['success']) && ($_SESSION['captcha']['success'] === TRUE);
case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM:
return isset($_SESSION['captcha'][$form_id]['success']) && ($_SESSION['captcha'][$form_id]['success'] === TRUE);
default:
return FALSE;
}
}
/**
* Implementation of hook_form_alter().
*
* This function adds a CAPTCHA to forms for untrusted users if needed and adds
* CAPTCHA administration links for site administrators if this option is enabled.
*/
function captcha_form_alter(&$form, $form_state, $form_id) {