Merge branch 'MDL-72353' of https://github.com/paulholden/moodle
[moodle.git] / lib / recaptchalib_v2.php
blob1de8f176b28475940a368ca74f5284854aa16d29
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
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.
8 //
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/>.
31 /**
32 * This is a PHP library that handles calling reCAPTCHA v2.
34 * - Documentation
35 * {@link https://developers.google.com/recaptcha/docs/display}
36 * - Get a reCAPTCHA API Key
37 * {@link https://www.google.com/recaptcha/admin}
38 * - Discussion group
39 * {@link http://groups.google.com/group/recaptcha}
41 * @package core
42 * @copyright 2018 Jeff Webster
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46 defined('MOODLE_INTERNAL') || die();
48 /**
49 * The reCAPTCHA URL's
51 define('RECAPTCHA_API_URL', 'https://www.recaptcha.net/recaptcha/api.js');
52 define('RECAPTCHA_VERIFY_URL', 'https://www.recaptcha.net/recaptcha/api/siteverify');
54 /**
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) {
64 if (empty($lang)) {
65 $lang = current_language();
68 $glang = $lang;
69 switch ($glang) {
70 case 'en':
71 $glang = 'en-GB';
72 break;
73 case 'en_us':
74 $glang = 'en';
75 break;
76 case 'zh_cn':
77 $glang = 'zh-CN';
78 break;
79 case 'zh_tw':
80 $glang = 'zh-TW';
81 break;
82 case 'fr_ca':
83 $glang = 'fr-CA';
84 break;
85 case 'pt_br':
86 $glang = 'pt-BR';
87 break;
88 case 'he':
89 $glang = 'iw';
90 break;
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);
96 return $glang;
99 /**
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) {
111 global $CFG, $PAGE;
113 // To use reCAPTCHA you must have an API key.
114 if ($pubkey === null || $pubkey === '') {
115 return get_string('getrecaptchaapi', 'auth');
118 $jscode = "
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'),
135 $return .= $apicode;
137 return $return;
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
147 * @return array
149 function recaptcha_check_response($verifyurl, $privkey, $remoteip, $response) {
150 global $CFG;
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);
178 $curl = new curl();
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'] = '';
187 } else {
188 $checkresponse['isvalid'] = false;
189 $checkresponse['error'] = $curldata->{'error-codes'};
191 } else {
192 $checkresponse['isvalid'] = false;
193 $checkresponse['error'] = 'check-failed';
195 return $checkresponse;