diff --git a/composer.json b/composer.json
index 5a748f9b2e6a0..e2419041038a1 100644
--- a/composer.json
+++ b/composer.json
@@ -43,7 +43,8 @@
"symfony/polyfill-php56": "~1.0",
"symfony/polyfill-php73": "~1.8",
"symfony/yaml": "2.*",
- "simplepie/simplepie": "1.3.1"
+ "simplepie/simplepie": "1.3.1",
+ "google/recaptcha": "^1.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35",
diff --git a/composer.lock b/composer.lock
index 819e73ffc7b99..4a5b222a3f24d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,53 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "df5cd1e4b9c48fd4beea1f87832cd250",
+ "content-hash": "c3f089d81a6bd685e0b37b4483597f20",
"packages": [
+ {
+ "name": "google/recaptcha",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/google/recaptcha.git",
+ "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/google/recaptcha/zipball/2b7e00566afca82a38a1d3adb8e42c118006296e",
+ "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.5.*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "ReCaptcha\\": "src/ReCaptcha"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.",
+ "homepage": "http://www.google.com/recaptcha/",
+ "keywords": [
+ "Abuse",
+ "captcha",
+ "recaptcha",
+ "spam"
+ ],
+ "time": "2015-09-02T17:23:59+00:00"
+ },
{
"name": "ircmaxell/password-compat",
"version": "v1.0.4",
diff --git a/installation/sql/mysql/joomla.sql b/installation/sql/mysql/joomla.sql
index 9599b09ae3234..0f7a2d2beb7d0 100644
--- a/installation/sql/mysql/joomla.sql
+++ b/installation/sql/mysql/joomla.sql
@@ -666,6 +666,7 @@ INSERT INTO `#__extensions` (`extension_id`, `package_id`, `name`, `type`, `elem
(491, 0, 'plg_privacy_content', 'plugin', 'content', 'privacy', 0, 1, 1, 0, '', '{}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(492, 0, 'plg_privacy_message', 'plugin', 'message', 'privacy', 0, 1, 1, 0, '', '{}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(493, 0, 'plg_privacy_actionlogs', 'plugin', 'actionlogs', 'privacy', 0, 0, 1, 0, '', '{}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
+(494, 0, 'plg_captcha_recaptcha_invisible', 'plugin', 'recaptcha_invisible', 'captcha', 0, 0, 1, 0, '', '{"public_key":"","private_key":"","theme":"clean"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(506, 0, 'protostar', 'template', 'protostar', '', 0, 1, 1, 0, '', '{"templateColor":"","logoFile":"","googleFont":"1","googleFontName":"Open+Sans","fluidContainer":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
diff --git a/installation/sql/postgresql/joomla.sql b/installation/sql/postgresql/joomla.sql
index 4c86112162db1..08e2c85cc593e 100644
--- a/installation/sql/postgresql/joomla.sql
+++ b/installation/sql/postgresql/joomla.sql
@@ -679,6 +679,7 @@ INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "elem
(491, 0, 'plg_privacy_content', 'plugin', 'content', 'privacy', 0, 1, 1, 0, '', '{}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(492, 0, 'plg_privacy_message', 'plugin', 'message', 'privacy', 0, 1, 1, 0, '', '{}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(493, 0, 'plg_privacy_actionlogs', 'plugin', 'actionlogs', 'privacy', 0, 0, 1, 0, '', '{}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
+(494, 0, 'plg_captcha_recaptcha_invisible', 'plugin', 'recaptcha_invisible', 'captcha', 0, 0, 1, 0, '', '{"public_key":"","private_key":"","theme":"clean"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(506, 0, 'protostar', 'template', 'protostar', '', 0, 1, 1, 0, '', '{"templateColor":"","logoFile":"","googleFont":"1","googleFontName":"Open+Sans","fluidContainer":"0"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
diff --git a/installation/sql/sqlazure/joomla.sql b/installation/sql/sqlazure/joomla.sql
index 60bae0aeebbc6..833e070fc49aa 100644
--- a/installation/sql/sqlazure/joomla.sql
+++ b/installation/sql/sqlazure/joomla.sql
@@ -894,6 +894,7 @@ INSERT INTO "#__extensions" ("extension_id", "package_id", "name", "type", "elem
(491, 0, 'plg_privacy_content', 'plugin', 'user', 'content', 0, 1, 1, 0, '', '{}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(492, 0, 'plg_privacy_message', 'plugin', 'user', 'message', 0, 1, 1, 0, '', '{}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(493, 0, 'plg_privacy_actionlogs', 'plugin', 'actionlogs', 'privacy', 0, 0, 1, 0, '', '{}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
+(494, 0, 'plg_captcha_recaptcha_invisible', 'plugin', 'recaptcha_invisible', 'captcha', 0, 0, 1, 0, '', '{"public_key":"","private_key":"","theme":"clean"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(503, 0, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(504, 0, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
(506, 0, 'protostar', 'template', 'protostar', '', 0, 1, 1, 0, '', '{"templateColor":"","logoFile":"","googleFont":"1","googleFontName":"Open+Sans","fluidContainer":"0"}', '', '', 0, '1900-01-01 00:00:00', 0, 0),
diff --git a/libraries/src/Captcha/Captcha.php b/libraries/src/Captcha/Captcha.php
index 396efad21be5d..293e071af97b8 100644
--- a/libraries/src/Captcha/Captcha.php
+++ b/libraries/src/Captcha/Captcha.php
@@ -76,10 +76,11 @@ class Captcha extends \JObject
/**
* Class constructor.
*
- * @param string $captcha The editor to use.
+ * @param string $captcha The plugin to use.
* @param array $options Associative array of options.
*
* @since 2.5
+ * @throws \RuntimeException
*/
public function __construct($captcha, $options)
{
@@ -97,6 +98,7 @@ public function __construct($captcha, $options)
* @return Captcha|null Instance of this class.
*
* @since 2.5
+ * @throws \RuntimeException
*/
public static function getInstance($captcha, array $options = array())
{
@@ -104,16 +106,7 @@ public static function getInstance($captcha, array $options = array())
if (empty(self::$_instances[$signature]))
{
- try
- {
- self::$_instances[$signature] = new Captcha($captcha, $options);
- }
- catch (\RuntimeException $e)
- {
- \JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
-
- return;
- }
+ self::$_instances[$signature] = new Captcha($captcha, $options);
}
return self::$_instances[$signature];
@@ -127,22 +120,14 @@ public static function getInstance($captcha, array $options = array())
* @return boolean True on success
*
* @since 2.5
+ * @throws \RuntimeException
*/
public function initialise($id)
{
$args['id'] = $id;
$args['event'] = 'onInit';
- try
- {
- $this->_captcha->update($args);
- }
- catch (\Exception $e)
- {
- \JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
-
- return false;
- }
+ $this->_captcha->update($args);
return true;
}
@@ -157,6 +142,7 @@ public function initialise($id)
* @return mixed The return value of the function "onDisplay" of the selected Plugin.
*
* @since 2.5
+ * @throws \RuntimeException
*/
public function display($name, $id, $class = '')
{
@@ -174,7 +160,7 @@ public function display($name, $id, $class = '')
$args['name'] = $name;
$args['id'] = $id ?: $name;
- $args['class'] = $class ? 'class="' . $class . '"' : '';
+ $args['class'] = $class;
$args['event'] = 'onDisplay';
return $this->_captcha->update($args);
@@ -185,9 +171,10 @@ public function display($name, $id, $class = '')
*
* @param string $code The answer.
*
- * @return mixed The return value of the function "onCheckAnswer" of the selected Plugin.
+ * @return bool Whether the provided answer was correct
*
* @since 2.5
+ * @throws \RuntimeException
*/
public function checkAnswer($code)
{
@@ -203,6 +190,32 @@ public function checkAnswer($code)
return $this->_captcha->update($args);
}
+ /**
+ * Method to react on the setup of a captcha field. Gives the possibility
+ * to change the field and/or the XML element for the field.
+ *
+ * @param \Joomla\CMS\Form\Field\CaptchaField $field Captcha field instance
+ * @param \SimpleXMLElement $element XML form definition
+ *
+ * @return void
+ */
+ public function setupField(\Joomla\CMS\Form\Field\CaptchaField $field, \SimpleXMLElement $element)
+ {
+ if ($this->_captcha === null)
+ {
+ return;
+ }
+
+ $args = array(
+ 'event' => 'onSetupField',
+ 'field' => $field,
+ 'element' => $element,
+ );
+
+ // Forward to the captcha plugin
+ return $this->_captcha->update($args);
+ }
+
/**
* Load the Captcha plugin.
*
diff --git a/libraries/src/Captcha/Google/HttpBridgePostRequestMethod.php b/libraries/src/Captcha/Google/HttpBridgePostRequestMethod.php
new file mode 100644
index 0000000000000..057c7666c0046
--- /dev/null
+++ b/libraries/src/Captcha/Google/HttpBridgePostRequestMethod.php
@@ -0,0 +1,77 @@
+http = $http ?: HttpFactory::getHttp();
+ }
+
+ /**
+ * Submit the request with the specified parameters.
+ *
+ * @param RequestParameters $params Request parameters
+ *
+ * @return string Body of the reCAPTCHA response
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function submit(RequestParameters $params)
+ {
+ try
+ {
+ $response = $this->http->post(self::SITE_VERIFY_URL, $params->toArray());
+
+ return (string) $response->body;
+ }
+ catch (InvalidResponseCodeException $exception)
+ {
+ return '';
+ }
+ }
+}
+
diff --git a/libraries/src/Form/Field/CaptchaField.php b/libraries/src/Form/Field/CaptchaField.php
index 9e0646c18a4a2..45bc06eea712a 100644
--- a/libraries/src/Form/Field/CaptchaField.php
+++ b/libraries/src/Form/Field/CaptchaField.php
@@ -29,6 +29,13 @@ class CaptchaField extends FormField
*/
protected $type = 'Captcha';
+ /**
+ * The captcha base instance of our type.
+ *
+ * @var Captcha
+ */
+ protected $_captcha;
+
/**
* Method to get certain otherwise inaccessible properties from the form field object.
*
@@ -124,6 +131,25 @@ public function setup(\SimpleXMLElement $element, $value, $group = null)
$this->namespace = $this->element['namespace'] ? (string) $this->element['namespace'] : $this->form->getName();
+ try
+ {
+ // Get an instance of the captcha class that we are using
+ $this->_captcha = Captcha::getInstance($this->plugin, array('namespace' => $this->namespace));
+
+ /**
+ * Give the captcha instance a possibility to react on the setup-process,
+ * e.g. by altering the XML structure of the field, for example hiding the label
+ * when using invisible captchas.
+ */
+ $this->_captcha->setupField($this, $element);
+ }
+ catch (\RuntimeException $e)
+ {
+ $this->_captcha = null;
+ \JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
+ return false;
+ }
+
return $result;
}
@@ -136,18 +162,19 @@ public function setup(\SimpleXMLElement $element, $value, $group = null)
*/
protected function getInput()
{
- if ($this->hidden)
+ if ($this->hidden || $this->_captcha == null)
{
return '';
}
- else
+
+ try
{
- if (($captcha = Captcha::getInstance($this->plugin, array('namespace' => $this->namespace))) == null)
- {
- return '';
- }
+ return $this->_captcha->display($this->name, $this->id, $this->class);
}
-
- return $captcha->display($this->name, $this->id, $this->class);
+ catch (\RuntimeException $e)
+ {
+ \JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
+ }
+ return '';
}
}
diff --git a/libraries/src/Form/Rule/CaptchaRule.php b/libraries/src/Form/Rule/CaptchaRule.php
index 8de64ab3d282b..65d0f40577770 100644
--- a/libraries/src/Form/Rule/CaptchaRule.php
+++ b/libraries/src/Form/Rule/CaptchaRule.php
@@ -54,26 +54,16 @@ public function test(\SimpleXMLElement $element, $value, $group = null, Registry
{
return true;
}
- else
+
+ try
{
$captcha = Captcha::getInstance((string) $plugin, array('namespace' => (string) $namespace));
+ return $captcha->checkAnswer($value);
}
-
- // Test the value.
- if (!$captcha->checkAnswer($value))
+ catch (\RuntimeException $e)
{
- $error = $captcha->getError();
-
- if ($error instanceof \Exception)
- {
- return $error;
- }
- else
- {
- return new \JException($error);
- }
+ \JFactory::getApplication()->enqueueMessage($e->getMessage(), 'error');
}
-
- return true;
+ return false;
}
}
diff --git a/libraries/vendor/composer/autoload_classmap.php b/libraries/vendor/composer/autoload_classmap.php
index 23fd5b4d8497f..ac744dee23b42 100644
--- a/libraries/vendor/composer/autoload_classmap.php
+++ b/libraries/vendor/composer/autoload_classmap.php
@@ -123,6 +123,15 @@
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
+ 'ReCaptcha\\ReCaptcha' => $vendorDir . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php',
+ 'ReCaptcha\\RequestMethod' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod.php',
+ 'ReCaptcha\\RequestMethod\\Curl' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php',
+ 'ReCaptcha\\RequestMethod\\CurlPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php',
+ 'ReCaptcha\\RequestMethod\\Post' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php',
+ 'ReCaptcha\\RequestMethod\\Socket' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php',
+ 'ReCaptcha\\RequestMethod\\SocketPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php',
+ 'ReCaptcha\\RequestParameters' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestParameters.php',
+ 'ReCaptcha\\Response' => $vendorDir . '/google/recaptcha/src/ReCaptcha/Response.php',
'SMTP' => $vendorDir . '/phpmailer/phpmailer/class.smtp.php',
'SimplePie' => $vendorDir . '/simplepie/simplepie/library/SimplePie.php',
'SimplePie_Author' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Author.php',
diff --git a/libraries/vendor/composer/autoload_psr4.php b/libraries/vendor/composer/autoload_psr4.php
index a08fa71cb6bd6..c967fb06f6766 100644
--- a/libraries/vendor/composer/autoload_psr4.php
+++ b/libraries/vendor/composer/autoload_psr4.php
@@ -12,6 +12,7 @@
'Symfony\\Polyfill\\Php55\\' => array($vendorDir . '/symfony/polyfill-php55'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
+ 'ReCaptcha\\' => array($vendorDir . '/google/recaptcha/src/ReCaptcha'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Joomla\\Utilities\\' => array($vendorDir . '/joomla/utilities/src'),
diff --git a/libraries/vendor/composer/autoload_static.php b/libraries/vendor/composer/autoload_static.php
index 0f1dda4928e3f..ed03841ab3933 100644
--- a/libraries/vendor/composer/autoload_static.php
+++ b/libraries/vendor/composer/autoload_static.php
@@ -41,6 +41,10 @@ class ComposerStaticInit205c915b9c7d3e718e7c95793ee67ffe
'Symfony\\Polyfill\\Ctype\\' => 23,
'Symfony\\Component\\Yaml\\' => 23,
),
+ 'R' =>
+ array (
+ 'ReCaptcha\\' => 10,
+ ),
'P' =>
array (
'Psr\\Log\\' => 8,
@@ -93,6 +97,10 @@ class ComposerStaticInit205c915b9c7d3e718e7c95793ee67ffe
array (
0 => __DIR__ . '/..' . '/symfony/yaml',
),
+ 'ReCaptcha\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha',
+ ),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
@@ -306,6 +314,15 @@ class ComposerStaticInit205c915b9c7d3e718e7c95793ee67ffe
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
+ 'ReCaptcha\\ReCaptcha' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php',
+ 'ReCaptcha\\RequestMethod' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod.php',
+ 'ReCaptcha\\RequestMethod\\Curl' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php',
+ 'ReCaptcha\\RequestMethod\\CurlPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php',
+ 'ReCaptcha\\RequestMethod\\Post' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php',
+ 'ReCaptcha\\RequestMethod\\Socket' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php',
+ 'ReCaptcha\\RequestMethod\\SocketPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php',
+ 'ReCaptcha\\RequestParameters' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestParameters.php',
+ 'ReCaptcha\\Response' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/Response.php',
'SMTP' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.smtp.php',
'SimplePie' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie.php',
'SimplePie_Author' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Author.php',
diff --git a/libraries/vendor/composer/installed.json b/libraries/vendor/composer/installed.json
index 9a581b0f7c7ac..3bd4b035ac38a 100644
--- a/libraries/vendor/composer/installed.json
+++ b/libraries/vendor/composer/installed.json
@@ -1,4 +1,51 @@
[
+ {
+ "name": "google/recaptcha",
+ "version": "1.1.2",
+ "version_normalized": "1.1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/google/recaptcha.git",
+ "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/google/recaptcha/zipball/2b7e00566afca82a38a1d3adb8e42c118006296e",
+ "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.5.*"
+ },
+ "time": "2015-09-02T17:23:59+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "ReCaptcha\\": "src/ReCaptcha"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.",
+ "homepage": "http://www.google.com/recaptcha/",
+ "keywords": [
+ "Abuse",
+ "captcha",
+ "recaptcha",
+ "spam"
+ ]
+ },
{
"name": "ircmaxell/password-compat",
"version": "v1.0.4",
diff --git a/libraries/vendor/google/recaptcha/LICENSE b/libraries/vendor/google/recaptcha/LICENSE
new file mode 100644
index 0000000000000..f6412328f4c81
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/LICENSE
@@ -0,0 +1,29 @@
+Copyright 2014, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php
new file mode 100644
index 0000000000000..e2f7c347ebfc4
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php
@@ -0,0 +1,97 @@
+secret = $secret;
+
+ if (!is_null($requestMethod)) {
+ $this->requestMethod = $requestMethod;
+ } else {
+ $this->requestMethod = new RequestMethod\Post();
+ }
+ }
+
+ /**
+ * Calls the reCAPTCHA siteverify API to verify whether the user passes
+ * CAPTCHA test.
+ *
+ * @param string $response The value of 'g-recaptcha-response' in the submitted form.
+ * @param string $remoteIp The end user's IP address.
+ * @return Response Response from the service.
+ */
+ public function verify($response, $remoteIp = null)
+ {
+ // Discard empty solution submissions
+ if (empty($response)) {
+ $recaptchaResponse = new Response(false, array('missing-input-response'));
+ return $recaptchaResponse;
+ }
+
+ $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
+ $rawResponse = $this->requestMethod->submit($params);
+ return Response::fromJson($rawResponse);
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php
new file mode 100644
index 0000000000000..fc4dde59c0fce
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php
@@ -0,0 +1,42 @@
+curl = $curl;
+ } else {
+ $this->curl = new Curl();
+ }
+ }
+
+ /**
+ * Submit the cURL request with the specified parameters.
+ *
+ * @param RequestParameters $params Request parameters
+ * @return string Body of the reCAPTCHA response
+ */
+ public function submit(RequestParameters $params)
+ {
+ $handle = $this->curl->init(self::SITE_VERIFY_URL);
+
+ $options = array(
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => $params->toQueryString(),
+ CURLOPT_HTTPHEADER => array(
+ 'Content-Type: application/x-www-form-urlencoded'
+ ),
+ CURLINFO_HEADER_OUT => false,
+ CURLOPT_HEADER => false,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_SSL_VERIFYPEER => true
+ );
+ $this->curl->setoptArray($handle, $options);
+
+ $response = $this->curl->exec($handle);
+ $this->curl->close($handle);
+
+ return $response;
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php
new file mode 100644
index 0000000000000..7770d9081414a
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php
@@ -0,0 +1,70 @@
+ array(
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\n",
+ 'method' => 'POST',
+ 'content' => $params->toQueryString(),
+ // Force the peer to validate (not needed in 5.6.0+, but still works
+ 'verify_peer' => true,
+ // Force the peer validation to use www.google.com
+ $peer_key => 'www.google.com',
+ ),
+ );
+ $context = stream_context_create($options);
+ return file_get_contents(self::SITE_VERIFY_URL, false, $context);
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php
new file mode 100644
index 0000000000000..d3c87922d224e
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php
@@ -0,0 +1,105 @@
+handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout));
+
+ if ($this->handle != false && $errno === 0 && $errstr === '') {
+ return $this->handle;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * fwrite
+ *
+ * @see http://php.net/fwrite
+ * @param string $string
+ * @param int $length
+ * @return int | bool
+ */
+ public function fwrite($string, $length = null)
+ {
+ return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length));
+ }
+
+ /**
+ * fgets
+ *
+ * @see http://php.net/fgets
+ * @param int $length
+ * @return string
+ */
+ public function fgets($length = null)
+ {
+ return fgets($this->handle, $length);
+ }
+
+ /**
+ * feof
+ *
+ * @see http://php.net/feof
+ * @return bool
+ */
+ public function feof()
+ {
+ return feof($this->handle);
+ }
+
+ /**
+ * fclose
+ *
+ * @see http://php.net/fclose
+ * @return bool
+ */
+ public function fclose()
+ {
+ return fclose($this->handle);
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php
new file mode 100644
index 0000000000000..47541215f046f
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php
@@ -0,0 +1,121 @@
+socket = $socket;
+ } else {
+ $this->socket = new Socket();
+ }
+ }
+
+ /**
+ * Submit the POST request with the specified parameters.
+ *
+ * @param RequestParameters $params Request parameters
+ * @return string Body of the reCAPTCHA response
+ */
+ public function submit(RequestParameters $params)
+ {
+ $errno = 0;
+ $errstr = '';
+
+ if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) {
+ return self::BAD_REQUEST;
+ }
+
+ $content = $params->toQueryString();
+
+ $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
+ $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
+ $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
+ $request .= "Content-length: " . strlen($content) . "\r\n";
+ $request .= "Connection: close\r\n\r\n";
+ $request .= $content . "\r\n\r\n";
+
+ $this->socket->fwrite($request);
+ $response = '';
+
+ while (!$this->socket->feof()) {
+ $response .= $this->socket->fgets(4096);
+ }
+
+ $this->socket->fclose();
+
+ if (0 !== strpos($response, 'HTTP/1.1 200 OK')) {
+ return self::BAD_RESPONSE;
+ }
+
+ $parts = preg_split("#\n\s*\n#Uis", $response);
+
+ return $parts[1];
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php
new file mode 100644
index 0000000000000..cb66f26cf4b6c
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php
@@ -0,0 +1,103 @@
+secret = $secret;
+ $this->response = $response;
+ $this->remoteIp = $remoteIp;
+ $this->version = $version;
+ }
+
+ /**
+ * Array representation.
+ *
+ * @return array Array formatted parameters.
+ */
+ public function toArray()
+ {
+ $params = array('secret' => $this->secret, 'response' => $this->response);
+
+ if (!is_null($this->remoteIp)) {
+ $params['remoteip'] = $this->remoteIp;
+ }
+
+ if (!is_null($this->version)) {
+ $params['version'] = $this->version;
+ }
+
+ return $params;
+ }
+
+ /**
+ * Query string representation for HTTP request.
+ *
+ * @return string Query string formatted parameters.
+ */
+ public function toQueryString()
+ {
+ return http_build_query($this->toArray(), '', '&');
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/ReCaptcha/Response.php b/libraries/vendor/google/recaptcha/src/ReCaptcha/Response.php
new file mode 100644
index 0000000000000..d2d8a8bf77720
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/ReCaptcha/Response.php
@@ -0,0 +1,102 @@
+success = $success;
+ $this->errorCodes = $errorCodes;
+ }
+
+ /**
+ * Is success?
+ *
+ * @return boolean
+ */
+ public function isSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Get error codes.
+ *
+ * @return array
+ */
+ public function getErrorCodes()
+ {
+ return $this->errorCodes;
+ }
+}
diff --git a/libraries/vendor/google/recaptcha/src/autoload.php b/libraries/vendor/google/recaptcha/src/autoload.php
new file mode 100644
index 0000000000000..a53cbd78bf25c
--- /dev/null
+++ b/libraries/vendor/google/recaptcha/src/autoload.php
@@ -0,0 +1,38 @@
+params->get('version', '1.0') === '1.0')
{
JHtml::_('jquery.framework');
- $theme = $this->params->get('theme', 'clean');
- $file = 'https://www.google.com/recaptcha/api/js/recaptcha_ajax.js';
+ $theme = $this->params->get('theme', 'clean');
+ $file = 'https://www.google.com/recaptcha/api/js/recaptcha_ajax.js';
JHtml::_('script', $file);
JFactory::getDocument()->addScriptDeclaration('jQuery( document ).ready(function()
@@ -91,8 +92,7 @@ public function onInit($id = 'dynamic_recaptcha_1')
*
* @param string $name The name of the field. Not Used.
* @param string $id The id of the field.
- * @param string $class The class of the field. This should be passed as
- * e.g. 'class="required"'.
+ * @param string $class The class of the field.
*
* @return string The HTML to be embedded in the form.
*
@@ -100,18 +100,28 @@ public function onInit($id = 'dynamic_recaptcha_1')
*/
public function onDisplay($name = null, $id = 'dynamic_recaptcha_1', $class = '')
{
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $ele = $dom->createElement('div');
+ $ele->setAttribute('id', $id);
+
if ($this->params->get('version', '1.0') === '1.0')
{
- return '
';
+ $ele->setAttribute('class', $class);
}
else
{
- return '
';
+ $ele->setAttribute('class', ((trim($class) == '') ? 'g-recaptcha' : ($class . ' g-recaptcha')));
+ $ele->setAttribute('data-sitekey', $this->params->get('public_key', ''));
+ $ele->setAttribute('data-theme', $this->params->get('theme2', 'light'));
+ $ele->setAttribute('data-size', $this->params->get('size', 'normal'));
+ $ele->setAttribute('data-tabindex', $this->params->get('tabindex', '0'));
+ $ele->setAttribute('data-callback', $this->params->get('callback', ''));
+ $ele->setAttribute('data-expired-callback', $this->params->get('expired_callback', ''));
+ $ele->setAttribute('data-error-callback', $this->params->get('error_callback', ''));
}
+
+ $dom->appendChild($ele);
+ return $dom->saveXML($ele);
}
/**
@@ -121,11 +131,12 @@ public function onDisplay($name = null, $id = 'dynamic_recaptcha_1', $class = ''
*
* @return True if the answer is correct, false otherwise
*
- * @since 2.5
+ * @since 2.5
+ * @throws \RuntimeException
*/
public function onCheckAnswer($code = null)
{
- $input = JFactory::getApplication()->input;
+ $input = \JFactory::getApplication()->input;
$privatekey = $this->params->get('private_key');
$version = $this->params->get('version', '1.0');
$remoteip = $input->server->get('REMOTE_ADDR', '', 'string');
@@ -148,25 +159,19 @@ public function onCheckAnswer($code = null)
// Check for Private Key
if (empty($privatekey))
{
- $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_NO_PRIVATE_KEY'));
-
- return false;
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_NO_PRIVATE_KEY'));
}
// Check for IP
if (empty($remoteip))
{
- $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_NO_IP'));
-
- return false;
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_NO_IP'));
}
// Discard spam submissions
if ($spam)
{
- $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_EMPTY_SOLUTION'));
-
- return false;
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_EMPTY_SOLUTION'));
}
return $this->getResponse($privatekey, $remoteip, $response, $challenge);
@@ -178,11 +183,12 @@ public function onCheckAnswer($code = null)
* @param string $privatekey The private key for authentication.
* @param string $remoteip The remote IP of the visitor.
* @param string $response The response received from Google.
- * @param string $challenge The challenge field from the reCaptcha. Only for 1.0.
+ * @param string $challenge The challenge field from the reCaptcha. Only for 1.0
*
* @return bool True if response is good | False if response is bad.
*
* @since 3.4
+ * @throws \RuntimeException
*/
private function getResponse($privatekey, $remoteip, $response, $challenge = null)
{
@@ -212,20 +218,14 @@ private function getResponse($privatekey, $remoteip, $response, $challenge = nul
}
break;
case '2.0':
- require_once 'recaptchalib.php';
+ $reCaptcha = new \ReCaptcha\ReCaptcha($privatekey, new HttpBridgePostRequestMethod);
+ $response = $reCaptcha->verify($response, $remoteip);
- $reCaptcha = new JReCaptcha($privatekey);
- $response = $reCaptcha->verifyResponse($remoteip, $response);
-
- if (!isset($response->success) || !$response->success)
+ if (!$response->isSuccess())
{
- // @todo use exceptions here
- if (is_array($response->errorCodes))
+ foreach ($response->getErrorCodes() as $error)
{
- foreach ($response->errorCodes as $error)
- {
- $this->_subject->setError($error);
- }
+ throw new \RuntimeException($error);
}
return false;
diff --git a/plugins/captcha/recaptcha/recaptcha.xml b/plugins/captcha/recaptcha/recaptcha.xml
index 1325c6f4efe68..0c321434677ff 100644
--- a/plugins/captcha/recaptcha/recaptcha.xml
+++ b/plugins/captcha/recaptcha/recaptcha.xml
@@ -11,7 +11,6 @@
PLG_CAPTCHA_RECAPTCHA_XML_DESCRIPTION
recaptcha.php
- recaptchalib.php
@@ -43,7 +42,8 @@
default=""
required="true"
filter="string"
- size="50"
+ size="100"
+ class="input-xxlarge"
/>
PLG_RECAPTCHA_THEME_NORMAL
PLG_RECAPTCHA_THEME_COMPACT
+
+
+
+
+
+
+
+
diff --git a/plugins/captcha/recaptcha/recaptchalib.php b/plugins/captcha/recaptcha/recaptchalib.php
deleted file mode 100644
index ee969b79ae9aa..0000000000000
--- a/plugins/captcha/recaptcha/recaptchalib.php
+++ /dev/null
@@ -1,162 +0,0 @@
-" . 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 '&'
- return substr($req, 0, strlen($req) - 1);
- }
-
- /**
- * 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 mixed JSON string or false on error
- */
- private function _submitHTTPGet($path, $data)
- {
- $req = $this->_encodeQS($data);
-
- try
- {
- $http = JHttpFactory::getHttp();
- $result = $http->get($path . '?' . $req)->body;
- }
- catch (RuntimeException $e)
- {
- return false;
- }
-
- return $result;
- }
-
- /**
- * 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 JReCaptchaResponse
- */
- public function verifyResponse($remoteIp, $response)
- {
- // Discard empty solution submissions
- if ($response === null || $response === '')
- {
- $recaptchaResponse = new JReCaptchaResponse();
- $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
- )
- );
-
- // Something is broken in submiting the http get request
- if ($getResponse === false)
- {
- $recaptchaResponse->success = false;
- }
-
- $answers = json_decode($getResponse, true);
- $recaptchaResponse = new JReCaptchaResponse();
-
- if (trim($answers['success']) !== '')
- {
- $recaptchaResponse->success = true;
- }
- else
- {
- $recaptchaResponse->success = false;
- $recaptchaResponse->errorCodes = isset($answers['error-codes']) ? $answers['error-codes'] : '';
- }
-
- return $recaptchaResponse;
- }
-}
\ No newline at end of file
diff --git a/plugins/captcha/recaptcha_invisible/recaptcha_invisible.php b/plugins/captcha/recaptcha_invisible/recaptcha_invisible.php
new file mode 100644
index 0000000000000..a46f7c3598885
--- /dev/null
+++ b/plugins/captcha/recaptcha_invisible/recaptcha_invisible.php
@@ -0,0 +1,182 @@
+params->get('public_key', '');
+
+ if ($pubkey === '')
+ {
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_PUBLIC_KEY'));
+ }
+
+
+ // Load callback first for browser compatibility
+ \JHtml::_(
+ 'script',
+ 'plg_captcha_recaptcha_invisible/recaptcha.min.js',
+ array('version' => 'auto', 'relative' => true),
+ array('async' => 'async', 'defer' => 'defer')
+ );
+
+ // Load Google reCAPTCHA api js
+ $file = 'https://www.google.com/recaptcha/api.js'
+ . '?onload=JoomlaInitReCaptchaInvisible'
+ . '&render=explicit'
+ . '&hl=' . \JFactory::getLanguage()->getTag();
+ \JHtml::_(
+ 'script',
+ $file,
+ array(),
+ array('async' => 'async', 'defer' => 'defer')
+ );
+ }
+
+ /**
+ * Gets the challenge HTML
+ *
+ * @param string $name The name of the field. Not Used.
+ * @param string $id The id of the field.
+ * @param string $class The class of the field.
+ *
+ * @return string The HTML to be embedded in the form.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function onDisplay($name = null, $id = 'dynamic_recaptcha_invisible_1', $class = '')
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $ele = $dom->createElement('div');
+ $ele->setAttribute('id', $id);
+ $ele->setAttribute('class', ((trim($class) == '') ? 'g-recaptcha' : ($class . ' g-recaptcha')));
+ $ele->setAttribute('data-sitekey', $this->params->get('public_key', ''));
+ $ele->setAttribute('data-badge', $this->params->get('badge', 'bottomright'));
+ $ele->setAttribute('data-size', 'invisible');
+ $ele->setAttribute('data-tabindex', $this->params->get('tabindex', '0'));
+ $ele->setAttribute('data-callback', $this->params->get('callback', ''));
+ $ele->setAttribute('data-expired-callback', $this->params->get('expired_callback', ''));
+ $ele->setAttribute('data-error-callback', $this->params->get('error_callback', ''));
+ $dom->appendChild($ele);
+
+ return $dom->saveHTML($ele);
+ }
+
+ /**
+ * Calls an HTTP POST function to verify if the user's guess was correct
+ *
+ * @param string $code Answer provided by user. Not needed for the Recaptcha implementation
+ *
+ * @return True if the answer is correct, false otherwise
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ public function onCheckAnswer($code = null)
+ {
+ $input = \JFactory::getApplication()->input;
+ $privatekey = $this->params->get('private_key');
+ $remoteip = $input->server->get('REMOTE_ADDR', '', 'string');
+
+ $response = $input->get('g-recaptcha-response', '', 'string');
+
+ // Check for Private Key
+ if (empty($privatekey))
+ {
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_PRIVATE_KEY'));
+ }
+
+ // Check for IP
+ if (empty($remoteip))
+ {
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_IP'));
+ }
+
+ // Discard spam submissions
+ if (trim($response) == '')
+ {
+ throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_EMPTY_SOLUTION'));
+ }
+
+ return $this->getResponse($privatekey, $remoteip, $response);
+ }
+
+ /**
+ * Method to react on the setup of a captcha field. Gives the possibility
+ * to change the field and/or the XML element for the field.
+ *
+ * @param \Joomla\CMS\Form\Field\CaptchaField $field Captcha field instance
+ * @param \SimpleXMLElement $element XML form definition
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function onSetupField(\Joomla\CMS\Form\Field\CaptchaField $field, \SimpleXMLElement $element)
+ {
+ // Hide the label for the invisible recaptcha type
+ $element['hiddenLabel'] = true;
+ }
+
+ /**
+ * Get the reCaptcha response.
+ *
+ * @param string $privatekey The private key for authentication.
+ * @param string $remoteip The remote IP of the visitor.
+ * @param string $response The response received from Google.
+ *
+ * @return bool True if response is good | False if response is bad.
+ *
+ * @since __DEPLOY_VERSION__
+ * @throws \RuntimeException
+ */
+ private function getResponse($privatekey, $remoteip, $response)
+ {
+ $reCaptcha = new \ReCaptcha\ReCaptcha($privatekey, new HttpBridgePostRequestMethod);
+ $response = $reCaptcha->verify($response, $remoteip);
+
+ if (!$response->isSuccess())
+ {
+ foreach ($response->getErrorCodes() as $error)
+ {
+ throw new \RuntimeException($error);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/plugins/captcha/recaptcha_invisible/recaptcha_invisible.xml b/plugins/captcha/recaptcha_invisible/recaptcha_invisible.xml
new file mode 100644
index 0000000000000..b5dc7f606b5bb
--- /dev/null
+++ b/plugins/captcha/recaptcha_invisible/recaptcha_invisible.xml
@@ -0,0 +1,93 @@
+
+
+ plg_captcha_recaptcha_invisible
+ 3.8
+ November 2017
+ Joomla! Project
+ admin@joomla.org
+ www.joomla.org
+ Copyright (C) 2005 - 2017 Open Source Matters. All rights reserved.
+ GNU General Public License version 2 or later; see LICENSE.txt
+ PLG_CAPTCHA_RECAPTCHA_INVISIBLE_XML_DESCRIPTION
+
+ recaptcha_invisible.php
+
+
+
+
+
+
+
+
+
+
+ PLG_RECAPTCHA_INVISIBLE_BADGE_BOTTOMRIGHT
+ PLG_RECAPTCHA_INVISIBLE_BADGE_BOTTOMLEFT
+ PLG_RECAPTCHA_INVISIBLE_BADGE_INLINE
+
+
+
+
+
+
+
+
+
+
+
+
+