Merge pull request #6006 from WoltLab/file-processor-can-adopt
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / captcha / RecaptchaHandler.class.php
CommitLineData
96714cab 1<?php
a9229942 2
96714cab 3namespace wcf\system\captcha;
a9229942 4
5e0b820a
TD
5use GuzzleHttp\ClientInterface;
6use GuzzleHttp\Psr7\Request;
7use Psr\Http\Client\ClientExceptionInterface;
b763de7a 8use wcf\system\exception\UserInputException;
5e0b820a 9use wcf\system\io\HttpFactory;
96714cab 10use wcf\system\WCF;
b763de7a
TD
11use wcf\util\JSON;
12use wcf\util\UserUtil;
96714cab
MS
13
14/**
15 * Captcha handler for reCAPTCHA.
a9229942
TD
16 *
17 * @author Tim Duesterhus, Matthias Schmidt
18 * @copyright 2001-2020 WoltLab GmbH
19 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
96714cab 20 */
a9229942
TD
21class RecaptchaHandler implements ICaptchaHandler
22{
23 /**
24 * recaptcha challenge
25 * @var string
26 */
27 public $challenge = '';
28
29 /**
30 * response to the challenge
31 * @var string
32 */
33 public $response = '';
34
35 /**
36 * ACP option override
37 * @var bool
38 */
39 public static $forceIsAvailable = false;
40
41 /**
42 * @inheritDoc
43 */
44 public function getFormElement()
45 {
46 if (WCF::getSession()->getVar('recaptchaDone')) {
47 return '';
48 }
49
50 WCF::getTPL()->assign([
51 'recaptchaLegacyMode' => true,
52 ]);
53
b54fdcc0 54 return WCF::getTPL()->fetch('shared_recaptcha');
a9229942
TD
55 }
56
57 /**
58 * @inheritDoc
59 */
60 public function isAvailable()
61 {
62 if (!RECAPTCHA_PUBLICKEY || !RECAPTCHA_PRIVATEKEY) {
63 // OEM keys are no longer supported, disable reCAPTCHA
64 if (self::$forceIsAvailable) {
65 // work-around for the ACP option selection
66 return true;
67 }
68
69 return false;
70 }
71
72 return true;
73 }
74
75 /**
76 * @inheritDoc
77 */
78 public function readFormParameters()
79 {
80 if (isset($_POST['recaptcha-type'])) {
81 $this->challenge = $_POST['recaptcha-type'];
27454d07 82 } elseif (isset($_POST['parameters']['recaptcha-type'])) {
1bb8616e 83 $this->challenge = $_POST['parameters']['recaptcha-type'];
a9229942
TD
84 }
85 if (isset($_POST['g-recaptcha-response'])) {
86 $this->response = $_POST['g-recaptcha-response'];
27454d07 87 } elseif (isset($_POST['parameters']['g-recaptcha-response'])) {
1bb8616e 88 $this->response = $_POST['parameters']['g-recaptcha-response'];
a9229942
TD
89 }
90 }
91
92 /**
93 * @inheritDoc
94 */
95 public function reset()
96 {
97 WCF::getSession()->unregister('recaptchaDone');
98 }
99
100 /**
101 * @inheritDoc
102 */
103 public function validate()
104 {
105 if (WCF::getSession()->getVar('recaptchaDone')) {
106 return;
107 }
108
109 // fail if response is empty to avoid sending api requests
110 if (empty($this->response)) {
111 throw new UserInputException('recaptchaString', 'false');
112 }
113
114 $type = $this->challenge ?: 'v2';
115
116 if ($type === 'v2') {
117 $key = RECAPTCHA_PRIVATEKEY;
118 } elseif ($type === 'invisible') {
119 $key = RECAPTCHA_PRIVATEKEY_INVISIBLE;
120 } else {
067f2d1d 121 // The bot modified the `recaptcha-type` form field.
122 throw new UserInputException('recaptchaString', 'false');
a9229942
TD
123 }
124
5e0b820a
TD
125 $request = new Request(
126 'GET',
a9229942 127 \sprintf(
ddd2a38e 128 'https://www.google.com/recaptcha/api/siteverify?%s',
5e0b820a
TD
129 \http_build_query([
130 'secret' => $key,
131 'response' => $this->response,
132 'remoteip' => UserUtil::getIpAddress(),
133 ], '', '&')
134 )
a9229942
TD
135 );
136
137 try {
5e0b820a
TD
138 $response = $this->getHttpClient()->send($request);
139 $data = JSON::decode((string)$response->getBody());
a9229942
TD
140
141 if ($data['success']) {
142 // yeah
143 } else {
144 throw new UserInputException('recaptchaString', 'false');
145 }
5e0b820a 146 } catch (ClientExceptionInterface $e) {
a9229942
TD
147 // log error, but accept captcha
148 \wcf\functions\exception\logThrowable($e);
149 }
150
151 WCF::getSession()->register('recaptchaDone', true);
152 }
5e0b820a
TD
153
154 private function getHttpClient(): ClientInterface
155 {
156 return HttpFactory::makeClientWithTimeout(5);
157 }
96714cab 158}