diff options
| author | spiderr <spiderr@bitweaver.org> | 2020-08-12 12:23:27 -0400 |
|---|---|---|
| committer | spiderr <spiderr@bitweaver.org> | 2020-08-12 12:23:27 -0400 |
| commit | 9505b7a4c2e117bf555a739d22d13a72d1ba072a (patch) | |
| tree | 491bd7336a9ae7bb8fc781a4807e134d4fd586d0 /includes | |
| parent | 0c20b1f6b3b5647c63609f3899fa106ab74d33c3 (diff) | |
| download | users-9505b7a4c2e117bf555a739d22d13a72d1ba072a.tar.gz users-9505b7a4c2e117bf555a739d22d13a72d1ba072a.tar.bz2 users-9505b7a4c2e117bf555a739d22d13a72d1ba072a.zip | |
move all captcha code to users/
Diffstat (limited to 'includes')
| -rw-r--r-- | includes/BitUser.php | 4 | ||||
| -rw-r--r-- | includes/RoleUser.php | 2 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/ReCaptcha.php | 269 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod.php | 50 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod/Curl.php | 82 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod/CurlPost.php | 104 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod/Post.php | 88 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod/Socket.php | 112 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestMethod/SocketPost.php | 108 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/RequestParameters.php | 111 | ||||
| -rw-r--r-- | includes/recaptcha/ReCaptcha/Response.php | 218 | ||||
| -rw-r--r-- | includes/recaptcha/autoload.php | 69 | ||||
| -rwxr-xr-x | includes/recaptchalib.php | 282 |
13 files changed, 1214 insertions, 285 deletions
diff --git a/includes/BitUser.php b/includes/BitUser.php index d3d8b80..11e5eaa 100644 --- a/includes/BitUser.php +++ b/includes/BitUser.php @@ -404,9 +404,9 @@ class BitUser extends LibertyMime { if( $gBitSystem->isFeatureActive( 'users_register_recaptcha' ) && (empty( $pParamHash['novalidation'] ) || $pParamHash['novalidation'] != 'yes') ) { if( !empty( $pParamHash['g-recaptcha-response'] ) ) { - require_once UTIL_PKG_PATH.'includes/recaptcha/autoload.php'; + require_once USERS_PKG_PATH.'includes/recaptcha/autoload.php'; - $recaptcha = new \ReCaptcha\ReCaptcha( $gBitSystem->getConfig( 'users_register_recaptcha_private_key' ) ); + $recaptcha = new \ReCaptcha\ReCaptcha( $gBitSystem->getConfig( 'users_register_recaptcha_secret_key' ) ); $resp = $recaptcha->setExpectedHostname( $_SERVER['HTTP_HOST'] ) ->verify( $pParamHash['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'] ); if( !$resp->isSuccess() ) { diff --git a/includes/RoleUser.php b/includes/RoleUser.php index 66ea7bb..a042a71 100644 --- a/includes/RoleUser.php +++ b/includes/RoleUser.php @@ -405,7 +405,7 @@ class BitUser extends LibertyMime { if( $gBitSystem->isFeatureActive( 'users_register_recaptcha' ) && (empty( $pParamHash['novalidation'] ) || $pParamHash['novalidation'] != 'yes') ) { require_once( USERS_PKG_PATH.'includes/recaptchalib.php' ); if( !empty( $pParamHash["recaptcha_challenge_field"] ) && !empty( $pParamHash["recaptcha_response_field"] ) ) { - $resp = recaptcha_check_answer ( $gBitSystem->getConfig( 'users_register_recaptcha_private_key' ), $_SERVER["REMOTE_ADDR"], $pParamHash["recaptcha_challenge_field"], $pParamHash["recaptcha_response_field"] ); + $resp = recaptcha_check_answer ( $gBitSystem->getConfig( 'users_register_recaptcha_secret_key' ), $_SERVER["REMOTE_ADDR"], $pParamHash["recaptcha_challenge_field"], $pParamHash["recaptcha_response_field"] ); if( !$resp->is_valid ) { $this->mErrors['recaptcha'] = $resp->error; } diff --git a/includes/recaptcha/ReCaptcha/ReCaptcha.php b/includes/recaptcha/ReCaptcha/ReCaptcha.php new file mode 100644 index 0000000..31ec44a --- /dev/null +++ b/includes/recaptcha/ReCaptcha/ReCaptcha.php @@ -0,0 +1,269 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha; + +/** + * reCAPTCHA client. + */ +class ReCaptcha +{ + /** + * Version of this client library. + * @const string + */ + const VERSION = 'php_1.2.4'; + + /** + * URL for reCAPTCHA siteverify API + * @const string + */ + const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; + + /** + * Invalid JSON received + * @const string + */ + const E_INVALID_JSON = 'invalid-json'; + + /** + * Could not connect to service + * @const string + */ + const E_CONNECTION_FAILED = 'connection-failed'; + + /** + * Did not receive a 200 from the service + * @const string + */ + const E_BAD_RESPONSE = 'bad-response'; + + /** + * Not a success, but no error codes received! + * @const string + */ + const E_UNKNOWN_ERROR = 'unknown-error'; + + /** + * ReCAPTCHA response not provided + * @const string + */ + const E_MISSING_INPUT_RESPONSE = 'missing-input-response'; + + /** + * Expected hostname did not match + * @const string + */ + const E_HOSTNAME_MISMATCH = 'hostname-mismatch'; + + /** + * Expected APK package name did not match + * @const string + */ + const E_APK_PACKAGE_NAME_MISMATCH = 'apk_package_name-mismatch'; + + /** + * Expected action did not match + * @const string + */ + const E_ACTION_MISMATCH = 'action-mismatch'; + + /** + * Score threshold not met + * @const string + */ + const E_SCORE_THRESHOLD_NOT_MET = 'score-threshold-not-met'; + + /** + * Challenge timeout + * @const string + */ + const E_CHALLENGE_TIMEOUT = 'challenge-timeout'; + + /** + * Shared secret for the site. + * @var string + */ + private $secret; + + /** + * Method used to communicate with service. Defaults to POST request. + * @var RequestMethod + */ + private $requestMethod; + + /** + * Create a configured instance to use the reCAPTCHA service. + * + * @param string $secret The shared key between your site and reCAPTCHA. + * @param RequestMethod $requestMethod method used to send the request. Defaults to POST. + * @throws \RuntimeException if $secret is invalid + */ + public function __construct($secret, RequestMethod $requestMethod = null) + { + if (empty($secret)) { + throw new \RuntimeException('No secret provided'); + } + + if (!is_string($secret)) { + throw new \RuntimeException('The provided secret must be a string'); + } + + $this->secret = $secret; + $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod; + } + + /** + * Calls the reCAPTCHA siteverify API to verify whether the user passes + * CAPTCHA test and additionally runs any specified additional checks + * + * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site. + * @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(self::E_MISSING_INPUT_RESPONSE)); + return $recaptchaResponse; + } + + $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION); + $rawResponse = $this->requestMethod->submit($params); + $initialResponse = Response::fromJson($rawResponse); + $validationErrors = array(); + + if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) { + $validationErrors[] = self::E_HOSTNAME_MISMATCH; + } + + if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) { + $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH; + } + + if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) { + $validationErrors[] = self::E_ACTION_MISMATCH; + } + + if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) { + $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET; + } + + if (isset($this->timeoutSeconds)) { + $challengeTs = strtotime($initialResponse->getChallengeTs()); + + if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) { + $validationErrors[] = self::E_CHALLENGE_TIMEOUT; + } + } + + if (empty($validationErrors)) { + return $initialResponse; + } + + return new Response( + false, + array_merge($initialResponse->getErrorCodes(), $validationErrors), + $initialResponse->getHostname(), + $initialResponse->getChallengeTs(), + $initialResponse->getApkPackageName(), + $initialResponse->getScore(), + $initialResponse->getAction() + ); + } + + /** + * Provide a hostname to match against in verify() + * This should be without a protocol or trailing slash, e.g. www.google.com + * + * @param string $hostname Expected hostname + * @return ReCaptcha Current instance for fluent interface + */ + public function setExpectedHostname($hostname) + { + $this->hostname = $hostname; + return $this; + } + + /** + * Provide an APK package name to match against in verify() + * + * @param string $apkPackageName Expected APK package name + * @return ReCaptcha Current instance for fluent interface + */ + public function setExpectedApkPackageName($apkPackageName) + { + $this->apkPackageName = $apkPackageName; + return $this; + } + + /** + * Provide an action to match against in verify() + * This should be set per page. + * + * @param string $action Expected action + * @return ReCaptcha Current instance for fluent interface + */ + public function setExpectedAction($action) + { + $this->action = $action; + return $this; + } + + /** + * Provide a threshold to meet or exceed in verify() + * Threshold should be a float between 0 and 1 which will be tested as response >= threshold. + * + * @param float $threshold Expected threshold + * @return ReCaptcha Current instance for fluent interface + */ + public function setScoreThreshold($threshold) + { + $this->threshold = floatval($threshold); + return $this; + } + + /** + * Provide a timeout in seconds to test against the challenge timestamp in verify() + * + * @param int $timeoutSeconds Expected hostname + * @return ReCaptcha Current instance for fluent interface + */ + public function setChallengeTimeout($timeoutSeconds) + { + $this->timeoutSeconds = $timeoutSeconds; + return $this; + } +} diff --git a/includes/recaptcha/ReCaptcha/RequestMethod.php b/includes/recaptcha/ReCaptcha/RequestMethod.php new file mode 100644 index 0000000..0a2a671 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod.php @@ -0,0 +1,50 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha; + +/** + * Method used to send the request to the service. + */ +interface RequestMethod +{ + + /** + * Submit the request with the specified parameters. + * + * @param RequestParameters $params Request parameters + * @return string Body of the reCAPTCHA response + */ + public function submit(RequestParameters $params); +} diff --git a/includes/recaptcha/ReCaptcha/RequestMethod/Curl.php b/includes/recaptcha/ReCaptcha/RequestMethod/Curl.php new file mode 100644 index 0000000..eb99842 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod/Curl.php @@ -0,0 +1,82 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha\RequestMethod; + +/** + * Convenience wrapper around the cURL functions to allow mocking. + */ +class Curl +{ + + /** + * @see http://php.net/curl_init + * @param string $url + * @return resource cURL handle + */ + public function init($url = null) + { + return curl_init($url); + } + + /** + * @see http://php.net/curl_setopt_array + * @param resource $ch + * @param array $options + * @return bool + */ + public function setoptArray($ch, array $options) + { + return curl_setopt_array($ch, $options); + } + + /** + * @see http://php.net/curl_exec + * @param resource $ch + * @return mixed + */ + public function exec($ch) + { + return curl_exec($ch); + } + + /** + * @see http://php.net/curl_close + * @param resource $ch + */ + public function close($ch) + { + curl_close($ch); + } +} diff --git a/includes/recaptcha/ReCaptcha/RequestMethod/CurlPost.php b/includes/recaptcha/ReCaptcha/RequestMethod/CurlPost.php new file mode 100644 index 0000000..1bd7cce --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod/CurlPost.php @@ -0,0 +1,104 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends cURL request to the reCAPTCHA service. + * Note: this requires the cURL extension to be enabled in PHP + * @see http://php.net/manual/en/book.curl.php + */ +class CurlPost implements RequestMethod +{ + /** + * Curl connection to the reCAPTCHA service + * @var Curl + */ + private $curl; + + /** + * URL for reCAPTCHA siteverify API + * @var string + */ + private $siteVerifyUrl; + + /** + * Only needed if you want to override the defaults + * + * @param Curl $curl Curl resource + * @param string $siteVerifyUrl URL for reCAPTCHA siteverify API + */ + public function __construct(Curl $curl = null, $siteVerifyUrl = null) + { + $this->curl = (is_null($curl)) ? new Curl() : $curl; + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * 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($this->siteVerifyUrl); + + $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); + + if ($response !== false) { + return $response; + } + + return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; + } +} diff --git a/includes/recaptcha/ReCaptcha/RequestMethod/Post.php b/includes/recaptcha/ReCaptcha/RequestMethod/Post.php new file mode 100644 index 0000000..a4ff716 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod/Post.php @@ -0,0 +1,88 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends POST requests to the reCAPTCHA service. + */ +class Post implements RequestMethod +{ + /** + * URL for reCAPTCHA siteverify API + * @var string + */ + private $siteVerifyUrl; + + /** + * Only needed if you want to override the defaults + * + * @param string $siteVerifyUrl URL for reCAPTCHA siteverify API + */ + public function __construct($siteVerifyUrl = null) + { + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * 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) + { + $options = array( + 'http' => 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, + ), + ); + $context = stream_context_create($options); + $response = file_get_contents($this->siteVerifyUrl, false, $context); + + if ($response !== false) { + return $response; + } + + return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; + } +} diff --git a/includes/recaptcha/ReCaptcha/RequestMethod/Socket.php b/includes/recaptcha/ReCaptcha/RequestMethod/Socket.php new file mode 100644 index 0000000..236bd5f --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod/Socket.php @@ -0,0 +1,112 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha\RequestMethod; + +/** + * Convenience wrapper around native socket and file functions to allow for + * mocking. + */ +class Socket +{ + private $handle = null; + + /** + * fsockopen + * + * @see http://php.net/fsockopen + * @param string $hostname + * @param int $port + * @param int $errno + * @param string $errstr + * @param float $timeout + * @return resource + */ + public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null) + { + $this->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; + } + 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/includes/recaptcha/ReCaptcha/RequestMethod/SocketPost.php b/includes/recaptcha/ReCaptcha/RequestMethod/SocketPost.php new file mode 100644 index 0000000..464bc28 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestMethod/SocketPost.php @@ -0,0 +1,108 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends a POST request to the reCAPTCHA service, but makes use of fsockopen() + * instead of get_file_contents(). This is to account for people who may be on + * servers where allow_url_open is disabled. + */ +class SocketPost implements RequestMethod +{ + /** + * Socket to the reCAPTCHA service + * @var Socket + */ + private $socket; + + /** + * Only needed if you want to override the defaults + * + * @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing + * @param string $siteVerifyUrl URL for reCAPTCHA siteverify API + */ + public function __construct(Socket $socket = null, $siteVerifyUrl = null) + { + $this->socket = (is_null($socket)) ? new Socket() : $socket; + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * 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 = ''; + $urlParsed = parse_url($this->siteVerifyUrl); + + if (false === $this->socket->fsockopen('ssl://' . $urlParsed['host'], 443, $errno, $errstr, 30)) { + return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; + } + + $content = $params->toQueryString(); + + $request = "POST " . $urlParsed['path'] . " HTTP/1.0\r\n"; + $request .= "Host: " . $urlParsed['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.0 200 OK')) { + return '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}'; + } + + $parts = preg_split("#\n\s*\n#Uis", $response); + + return $parts[1]; + } +} diff --git a/includes/recaptcha/ReCaptcha/RequestParameters.php b/includes/recaptcha/ReCaptcha/RequestParameters.php new file mode 100644 index 0000000..e9ba453 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/RequestParameters.php @@ -0,0 +1,111 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha; + +/** + * Stores and formats the parameters for the request to the reCAPTCHA service. + */ +class RequestParameters +{ + /** + * The shared key between your site and reCAPTCHA. + * @var string + */ + private $secret; + + /** + * The user response token provided by reCAPTCHA, verifying the user on your site. + * @var string + */ + private $response; + + /** + * Remote user's IP address. + * @var string + */ + private $remoteIp; + + /** + * Client version. + * @var string + */ + private $version; + + /** + * Initialise parameters. + * + * @param string $secret Site secret. + * @param string $response Value from g-captcha-response form field. + * @param string $remoteIp User's IP address. + * @param string $version Version of this client library. + */ + public function __construct($secret, $response, $remoteIp = null, $version = null) + { + $this->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/includes/recaptcha/ReCaptcha/Response.php b/includes/recaptcha/ReCaptcha/Response.php new file mode 100644 index 0000000..55838c0 --- /dev/null +++ b/includes/recaptcha/ReCaptcha/Response.php @@ -0,0 +1,218 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +namespace ReCaptcha; + +/** + * The response returned from the service. + */ +class Response +{ + /** + * Success or failure. + * @var boolean + */ + private $success = false; + + /** + * Error code strings. + * @var array + */ + private $errorCodes = array(); + + /** + * The hostname of the site where the reCAPTCHA was solved. + * @var string + */ + private $hostname; + + /** + * Timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ) + * @var string + */ + private $challengeTs; + + /** + * APK package name + * @var string + */ + private $apkPackageName; + + /** + * Score assigned to the request + * @var float + */ + private $score; + + /** + * Action as specified by the page + * @var string + */ + private $action; + + /** + * Build the response from the expected JSON returned by the service. + * + * @param string $json + * @return \ReCaptcha\Response + */ + public static function fromJson($json) + { + $responseData = json_decode($json, true); + + if (!$responseData) { + return new Response(false, array(ReCaptcha::E_INVALID_JSON)); + } + + $hostname = isset($responseData['hostname']) ? $responseData['hostname'] : null; + $challengeTs = isset($responseData['challenge_ts']) ? $responseData['challenge_ts'] : null; + $apkPackageName = isset($responseData['apk_package_name']) ? $responseData['apk_package_name'] : null; + $score = isset($responseData['score']) ? floatval($responseData['score']) : null; + $action = isset($responseData['action']) ? $responseData['action'] : null; + + if (isset($responseData['success']) && $responseData['success'] == true) { + return new Response(true, array(), $hostname, $challengeTs, $apkPackageName, $score, $action); + } + + if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) { + return new Response(false, $responseData['error-codes'], $hostname, $challengeTs, $apkPackageName, $score, $action); + } + + return new Response(false, array(ReCaptcha::E_UNKNOWN_ERROR), $hostname, $challengeTs, $apkPackageName, $score, $action); + } + + /** + * Constructor. + * + * @param boolean $success + * @param string $hostname + * @param string $challengeTs + * @param string $apkPackageName + * @param float $score + * @param string $action + * @param array $errorCodes + */ + public function __construct($success, array $errorCodes = array(), $hostname = null, $challengeTs = null, $apkPackageName = null, $score = null, $action = null) + { + $this->success = $success; + $this->hostname = $hostname; + $this->challengeTs = $challengeTs; + $this->apkPackageName = $apkPackageName; + $this->score = $score; + $this->action = $action; + $this->errorCodes = $errorCodes; + } + + /** + * Is success? + * + * @return boolean + */ + public function isSuccess() + { + return $this->success; + } + + /** + * Get error codes. + * + * @return array + */ + public function getErrorCodes() + { + return $this->errorCodes; + } + + /** + * Get hostname. + * + * @return string + */ + public function getHostname() + { + return $this->hostname; + } + + /** + * Get challenge timestamp + * + * @return string + */ + public function getChallengeTs() + { + return $this->challengeTs; + } + + /** + * Get APK package name + * + * @return string + */ + public function getApkPackageName() + { + return $this->apkPackageName; + } + /** + * Get score + * + * @return float + */ + public function getScore() + { + return $this->score; + } + + /** + * Get action + * + * @return string + */ + public function getAction() + { + return $this->action; + } + + public function toArray() + { + return array( + 'success' => $this->isSuccess(), + 'hostname' => $this->getHostname(), + 'challenge_ts' => $this->getChallengeTs(), + 'apk_package_name' => $this->getApkPackageName(), + 'score' => $this->getScore(), + 'action' => $this->getAction(), + 'error-codes' => $this->getErrorCodes(), + ); + } +} diff --git a/includes/recaptcha/autoload.php b/includes/recaptcha/autoload.php new file mode 100644 index 0000000..7947a10 --- /dev/null +++ b/includes/recaptcha/autoload.php @@ -0,0 +1,69 @@ +<?php + +/* An autoloader for ReCaptcha\Foo classes. This should be required() + * by the user before attempting to instantiate any of the ReCaptcha + * classes. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +spl_autoload_register(function ($class) { + if (substr($class, 0, 10) !== 'ReCaptcha\\') { + /* If the class does not lie under the "ReCaptcha" namespace, + * then we can exit immediately. + */ + return; + } + + /* All of the classes have names like "ReCaptcha\Foo", so we need + * to replace the backslashes with frontslashes if we want the + * name to map directly to a location in the filesystem. + */ + $class = str_replace('\\', '/', $class); + + /* First, check under the current directory. It is important that + * we look here first, so that we don't waste time searching for + * test classes in the common case. + */ + $path = dirname(__FILE__).'/'.$class.'.php'; + if (is_readable($path)) { + require_once $path; + + return; + } + + /* If we didn't find what we're looking for already, maybe it's + * a test class? + */ + $path = dirname(__FILE__).'/../tests/'.$class.'.php'; + if (is_readable($path)) { + require_once $path; + } +}); diff --git a/includes/recaptchalib.php b/includes/recaptchalib.php deleted file mode 100755 index 71f9d90..0000000 --- a/includes/recaptchalib.php +++ /dev/null @@ -1,282 +0,0 @@ -<?php -/** - * This is a PHP library that handles calling reCAPTCHA. - * - Documentation and latest version - * http://recaptcha.net/plugins/php/ - * - Get a reCAPTCHA API Key - * https://www.google.com/recaptcha/admin/create - * - Discussion group - * http://groups.google.com/group/recaptcha - * - * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net - * AUTHORS: - * Mike Crawford - * Ben Maurer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package users - * @subpackage functions - */ - -/** - * The reCAPTCHA server URL's - */ -define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api"); -define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api"); -define("RECAPTCHA_VERIFY_SERVER", "www.google.com"); - -/** - * Encodes the given data into a query string format - * @param $data - array of string elements to be encoded - * @return string - encoded request - */ -function _recaptcha_qsencode ($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 POST to a reCAPTCHA server - * @param string $host - * @param string $path - * @param array $data - * @param int port - * @return array response - */ -function _recaptcha_http_post($host, $path, $data, $port = 80) { - - $req = _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( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { - die ('Could not open socket'); - } - - fwrite($fs, $http_request); - - while ( !feof($fs) ) - $response .= fgets($fs, 1160); // One TCP-IP packet - fclose($fs); - $response = explode("\r\n\r\n", $response, 2); - - return $response; -} - - - -/** - * Gets the challenge HTML (javascript and non-javascript version). - * This is called from the browser, and the resulting reCAPTCHA HTML widget - * is embedded within the HTML form it was called from. - * @param string $pubkey A public key for reCAPTCHA - * @param string $error The error given by reCAPTCHA (optional, default is null) - * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) - - * @return string - The HTML to be embedded in the user's form. - */ -function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) -{ - if ($pubkey == null || $pubkey == '') { - die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>"); - } - - if ($use_ssl) { - $server = RECAPTCHA_API_SECURE_SERVER; - } else { - $server = RECAPTCHA_API_SERVER; - } - - $errorpart = ""; - if ($error) { - $errorpart = "&error=" . $error; - } - return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script> - - <noscript> - <iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/> - <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea> - <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/> - </noscript>'; -} - - - - -/** - * A ReCaptchaResponse is returned from recaptcha_check_answer() - * - * @package users - */ -class ReCaptchaResponse { - var $is_valid; - var $error; -} - - -/** - * Calls an HTTP POST function to verify if the user's guess was correct - * @param string $privkey - * @param string $remoteip - * @param string $challenge - * @param string $response - * @param array $extra_params an array of extra variables to post to the server - * @return ReCaptchaResponse - */ -function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) -{ - if ($privkey == null || $privkey == '') { - die ("To use reCAPTCHA you must get an API key from <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>"); - } - - if ($remoteip == null || $remoteip == '') { - die ("For security reasons, you must pass the remote ip to reCAPTCHA"); - } - - - - //discard spam submissions - if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { - $recaptcha_response = new ReCaptchaResponse(); - $recaptcha_response->is_valid = false; - $recaptcha_response->error = 'incorrect-captcha-sol'; - return $recaptcha_response; - } - - $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", - array ( - 'privatekey' => $privkey, - 'remoteip' => $remoteip, - 'challenge' => $challenge, - 'response' => $response - ) + $extra_params - ); - - $answers = explode ("\n", $response [1]); - $recaptcha_response = new ReCaptchaResponse(); - - if (trim ($answers [0]) == 'true') { - $recaptcha_response->is_valid = true; - } - else { - $recaptcha_response->is_valid = false; - $recaptcha_response->error = $answers [1]; - } - return $recaptcha_response; - -} - -/** - * gets a URL where the user can sign up for reCAPTCHA. If your application - * has a configuration page where you enter a key, you should provide a link - * using this function. - * @param string $domain The domain where the page is hosted - * @param string $appname The name of your application - */ -function recaptcha_get_signup_url ($domain = null, $appname = null) { - return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); -} - -function _recaptcha_aes_pad($val) { - $block_size = 16; - $numpad = $block_size - (strlen ($val) % $block_size); - return str_pad($val, strlen ($val) + $numpad, chr($numpad)); -} - -/* Mailhide related code */ - -function _recaptcha_aes_encrypt($val,$ky) { - if (! function_exists ("mcrypt_encrypt")) { - die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); - } - $mode=MCRYPT_MODE_CBC; - $enc=MCRYPT_RIJNDAEL_128; - $val=_recaptcha_aes_pad($val); - return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); -} - - -function _recaptcha_mailhide_urlbase64 ($x) { - return strtr(base64_encode ($x), '+/', '-_'); -} - -/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ -function recaptcha_mailhide_url($pubkey, $privkey, $email) { - if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { - die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . - "you can do so at <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>"); - } - - - $ky = pack('H*', $privkey); - $cryptmail = _recaptcha_aes_encrypt ($email, $ky); - - return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); -} - -/** - * gets the parts of the email to expose to the user. - * eg, given johndoe@example,com return ["john", "example.com"]. - * the email is then displayed as john...@example.com - */ -function _recaptcha_mailhide_email_parts ($email) { - $arr = preg_split("/@/", $email ); - - if (strlen ($arr[0]) <= 4) { - $arr[0] = substr ($arr[0], 0, 1); - } else if (strlen ($arr[0]) <= 6) { - $arr[0] = substr ($arr[0], 0, 3); - } else { - $arr[0] = substr ($arr[0], 0, 4); - } - return $arr; -} - -/** - * Gets html to display an email address given a public an private key. - * to get a key, go to: - * - * http://www.google.com/recaptcha/mailhide/apikey - */ -function recaptcha_mailhide_html($pubkey, $privkey, $email) { - $emailparts = _recaptcha_mailhide_email_parts ($email); - $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); - - return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) . - "' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]); - -} - - -?> |
