2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16 // This file is part of Moodle - http://moodle.org/
18 // Moodle is free software: you can redistribute it and/or modify
19 // it under the terms of the GNU General Public License as published by
20 // the Free Software Foundation, either version 3 of the License, or
21 // (at your option) any later version.
23 // Moodle is distributed in the hope that it will be useful,
24 // but WITHOUT ANY WARRANTY; without even the implied warranty of
25 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 // GNU General Public License for more details.
28 // You should have received a copy of the GNU General Public License
29 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
32 * This is a PHP library that handles calling reCAPTCHA v2.
35 * {@link https://developers.google.com/recaptcha/docs/display}
36 * - Get a reCAPTCHA API Key
37 * {@link https://www.google.com/recaptcha/admin}
39 * {@link http://groups.google.com/group/recaptcha}
42 * @copyright 2018 Jeff Webster
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46 defined('MOODLE_INTERNAL') ||
die();
51 define('RECAPTCHA_API_URL', 'https://www.recaptcha.net/recaptcha/api.js');
52 define('RECAPTCHA_VERIFY_URL', 'https://www.recaptcha.net/recaptcha/api/siteverify');
55 * Returns the language code the reCAPTCHA element should use.
56 * Google reCAPTCHA uses different language codes than Moodle so we must convert.
57 * https://developers.google.com/recaptcha/docs/language
59 * @param string $lang Language to use. If not provided, get current language.
60 * @return string A language code
62 function recaptcha_lang($lang = null) {
65 $lang = current_language();
92 // For any language code that didn't change reduce down to the base language.
93 if (($lang === $glang) and (strpos($lang, '_') !== false)) {
94 list($glang, $trash) = explode('_', $lang, 2);
100 * Gets the challenge HTML
101 * This is called from the browser, and the resulting reCAPTCHA HTML widget
102 * is embedded within the HTML form it was called from.
104 * @param string $apiurl URL for reCAPTCHA API
105 * @param string $pubkey The public key for reCAPTCHA
106 * @param string $lang Language to use. If not provided, get current language.
107 * @param bool $compactmode If true, use the compact widget.
108 * @return string - The HTML to be embedded in the user's form.
110 function recaptcha_get_challenge_html($apiurl, $pubkey, $lang = null, bool $compactmode = false) {
113 // To use reCAPTCHA you must have an API key.
114 if ($pubkey === null ||
$pubkey === '') {
115 return get_string('getrecaptchaapi', 'auth');
119 var recaptchacallback = function() {
120 grecaptcha.render('recaptcha_element', {
121 'sitekey' : '$pubkey'
125 $lang = recaptcha_lang($lang);
126 $apicode = "\n<script type=\"text/javascript\" ";
127 $apicode .= "src=\"$apiurl?onload=recaptchacallback&render=explicit&hl=$lang\" async defer>";
128 $apicode .= "</script>\n";
130 $return = html_writer
::script($jscode, '');
131 $return .= html_writer
::div('', 'recaptcha_element', [
132 'id' => 'recaptcha_element',
133 'data-size' => ($compactmode ?
'compact' : 'normal'),
141 * Calls an HTTP POST function to verify if the user's response was correct
143 * @param string $verifyurl URL for reCAPTCHA verification
144 * @param string $privkey The private key for reCAPTCHA
145 * @param string $remoteip The user's IP
146 * @param string $response The response from reCAPTCHA
149 function recaptcha_check_response($verifyurl, $privkey, $remoteip, $response) {
151 require_once($CFG->libdir
.'/filelib.php');
153 // Check response - isvalid boolean, error string.
154 $checkresponse = array('isvalid' => false, 'error' => 'check-not-started');
156 // To use reCAPTCHA you must have an API key.
157 if ($privkey === null ||
$privkey === '') {
158 $checkresponse['isvalid'] = false;
159 $checkresponse['error'] = 'no-apikey';
160 return $checkresponse;
163 // For security reasons, you must pass the remote ip to reCAPTCHA.
164 if ($remoteip === null ||
$remoteip === '') {
165 $checkresponse['isvalid'] = false;
166 $checkresponse['error'] = 'no-remoteip';
167 return $checkresponse;
170 // Discard spam submissions.
171 if ($response === null ||
strlen($response) === 0) {
172 $checkresponse['isvalid'] = false;
173 $checkresponse['error'] = 'incorrect-captcha-sol';
174 return $checkresponse;
177 $params = array('secret' => $privkey, 'remoteip' => $remoteip, 'response' => $response);
179 $curlresponse = $curl->post($verifyurl, $params);
181 if ($curl->get_errno() === 0) {
182 $curldata = json_decode($curlresponse);
184 if (isset($curldata->success
) && $curldata->success
=== true) {
185 $checkresponse['isvalid'] = true;
186 $checkresponse['error'] = '';
188 $checkresponse['isvalid'] = false;
189 $checkresponse['error'] = $curldata->{'error-codes'};
192 $checkresponse['isvalid'] = false;
193 $checkresponse['error'] = 'check-failed';
195 return $checkresponse;