Provide a way to define custom CAPTCHA validation
| Project: | CAPTCHA |
| Version: | 6.x-2.x-dev |
| Component: | Captcha API (captcha) |
| Category: | feature request |
| Priority: | normal |
| Assigned: | soxofaan |
| Status: | closed |
This issue is closely related to #463002: Drop preprocess op of hook_captcha?
Case insensitive validation is now part of CAPTCHA core, but there are other kinds of CAPTCHA validation that go further than a simple equality test. For example:
http://drupal.org/node/440490#comment-1601638 (handling multiple possible responses)
#472450: Right to Left fonts validation
I'm currently thinking along the lines of adding a '#captcha_validate' property to the generate op of hook_captcha like for example
<?php
case 'generate':
if ($captcha_type == 'Foo CAPTCHA') {
$captcha = array();
$captcha['solution'] = 'foo';
$captcha['form']['captcha_response'] = array(
'#type' => 'textfield',
'#title' => t('Enter "foo"'),
);
$captcha['form']['#captcha_validate'] = 'foo_custom_validation';
return $captcha;
?>We just can't reuse the '#validate' property because the arguments we should pass differ from a standard Drupal FAPI validate function. e.g.
<?php
function foo_custom_validation($solution, $response) {
}
?>Maybe we could also pass the CAPTCHA session id? But we could add this later too.

#1
this one should be fixed before 6.x-2.0-rc2
#2
poke. subscribing.
#3
marked #496806: Do more with $result as duplicate
#4
subscribe
#5
Here is a first patch to implement this.
For challenge implementing modules: custom CAPTCHA validation can be defined as follows:
<?php
function foo_captcha(...) {
...
case 'generate':
$captcha = array();
$captcha['solution'] = ...
$captcha['form'] = ...
$captcha['captcha_validate'] = 'foo_custom_validation';
return $captcha;
...
function foo_custom_validation($solution, $response) {
return $response == "foo $solution foo";
}
?>
function foo_custom_validation() will be called instead of the traditional equality test, it should return TRUE/FALSE for correct/wrong response.
Also attached: proof of concept example for the image CAPTCHA module to make it only accept the answer 'foo'.
Also note that the global case sensitive/insensitive option is implemented through this mechanism now.
edit: typo
#6
This works perfectly form me. For validating a response against multiple possible solutions in the riddler module I used:
// Custom captcha validation.function riddler_captcha_validate($solution, $response) {
$solution = str_ireplace(',', '', $solution);
$solution = explode(' ', $solution);
return in_array($response, $solution);
}
I did see that captcha will pass an empty response to the custom validation, which could require checking there. I'm not sure if there is a case where you would want to see an empty response, so maybe it's better to check for that before response is passed.
Thanks!
#7
@awolfey: thanks for testing
I'm not sure I understand what you mean, but to block empty responses you can flag a texfield as required, which will make drupal complain about the empty response, even before CAPTCHA kicks in. Take the simple math challenge for example:
<?php$result['form']['captcha_response'] = array(
'#type' => 'textfield',
'#title' => t('Math question'),
'#description' => t('Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.'),
'#field_prefix' => t('@x + @y = ', array('@x' => $x, '@y' => $y)),
'#size' => 4,
'#maxlength' => 2,
'#required' => TRUE,
);
?>
note the
<?php'#required' => TRUE,
?>
#8
I have required set to TRUE. I first did my test this way:
function riddler_captcha_validate($solution, $response) {drupal_set_message('at riddler validate '. $solution . ' response '. $response);
return stristr($solution, $response);
}
Which, on an empty response, gave me this php warning: warning: stristr() [function.stristr]: Empty delimiter in C:\wamp\www\sandbox\sites\all\modules\riddler\riddler.module on line 180. There is ALSO a message that the field is required.
Returning FALSE on !$response catches it before stristr(). It's not a problem for in_array().
Thanks
#9
That warning is because of the behavior of the stristr() function when the second argument is an empty string, it has nothing to do with the CAPTCHA module or even Drupal.
In this case you should indeed catch it before calling stristr().
Note that you are not limited to oneliner validation functions (maybe the examples suggest this): you can add for loops and if test all you want. As long as they return TRUE on success en FALSE on failure.
#10
Right. I wasn't expecting empty results to get passed to the validation function. Not a big deal.
Anyway, looking forward to this being committed.
Thanks.
#11
The patch from #5 went in with some further tweaks:
http://drupal.org/cvs?commit=232866
Question to the module developers that want to use this functionality:
the custom CAPTCHA validation function are expected to be of the form:
<?phpfunction foo_custom_validation($solution, $response) {
...
}
?>
Is this enough or would there be interest to receive more input, like $form, $form_state?
#12
Working for me. Updated the D6 version of riddler to be compatible. For that module there's no need for $form, $form_state that I can see now.
Thanks.
#13
added optional $element and $form_state for captcha validate functions as suggested in #11
http://drupal.org/cvs?commit=256822
this issue can finally be closed
#14
Automatically closed -- issue fixed for 2 weeks with no activity.