Commit ed20c939 authored by ameoba32's avatar ameoba32

[NEW] Support of ReCaptcha 2.0

parent 30edd97c
......@@ -1977,6 +1977,7 @@ lib/calendar/calrecurrence.php -text
lib/calendar/index.php -text
lib/calendar/tikicalendarlib.php -text
lib/captcha/Captcha_Questions.php -text
lib/captcha/Captcha_ReCaptcha20.php -text
lib/captcha/DejaVuSansMono.ttf -text
lib/captcha/captchalib.js -text
lib/captcha/captchalib.php -text
......
<?php
// (c) Copyright 2002-2015 by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
class Captcha_ReCaptcha20 extends Zend\Captcha\ReCaptcha
{
protected $_RESPONSE = 'g-recaptcha-response';
/**
* Validate captcha
*
* @see Zend_Validate_Interface::isValid()
* @param mixed $value
* @param array|null $context
* @return boolean
*/
public function isValid($value, $context = null)
{
if (!is_array($value) && !is_array($context)) {
$this->error(self::MISSING_VALUE);
return false;
}
if (empty($value[$this->_RESPONSE])) {
$this->error(self::MISSING_VALUE);
return false;
}
// Google request was cached
if (in_array($value[$this->_RESPONSE], $_SESSION['recaptcha_cache'])) {
return true;
}
//set POST variables
$url = 'https://www.google.com/recaptcha/api/siteverify';
$fields = array(
'secret' => urlencode($this->getPrivkey()),
'response' => urlencode($value[$this->_RESPONSE]),
'remoteip' => urlencode($_SERVER['REMOTE_ADDR']),
);
$fields_string = '';
foreach ($fields as $k => $v) {
$fields_string .= $k . '=' . $v . '&';
}
rtrim($fields_string, '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = @json_decode(curl_exec($ch), true);
if (!is_array($result)) {
$this->error(self::ERR_CAPTCHA);
return false;
}
if ($result['success'] == false) {
$this->error(self::BAD_CAPTCHA);
return false;
}
// Cache google respnonse to avoid second resubmission on ajax form
$_SESSION['recaptcha_cache'][] = $value[$this->_RESPONSE];
return true;
}
/**
* Render captcha
*
* @return string
*/
public function render()
{
return '<div class="g-recaptcha" data-sitekey="' . $this->getPubkey() . '" id="antibotcode"></div>';
}
/**
* Render captcha though Ajax
*
* @return string
*/
public function renderAjax()
{
static $id = 1;
TikiLib::lib('header')->add_js("
grecaptcha.render('g-recaptcha{$id}', {
'sitekey': '{$this->getPubkey()}'
});
", 100);
return '<div id="g-recaptcha'.$id.'"></div>';
}
}
......@@ -46,7 +46,11 @@ class Captcha
if (empty($type)) {
if ($prefs['recaptcha_enabled'] == 'y' && !empty($prefs['recaptcha_privkey']) && !empty($prefs['recaptcha_pubkey'])) {
$type = 'recaptcha';
if ($prefs['recaptcha_version'] == '2') {
$type = 'recaptcha20';
} else {
$type = 'recaptcha';
}
} else if ($prefs['captcha_questions_active'] == 'y' && !empty($prefs['captcha_questions'])) {
$type = 'questions';
} else if (extension_loaded('gd') && function_exists('imagepng') && function_exists('imageftbbox')) {
......@@ -67,7 +71,24 @@ class Captcha
$this->captcha->setOption('ssl', true);
$this->type = 'recaptcha';
$this->type = $type;
$this->recaptchaCustomTranslations();
} else if ($type === 'recaptcha20') {
include_once('lib/captcha/Captcha_ReCaptcha20.php');
$this->captcha = new Captcha_ReCaptcha20(
array(
'privkey' => $prefs['recaptcha_privkey'],
'pubkey' => $prefs['recaptcha_pubkey'],
'theme' => isset($prefs['recaptcha_theme']) ? $prefs['recaptcha_theme'] : 'clean',
)
);
$this->captcha->setOption('ssl', true);
$this->type = $type;
$this->recaptchaCustomTranslations();
} else if ($type === 'default') {
......@@ -152,16 +173,22 @@ class Captcha
{
$access = TikiLib::lib('access');
if ($access->is_xml_http_request()) {
$params = json_encode($this->captcha->getService()->getOptions());
$id = 1;
TikiLib::lib('header')->add_js('
if ($this->type == 'recaptcha20') {
return $this->captcha->renderAjax();
} else {
$params = json_encode($this->captcha->getService()->getOptions());
$id = 1;
TikiLib::lib('header')->add_js('
Recaptcha.create("' . $this->captcha->getPubKey() . '",
"captcha' . $id . '",' . $params . '
);
', 100);
return '<div id="captcha' . $id . '"></div>';
return '<div id="captcha' . $id . '"></div>';
}
} else {
if ($this->captcha instanceof Zend\Captcha\ReCaptcha){
if ($this->captcha instanceof Captcha_ReCaptcha20) {
return $this->captcha->render();
} else if ($this->captcha instanceof Zend\Captcha\ReCaptcha){
return $this->captcha->getService()->getHtml();
}
return $this->captcha->render();
......@@ -179,9 +206,9 @@ Recaptcha.create("' . $this->captcha->getPubKey() . '",
if (is_null($input)) {
$input = $_REQUEST;
}
if ($this->type == 'recaptcha') {
if ($this->type == 'recaptcha' || $this->type == 'recaptcha20') {
return $this->captcha->isValid($input);
} else {
} else {
return $this->captcha->isValid($input['captcha']);
}
}
......@@ -209,7 +236,7 @@ Recaptcha.create("' . $this->captcha->getPubKey() . '",
'badCaptcha' => tra('You have mistyped the anti-bot verification code. Please try again.')
);
if ($this->type == 'recaptcha')
if ($this->type == 'recaptcha' || $this->type == 'recaptcha20')
$errors['errCaptcha'] = tra('Failed to validate CAPTCHA');
else
$errors['missingID'] = tra('CAPTCHA ID field is missing');
......
......@@ -118,14 +118,7 @@ class Services_Comment_Controller
if (empty($user) && $prefs['feature_antibot'] == 'y') {
$captchalib = TikiLib::lib('captcha');
if (! $captchalib->validate(
array(
'recaptcha_challenge_field' => $input->recaptcha_challenge_field->none(),
'recaptcha_response_field' => $input->recaptcha_response_field->none(),
'captcha' => $input->captcha->none(),
)
)
) {
if (! $captchalib->validate($input->none())) {
$errors[] = $captchalib->getErrors();
}
}
......
......@@ -42,5 +42,15 @@ function prefs_recaptcha_list()
),
'default' => 'clean',
),
'recaptcha_version' => array(
'name' => tra('Version'),
'type' => 'list',
'description' => tra('ReCaptcha version.'),
'options' => array(
'1' => tra('1.0'),
'2' => tra('2.0'),
),
'default' => '2',
),
);
}
......@@ -115,6 +115,7 @@
{preference name=recaptcha_pubkey}
{preference name=recaptcha_privkey}
{preference name=recaptcha_theme}
{preference name=recaptcha_version}
</div>
{preference name=captcha_questions_active}
<div class="adminoptionboxchild" id="captcha_questions_active_childcontainer">
......
......@@ -8,9 +8,9 @@
{$inputclass = 'col-md-4 col-sm-6'}
{$captchaclass = 'col-md-5 col-sm-7 col-md-offset-4 col-sm-offset-3'}
{/if}
<div class="form-group">
{if $captchalib->type eq 'recaptcha'}
<div class="form-group">
<div class="form-group antibot">
{if $captchalib->type eq 'recaptcha' || $captchalib->type eq 'recaptcha20'}
<div class="form-group clearfix">
<div class="{$captchaclass}">
{$captchalib->render()}
</div>
......@@ -59,48 +59,56 @@
{/if}
</div>
{/if}
{jq}
if($("#antibotcode").parents('form').data("validator")) {
$( "#antibotcode" ).rules( "add", {
required: true,
remote: {
url: "validate-ajax.php",
type: "post",
data: {
validator: "captcha",
parameter: function() {
return $("#captchaId").val();
},
input: function() {
return $("#antibotcode").val();
}
}
function antibotVerification(element, rule) {
if (!jqueryTiki.validate) return;
var form = $(".antibot").parents('form');
if (!form.data("validator")) {
form.validate({});
}
});
} else if (jqueryTiki.validate) {
$("#antibotcode").parents('form').validate({
rules: {
"captcha[input]": {
required: true,
remote: {
url: "validate-ajax.php",
type: "post",
data: {
validator: "captcha",
parameter: function() {
return $("#captchaId").val();
},
input: function() {
return $("#antibotcode").val();
}
element.rules( "add", rule);
}
{/jq}
{if $captchalib->type eq 'recaptcha'}
{jq}
var existCondition = setInterval(function() {
if ($('#recaptcha_response_field').length) {
clearInterval(existCondition);
antibotVerification($("#recaptcha_response_field"), {required: true});
}
}, 100); // wait for captcha to load
{/jq}
{elseif $captchalib->type eq 'recaptcha20'}
{jq}
var existCondition = setInterval(function() {
if ($('#g-recaptcha-response').length) {
clearInterval(existCondition);
antibotVerification($("#g-recaptcha-response"), {required: true});
}
}, 100); // wait for captcha to load
{/jq}
{else}
{jq}
antibotVerification($("#antibotcode"), {
required: true,
remote: {
url: "validate-ajax.php",
type: "post",
data: {
validator: "captcha",
parameter: function() {
return $("#captchaId").val();
},
input: function() {
return $("#antibotcode").val();
}
}
}
},
messages: {
"captcha[input]": { required: "This field is required"}
},
submitHandler: function(){form.submit();}
});
}
{/jq}
});
{/jq}
{/if}
......@@ -246,7 +246,11 @@ if ($prefs['feature_wysiwyg'] == 'y') {
if ($prefs['feature_antibot'] == 'y' && empty($user)) {
if ($prefs['recaptcha_enabled'] === 'y') {
$headerlib->add_jsfile_cdn("$url_scheme://www.google.com/recaptcha/api/js/recaptcha_ajax.js");
if ($prefs['recaptcha_version'] == '2') {
$headerlib->add_jsfile_cdn("$url_scheme://www.google.com/recaptcha/api.js");
} else {
$headerlib->add_jsfile_cdn("$url_scheme://www.google.com/recaptcha/api/js/recaptcha_ajax.js");
}
}
$captchalib = TikiLib::lib('captcha');
$smarty->assign('captchalib', $captchalib);
......
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