2 namespace wcf\system\recaptcha
;
3 use wcf\system\exception\SystemException
;
4 use wcf\system\exception\UserInputException
;
5 use wcf\system\request\RouteHandler
;
6 use wcf\system\SingletonFactory
;
8 use wcf\util\HTTPRequest
;
9 use wcf\util\StringUtil
;
10 use wcf\util\UserUtil
;
13 * Handles reCAPTCHA support.
15 * Based upon reCAPTCHA-plugin originally created in 2010 by Markus Bartz <roul@codingcorner.info>
16 * and released under the conditions of the GNU Lesser General Public License.
18 * @author Alexander Ebert
19 * @copyright 2001-2019 WoltLab GmbH
20 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
21 * @package WoltLabSuite\Core\System\Recaptcha
23 class RecaptchaHandler
extends SingletonFactory
{
25 * list of supported languages
27 * @see http://code.google.com/intl/de-DE/apis/recaptcha/docs/customization.html#i18n
29 protected $supportedLanguages = ['de', 'en', 'es', 'fr', 'nl', 'pt', 'ru', 'tr'];
35 protected $languageCode = '';
41 protected $publicKey = '';
47 protected $privateKey = '';
49 // reply codes (see <http://code.google.com/intl/de-DE/apis/recaptcha/docs/verify.html>)
50 const VALID_ANSWER
= 'valid';
51 const ERROR_UNKNOWN
= 'unknown';
52 const ERROR_INVALID_PUBLICKEY
= 'invalid-site-public-key';
53 const ERROR_INVALID_PRIVATEKEY
= 'invalid-site-private-key';
54 const ERROR_INVALID_COOKIE
= 'invalid-request-cookie';
55 const ERROR_INCORRECT_SOLUTION
= 'incorrect-captcha-sol';
56 const ERROR_INCORRECT_PARAMS
= 'verify-params-incorrect';
57 const ERROR_INVALID_REFFERER
= 'invalid-referrer';
58 const ERROR_NOT_REACHABLE
= 'recaptcha-not-reachable';
59 const ERROR_TIMEOUT_OR_DUPLICATE
= 'timeout-or-duplicate';
64 protected function init() {
65 // set appropriate language code, fallback to EN if language code is not known to reCAPTCHA-API
66 $this->languageCode
= WCF
::getLanguage()->getFixedLanguageCode();
67 if (!in_array($this->languageCode
, $this->supportedLanguages
)) {
68 $this->languageCode
= 'en';
71 // WoltLab's V1 OEM keys
72 $this->publicKey
= '6LfOlMYSAAAAADvo3s4puBAYDqI-6YK2ybe7BJE5';
73 $this->privateKey
= '6LfOlMYSAAAAAKR3m_EFxmDv1xS8PCfeaSZ2LdG9';
77 * Validates response against given challenge.
79 * @param string $challenge
80 * @param string $response
81 * @throws SystemException
82 * @throws UserInputException
84 public function validate($challenge, $response) {
85 // fail if challenge or response are empty to avoid sending api requests
86 if (empty($challenge) ||
empty($response)) {
87 throw new UserInputException('recaptchaString', 'false');
90 $response = $this->verify($challenge, $response);
92 case self
::VALID_ANSWER
:
95 case self
::ERROR_INCORRECT_SOLUTION
:
96 throw new UserInputException('recaptchaString', 'false');
99 case self
::ERROR_NOT_REACHABLE
:
100 // if reCaptcha server is unreachable mark captcha as done
101 // this should be better than block users until server is back.
105 case self
::ERROR_INVALID_COOKIE
:
106 // do not throw a system exception, if validation fails
107 // while javascript is disabled. Otherwise, bots may produce
108 // a lot of log entries.
109 throw new UserInputException('recaptchaString', 'false');
112 case self
::ERROR_TIMEOUT_OR_DUPLICATE
:
113 // User was to slow to fill the captcha.
114 throw new UserInputException('recaptchaString', 'false');
118 throw new SystemException('reCAPTCHA returned the following error: '.$response);
121 WCF
::getSession()->register('recaptchaDone', true);
125 * Queries server to verify successful response.
127 * @param string $challenge
128 * @param string $response
131 protected function verify($challenge, $response) {
132 $request = new HTTPRequest('http://www.google.com/recaptcha/api/verify', ['timeout' => 10], [
133 'privatekey' => $this->privateKey
,
134 'remoteip' => UserUtil
::getIpAddress(),
135 'challenge' => $challenge,
136 'response' => $response
141 $reply = $request->getReply();
142 $reCaptchaResponse = explode("\n", $reply['body']);
144 if (StringUtil
::trim($reCaptchaResponse[0]) === "true") {
145 return self
::VALID_ANSWER
;
148 return StringUtil
::trim($reCaptchaResponse[1]);
151 catch (SystemException
$e) {
152 return self
::ERROR_NOT_REACHABLE
;
157 * Assigns template variables for reCAPTCHA.
159 public function assignVariables() {
160 WCF
::getTPL()->assign([
161 'recaptchaLanguageCode' => $this->languageCode
,
162 'recaptchaPublicKey' => $this->publicKey
,
163 'recaptchaUseSSL' => RouteHandler
::secureConnection(), // @deprecated 2.1
164 'recaptchaLegacyMode' => true