summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/ConfirmEdit/ReCaptcha/ReCaptcha.class.php
blob: a62633e0bcc372f68f1c40e22832addaf2e8fcfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<?php

use MediaWiki\Auth\AuthenticationRequest;

class ReCaptcha extends SimpleCaptcha {
	// used for recaptcha-edit, recaptcha-addurl, recaptcha-badlogin, recaptcha-createaccount,
	// recaptcha-create, recaptcha-sendemail via getMessage()
	protected static $messagePrefix = 'recaptcha-';

	// reCAPTHCA error code returned from recaptcha_check_answer
	private $recaptcha_error = null;

	/**
	 * Displays the reCAPTCHA widget.
	 * If $this->recaptcha_error is set, it will display an error in the widget.
	 * @param int $tabIndex
	 * @return array
	 */
	function getFormInformation( $tabIndex = 1 ) {
		global $wgReCaptchaPublicKey, $wgReCaptchaTheme;

		wfDeprecated( 'ConfirmEdit module ReCaptcha', '1.28' );
		$useHttps = ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' );
		$js = 'var RecaptchaOptions = ' . Xml::encodeJsVar(
			[ 'theme' => $wgReCaptchaTheme, 'tabindex' => $tabIndex ]
		);

		return [
			'html' => Html::inlineScript( $js ) .
				recaptcha_get_html( $wgReCaptchaPublicKey, $this->recaptcha_error, $useHttps )
		];
	}

	/**
	 * @param WebRequest $request
	 * @return array
	 */
	protected function getCaptchaParamsFromRequest( WebRequest $request ) {
		// API is hardwired to return captchaId and captchaWord,
		// so use that if the standard two are empty
		$challenge = $request->getVal( 'recaptcha_challenge_field', $request->getVal( 'captchaId' ) );
		$response = $request->getVal( 'recaptcha_response_field', $request->getVal( 'captchaWord' ) );
		return [ $challenge, $response ];
	}

	/**
	 * Calls the library function recaptcha_check_answer to verify the users input.
	 * Sets $this->recaptcha_error if the user is incorrect.
	 * @param string $challenge Challenge value
	 * @param string $response Response value
	 * @return bool
	 */
	function passCaptcha( $challenge, $response ) {
		global $wgReCaptchaPrivateKey, $wgRequest;

		if ( $response === null ) {
			// new captcha session
			return false;
		}

		$ip = $wgRequest->getIP();

		$recaptcha_response =
			recaptcha_check_answer( $wgReCaptchaPrivateKey, $ip, $challenge, $response );

		if ( !$recaptcha_response->is_valid ) {
			$this->recaptcha_error = $recaptcha_response->error;
			return false;
		}

		$recaptcha_error = null;

		return true;
	}

	/**
	 * @param array &$resultArr
	 */
	function addCaptchaAPI( &$resultArr ) {
		$resultArr['captcha'] = $this->describeCaptchaType();
		$resultArr['captcha']['error'] = $this->recaptcha_error;
	}

	/**
	 * @return array
	 */
	public function describeCaptchaType() {
		global $wgReCaptchaPublicKey;
		return [
			'type' => 'recaptcha',
			'mime' => 'image/png',
			'key' => $wgReCaptchaPublicKey,
		];
	}

	/**
	 * @param ApiBase &$module
	 * @param array &$params
	 * @param int $flags
	 * @return bool
	 */
	public function APIGetAllowedParams( &$module, &$params, $flags ) {
		if ( $flags && $this->isAPICaptchaModule( $module ) ) {
			$params['recaptcha_challenge_field'] = [
				ApiBase::PARAM_HELP_MSG => 'recaptcha-apihelp-param-recaptcha_challenge_field',
			];
			$params['recaptcha_response_field'] = [
				ApiBase::PARAM_HELP_MSG => 'recaptcha-apihelp-param-recaptcha_response_field',
			];
		}

		return true;
	}

	/**
	 * @return null
	 */
	public function getError() {
		// do not treat failed captcha attempts as errors
		if ( in_array( $this->recaptcha_error, [
			'invalid-request-cookie', 'incorrect-captcha-sol',
		], true ) ) {
			return null;
		}

		return $this->recaptcha_error;
	}

	public function storeCaptcha( $info ) {
		// ReCaptcha is stored by Google; the ID will be generated at that time as well, and
		// the one returned here won't be used. Just pretend this worked.
		return 'not used';
	}

	public function retrieveCaptcha( $index ) {
		// just pretend it worked
		return [ 'index' => $index ];
	}

	public function getCaptcha() {
		// ReCaptcha is handled by frontend code + an external provider; nothing to do here.
		return [];
	}

	/**
	 * @param array $captchaData
	 * @param string $id
	 * @return Message
	 */
	public function getCaptchaInfo( $captchaData, $id ) {
		return wfMessage( 'recaptcha-info' );
	}

	/**
	 * @return ReCaptchaAuthenticationRequest
	 */
	public function createAuthenticationRequest() {
		return new ReCaptchaAuthenticationRequest();
	}

	/**
	 * @param array $requests
	 * @param array $fieldInfo
	 * @param array &$formDescriptor
	 * @param string $action
	 */
	public function onAuthChangeFormFields(
		array $requests, array $fieldInfo, array &$formDescriptor, $action
	) {
		global $wgReCaptchaPublicKey, $wgReCaptchaTheme;

		$req = AuthenticationRequest::getRequestByClass( $requests,
			CaptchaAuthenticationRequest::class, true );
		if ( !$req ) {
			return;
		}

		// ugly way to retrieve error information
		$captcha = ConfirmEditHooks::getInstance();

		$formDescriptor['captchaInfo'] = [
			'class' => HTMLReCaptchaField::class,
			'key' => $wgReCaptchaPublicKey,
			'theme' => $wgReCaptchaTheme,
			'secure' => isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on',
			'error' => $captcha->getError(),
		] + $formDescriptor['captchaInfo'];

		// the custom form element cannot return multiple fields; work around that by
		// "redirecting" ReCaptcha names to standard names
		$formDescriptor['captchaId'] = [
			'class' => HTMLSubmittedValueField::class,
			'name' => 'recaptcha_challenge_field',
		];
		$formDescriptor['captchaWord'] = [
			'class' => HTMLSubmittedValueField::class,
			'name' => 'recaptcha_response_field',
		];
	}
}