diff --git a/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini b/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini index 6ba182347e565..326d46f199fa3 100644 --- a/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini +++ b/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini @@ -3,47 +3,37 @@ ; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php ; Note : All ini files need to be saved as UTF-8 -PLG_CAPTCHA_RECAPTCHA_XML_DESCRIPTION="This CAPTCHA plugin uses the reCAPTCHA service to prevent spammers while it helps to digitize books, newspapers and old radio shows. To get a public and private key for your domain, go to http://www.google.com/recaptcha. To use this for new account registration, go to Options in the User Manager and select Captcha - reCaptcha as the Captcha." +PLG_CAPTCHA_RECAPTCHA_XML_DESCRIPTION="This CAPTCHA plugin uses the reCAPTCHA service to prevent spammers while it helps to digitize books, newspapers and old radio shows. To get a site and secret key for your domain, go to http://www.google.com/recaptcha. To use this for new account registration, go to Options in the User Manager and select Captcha - reCaptcha as the Captcha." PLG_CAPTCHA_RECAPTCHA="Captcha - ReCaptcha" ; Params -PLG_RECAPTCHA_PUBLIC_KEY_LABEL="Public Key" -PLG_RECAPTCHA_PUBLIC_KEY_DESC="Used in the JavaScript code that is served to your users. See the plugin description for instructions on getting a public key." -PLG_RECAPTCHA_PRIVATE_KEY_LABEL="Private Key" -PLG_RECAPTCHA_PRIVATE_KEY_DESC="Used in the communication between your server and the ReCaptha server. Be sure to keep it a secret. See the plugin description for instructions on getting a private key." +PLG_RECAPTCHA_PUBLIC_KEY_LABEL="Site key" +PLG_RECAPTCHA_PUBLIC_KEY_DESC="Used in the JavaScript code that is served to your users. See the plugin description for instructions on getting a site key." +PLG_RECAPTCHA_PRIVATE_KEY_LABEL="Secret key" +PLG_RECAPTCHA_PRIVATE_KEY_DESC="Used in the communication between your server and the ReCaptha server. Be sure to keep it a secret. See the plugin description for instructions on getting a secret key." PLG_RECAPTCHA_THEME_LABEL="Theme" PLG_RECAPTCHA_THEME_DESC="Defines which theme to use for reCAPTCHA." +PLG_RECAPTCHA_THEME_LIGHT="Light" +PLG_RECAPTCHA_THEME_DARK="Dark" + +; Error messages +PLG_RECAPTCHA_ERROR_NO_PRIVATE_KEY="ReCaptcha plugin needs a secret key to be set in its parameters. Please contact a site administrator." +PLG_RECAPTCHA_ERROR_NO_PUBLIC_KEY="ReCaptcha plugin needs a site key to be set in its parameters. Please contact a site administrator." +PLG_RECAPTCHA_ERROR_NOT_SET="Please complete the security question." +PLG_RECAPTCHA_ERROR_NO_IP="For security reasons, you must pass the remote ip address to reCAPTCHA" + +; Deprecated: No longer used PLG_RECAPTCHA_THEME_RED="Red" PLG_RECAPTCHA_THEME_WHITE="White" PLG_RECAPTCHA_THEME_BLACKGLASS="BlackGlass" PLG_RECAPTCHA_THEME_CLEAN="Clean" PLG_RECAPTCHA_LANG_LABEL="Language" PLG_RECAPTCHA_LANG_DESC="Select the language for the reCAPTCHA. If default is set and the language file has a custom translation, it will be used." - -; Error messages -PLG_RECAPTCHA_ERROR_NO_PRIVATE_KEY="ReCaptcha plugin needs a private key to be set in its parameters. Please contact a site administrator." -PLG_RECAPTCHA_ERROR_NO_PUBLIC_KEY="ReCaptcha plugin needs a public key to be set in its parameters. Please contact a site administrator." PLG_RECAPTCHA_ERROR_EMPTY_SOLUTION="Empty solution not allowed." -PLG_RECAPTCHA_ERROR_NO_IP="For security reasons, you must pass the remote ip address to reCAPTCHA" -PLG_RECAPTCHA_ERROR_UNKNOWN="Unknown error." -PLG_RECAPTCHA_ERROR_INVALID_SITE_PUBLIC_KEY="We weren't able to verify the public key." -PLG_RECAPTCHA_ERROR_INVALID_SITE_PRIVATE_KEY="We weren't able to verify the private key." +PLG_RECAPTCHA_ERROR_INVALID_SITE_PUBLIC_KEY="We weren't able to verify the site key." +PLG_RECAPTCHA_ERROR_INVALID_SITE_PRIVATE_KEY="We weren't able to verify the secret key." PLG_RECAPTCHA_ERROR_INVALID_REQUEST_COOKIE="The challenge parameter of the verify script was incorrect." PLG_RECAPTCHA_ERROR_INCORRECT_CAPTCHA_SOL="The CAPTCHA solution was incorrect." PLG_RECAPTCHA_ERROR_VERIFY_PARAMS_INCORRECT="The parameters to verify were incorrect, make sure you are passing all the required parameters." PLG_RECAPTCHA_ERROR_INVALID_REFERRER="reCAPTCHA API keys are tied to a specific domain name for security reasons." PLG_RECAPTCHA_ERROR_RECAPTCHA_NOT_REACHABLE="Unable to contact the reCAPTCHA verify server." - -; Uncomment(remove the ";" from the beginning of the line) the following lines if reCAPTCHA is not available in your language -; When uncommenting, do NOT translate PLG_RECAPTCHA_CUSTOM_LANG -; As of 01/01/2012, the following languages do not need translation: en, nl, fr, de, pt, ru, es, tr -;PLG_RECAPTCHA_CUSTOM_LANG="true" -;PLG_RECAPTCHA_INSTRUCTIONS_VISUAL="Type the two words:" -;PLG_RECAPTCHA_INSTRUCTIONS_AUDIO="Type what you hear:" -;PLG_RECAPTCHA_PLAY_AGAIN="Play sound again" -;PLG_RECAPTCHA_CANT_HEAR_THIS="Download sound as MP3" -;PLG_RECAPTCHA_VISUAL_CHALLENGE="Get a visual challenge" -;PLG_RECAPTCHA_AUDIO_CHALLENGE="Get an audio challenge" -;PLG_RECAPTCHA_REFRESH_BTN="Get a new challenge" -;PLG_RECAPTCHA_HELP_BTN="Help" -;PLG_RECAPTCHA_INCORRECT_TRY_AGAIN="Incorrect. Try again." diff --git a/plugins/captcha/recaptcha/recaptcha.php b/plugins/captcha/recaptcha/recaptcha.php index 65d33f9e28da9..d3c54238ff31f 100644 --- a/plugins/captcha/recaptcha/recaptcha.php +++ b/plugins/captcha/recaptcha/recaptcha.php @@ -17,10 +17,6 @@ */ class PlgCaptchaRecaptcha extends JPlugin { - const RECAPTCHA_API_SERVER = "http://www.google.com/recaptcha/api"; - const RECAPTCHA_API_SECURE_SERVER = "https://www.google.com/recaptcha/api"; - const RECAPTCHA_VERIFY_SERVER = "www.google.com"; - /** * Load the language file on instantiation. * @@ -34,37 +30,33 @@ class PlgCaptchaRecaptcha extends JPlugin * * @param string $id The id of the field. * - * @return Boolean True on success, false otherwise + * @return Boolean True on success, false otherwise * * @since 2.5 */ public function onInit($id = 'dynamic_recaptcha_1') { $document = JFactory::getDocument(); - $app = JFactory::getApplication(); + $app = JFactory::getApplication(); JHtml::_('jquery.framework'); - $lang = $this->_getLanguage(); - $pubkey = $this->params->get('public_key', ''); - $theme = $this->params->get('theme', 'clean'); + $public_key = $this->params->get('public_key', ''); + $theme = $this->params->get('theme', 'light'); - if ($pubkey == null || $pubkey == '') + if ($public_key == null || $public_key == '') { throw new Exception(JText::_('PLG_RECAPTCHA_ERROR_NO_PUBLIC_KEY')); } - $server = self::RECAPTCHA_API_SERVER; + $file = $app->isSSLConnection() ? 'https' : 'http'; + $file .= '://www.google.com/recaptcha/api.js?hl=' . JFactory::getLanguage()->getTag() . '&onload=onloadCallback&render=explicit'; - if ($app->isSSLConnection()) - { - $server = self::RECAPTCHA_API_SECURE_SERVER; - } + JHtml::_('script', $file, true, true); - JHtml::_('script', $server . '/js/recaptcha_ajax.js'); - $document->addScriptDeclaration('jQuery( document ).ready(function() - { - Recaptcha.create("' . $pubkey . '", "' . $id . '", {theme: "' . $theme . '",' . $lang . 'tabindex: 0});});' + $document->addScriptDeclaration('var onloadCallback = function() {' + . 'grecaptcha.render("' . $id . '", {sitekey: "' . $public_key . '", theme: "' . $theme . '"});' + . '}' ); return true; @@ -98,11 +90,10 @@ public function onDisplay($name, $id = 'dynamic_recaptcha_1', $class = '') */ public function onCheckAnswer($code) { - $input = JFactory::getApplication()->input; + $input = JFactory::getApplication()->input; $privatekey = $this->params->get('private_key'); - $remoteip = $input->server->get('REMOTE_ADDR', '', 'string'); - $challenge = $input->get('recaptcha_challenge_field', '', 'string'); - $response = $input->get('recaptcha_response_field', '', 'string'); + $remoteip = $input->server->get('REMOTE_ADDR', '', 'string'); + $response = $input->get('g-recaptcha-response', '', 'string'); // Check for Private Key if (empty($privatekey)) @@ -121,147 +112,28 @@ public function onCheckAnswer($code) } // Discard spam submissions - if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) + if ($response == null || strlen($response) == 0) { $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_EMPTY_SOLUTION')); return false; } - $response = $this->_recaptcha_http_post( - self::RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", - array( - 'privatekey' => $privatekey, - 'remoteip' => $remoteip, - 'challenge' => $challenge, - 'response' => $response - ) - ); + require_once 'recaptchalib.php'; + $reCaptcha = new ReCaptcha($privatekey); + $response = $reCaptcha->verifyResponse($remoteip, $response); - $answers = explode("\n", $response[1]); - - if (trim($answers[0]) == 'true') - { - return true; - } - else + if (!isset($response->success) || !$response->success) { // @todo use exceptions here - $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_' . strtoupper(str_replace('-', '_', $answers[1])))); + foreach ($response->errorCodes as $error) + { + $this->_subject->setError($error); + } return false; } - } - - /** - * Encodes the given data into a query string format. - * - * @param array $data Array of string elements to be encoded - * - * @return string Encoded request - * - * @since 2.5 - */ - private function _recaptcha_qsencode($data) - { - $req = ""; - - foreach ($data as $key => $value) - { - $req .= $key . '=' . urlencode(stripslashes($value)) . '&'; - } - - // Cut the last '&' - $req = rtrim($req, '&'); - - return $req; - } - /** - * Submits an HTTP POST to a reCAPTCHA server. - * - * @param string $host Host name to POST to. - * @param string $path Path on host to POST to. - * @param array $data Data to be POSTed. - * @param int $port Optional port number on host. - * - * @return array Response - * - * @since 2.5 - */ - private function _recaptcha_http_post($host, $path, $data, $port = 80) - { - $req = $this->_recaptcha_qsencode($data); - - $http_request = "POST $path HTTP/1.0\r\n"; - $http_request .= "Host: $host\r\n"; - $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; - $http_request .= "Content-Length: " . strlen($req) . "\r\n"; - $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; - $http_request .= "\r\n"; - $http_request .= $req; - - $response = ''; - - if (($fs = @fsockopen($host, $port, $errno, $errstr, 10)) == false ) - { - die('Could not open socket'); - } - - fwrite($fs, $http_request); - - while (!feof($fs)) - { - // One TCP-IP packet - $response .= fgets($fs, 1160); - } - - fclose($fs); - $response = explode("\r\n\r\n", $response, 2); - - return $response; - } - - /** - * Get the language tag or a custom translation - * - * @return string - * - * @since 2.5 - */ - private function _getLanguage() - { - $language = JFactory::getLanguage(); - - $tag = explode('-', $language->getTag()); - $tag = $tag[0]; - $available = array('en', 'pt', 'fr', 'de', 'nl', 'ru', 'es', 'tr'); - - if (in_array($tag, $available)) - { - return "lang : '" . $tag . "',"; - } - - // If the default language is not available, let's search for a custom translation - if ($language->hasKey('PLG_RECAPTCHA_CUSTOM_LANG')) - { - $custom[] = 'custom_translations : {'; - $custom[] = "\t" . 'instructions_visual : "' . JText::_('PLG_RECAPTCHA_INSTRUCTIONS_VISUAL') . '",'; - $custom[] = "\t" . 'instructions_audio : "' . JText::_('PLG_RECAPTCHA_INSTRUCTIONS_AUDIO') . '",'; - $custom[] = "\t" . 'play_again : "' . JText::_('PLG_RECAPTCHA_PLAY_AGAIN') . '",'; - $custom[] = "\t" . 'cant_hear_this : "' . JText::_('PLG_RECAPTCHA_CANT_HEAR_THIS') . '",'; - $custom[] = "\t" . 'visual_challenge : "' . JText::_('PLG_RECAPTCHA_VISUAL_CHALLENGE') . '",'; - $custom[] = "\t" . 'audio_challenge : "' . JText::_('PLG_RECAPTCHA_AUDIO_CHALLENGE') . '",'; - $custom[] = "\t" . 'refresh_btn : "' . JText::_('PLG_RECAPTCHA_REFRESH_BTN') . '",'; - $custom[] = "\t" . 'help_btn : "' . JText::_('PLG_RECAPTCHA_HELP_BTN') . '",'; - $custom[] = "\t" . 'incorrect_try_again : "' . JText::_('PLG_RECAPTCHA_INCORRECT_TRY_AGAIN') . '",'; - $custom[] = '},'; - $custom[] = "lang : '" . $tag . "',"; - - return implode("\n", $custom); - } - - // If nothing helps fall back to english - return ''; + return true; } } diff --git a/plugins/captcha/recaptcha/recaptcha.xml b/plugins/captcha/recaptcha/recaptcha.xml index 2cda724bdb391..9f84145355ac4 100644 --- a/plugins/captcha/recaptcha/recaptcha.xml +++ b/plugins/captcha/recaptcha/recaptcha.xml @@ -39,19 +39,15 @@ + value="light">PLG_RECAPTCHA_THEME_LIGHT - - + value="dark">PLG_RECAPTCHA_THEME_DARK diff --git a/plugins/captcha/recaptcha/recaptchalib.php b/plugins/captcha/recaptcha/recaptchalib.php new file mode 100644 index 0000000000000..dc0c526c15745 --- /dev/null +++ b/plugins/captcha/recaptcha/recaptchalib.php @@ -0,0 +1,146 @@ +" . self::$_signupUrl . ""); + } + $this->_secret = $secret; + } + + /** + * Encodes the given data into a query string format. + * + * @param array $data array of string elements to be encoded. + * + * @return string - encoded request. + */ + private function _encodeQS($data) + { + $req = ""; + foreach ($data as $key => $value) + { + $req .= $key . '=' . urlencode(stripslashes($value)) . '&'; + } + + // Cut the last '&' + $req = substr($req, 0, strlen($req) - 1); + + return $req; + } + + /** + * Submits an HTTP GET to a reCAPTCHA server. + * + * @param string $path url path to recaptcha server. + * @param array $data array of parameters to be sent. + * + * @return array response + */ + private function _submitHTTPGet($path, $data) + { + $req = $this->_encodeQS($data); + $response = file_get_contents($path . $req); + + return $response; + } + + /** + * Calls the reCAPTCHA siteverify API to verify whether the user passes + * CAPTCHA test. + * + * @param string $remoteIp IP address of end user. + * @param string $response response string from recaptcha verification. + * + * @return ReCaptchaResponse + */ + public function verifyResponse($remoteIp, $response) + { + // Discard empty solution submissions + if ($response == null || strlen($response) == 0) + { + $recaptchaResponse = new ReCaptchaResponse(); + $recaptchaResponse->success = false; + $recaptchaResponse->errorCodes = 'missing-input'; + + return $recaptchaResponse; + } + + $getResponse = $this->_submitHttpGet( + self::$_siteVerifyUrl, + array( + 'secret' => $this->_secret, + 'remoteip' => $remoteIp, + 'v' => self::$_version, + 'response' => $response + ) + ); + $answers = json_decode($getResponse, true); + $recaptchaResponse = new ReCaptchaResponse(); + + if (trim($answers['success']) == true) + { + $recaptchaResponse->success = true; + } + else + { + $recaptchaResponse->success = false; + $recaptchaResponse->errorCodes = isset($answers['error-codes']) ? $answers['error-codes'] : ''; + } + + return $recaptchaResponse; + } +}