qa-recaptcha-captcha.php 4.44 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	File: qa-plugin/recaptcha-captcha/qa-recaptcha-captcha.php
	Description: Captcha module for reCAPTCHA


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

Scott committed
23 24 25
class qa_recaptcha_captcha
{
	private $directory;
Scott committed
26
	private $errorCodeMessages;
Scott committed
27 28 29

	public function load_module($directory, $urltoroot)
	{
Scott committed
30 31 32 33 34 35 36 37 38
		$this->directory = $directory;

		// human-readable error messages (though these are not currently displayed anywhere)
		$this->errorCodeMessages = array(
			'missing-input-secret' => 'The secret parameter is missing.',
			'invalid-input-secret' => 'The secret parameter is invalid or malformed.',
			'missing-input-response' => 'The response parameter is missing.',
			'invalid-input-response' => 'The response parameter is invalid or malformed.',
		);
Scott committed
39 40
	}

Scott committed
41 42
	public function admin_form()
	{
Scott committed
43
		$saved = false;
Scott committed
44

Scott committed
45 46 47
		if (qa_clicked('recaptcha_save_button')) {
			qa_opt('recaptcha_public_key', qa_post_text('recaptcha_public_key_field'));
			qa_opt('recaptcha_private_key', qa_post_text('recaptcha_private_key_field'));
Scott committed
48

Scott committed
49
			$saved = true;
Scott committed
50 51
		}

Scott committed
52 53 54 55 56 57 58 59 60 61
		$pub = trim(qa_opt('recaptcha_public_key'));
		$pri = trim(qa_opt('recaptcha_private_key'));

		$error = null;
		if (!strlen($pub) || !strlen($pri)) {
			require_once $this->directory.'recaptchalib.php';
			$error = 'To use reCAPTCHA, you must <a href="'.qa_html(ReCaptcha::getSignupUrl()).'" target="_blank">sign up</a> to get these keys.';
		}

		$form = array(
Scott committed
62
			'ok' => $saved ? 'reCAPTCHA settings saved' : null,
Scott committed
63

Scott committed
64 65
			'fields' => array(
				'public' => array(
66
					'label' => 'reCAPTCHA Site key:',
Scott committed
67
					'value' => $pub,
Scott committed
68
					'tags' => 'name="recaptcha_public_key_field"',
Scott committed
69 70
				),

Scott committed
71
				'private' => array(
72
					'label' => 'reCAPTCHA Secret key:',
Scott committed
73
					'value' => $pri,
Scott committed
74
					'tags' => 'name="recaptcha_private_key_field"',
Scott committed
75
					'error' => $error,
Scott committed
76
				),
Scott committed
77
			),
Scott committed
78

Scott committed
79 80 81 82 83 84 85
			'buttons' => array(
				array(
					'label' => 'Save Changes',
					'tags' => 'name="recaptcha_save_button"',
				),
			),
		);
Scott committed
86

Scott committed
87 88
		return $form;
	}
Scott committed
89

Scott committed
90 91 92
	/**
	 * Only allow reCAPTCHA if the keys are set up (new reCAPTCHA has no special requirements)
	 */
Scott committed
93 94
	public function allow_captcha()
	{
Scott committed
95 96
		$pub = trim(qa_opt('recaptcha_public_key'));
		$pri = trim(qa_opt('recaptcha_private_key'));
Scott committed
97

Scott committed
98 99
		return strlen($pub) && strlen($pri);
	}
Scott committed
100

Scott committed
101
	/**
102 103
	 * reCAPTCHA HTML - we actually return nothing because the new reCAPTCHA requires 'explicit rendering'
	 * via JavaScript when we have multiple Captchas per page. It also auto-detects the user's language.
Scott committed
104
	 */
Scott committed
105 106
	public function form_html(&$qa_content, $error)
	{
Scott committed
107 108
		$pub = qa_opt('recaptcha_public_key');

109 110 111 112 113 114 115 116 117 118 119 120
		// onload handler
		$qa_content['script_lines'][] = array(
			'function recaptcha_load(elemId) {',
			'  if (grecaptcha) {',
			'    grecaptcha.render(elemId, {',
			'      "sitekey": ' . qa_js($pub),
			'    });',
			'  }',
			'}',
			'function recaptcha_onload() {',
			'  recaptcha_load("qa_captcha_div_1");',
			'}',
Scott committed
121
		);
Scott committed
122

123 124 125 126
		$lang = urlencode(qa_opt('site_language'));
		if (empty($lang))
			$lang = 'en';
		$qa_content['script_src'][] = 'https://www.google.com/recaptcha/api.js?onload=recaptcha_onload&render=explicit&hl='.$lang;
127 128

		return '';
Scott committed
129
	}
Scott committed
130

Scott committed
131 132 133 134
	/**
	 * Check that the CAPTCHA was entered correctly. reCAPTCHA sets a long string in 'g-recaptcha-response'
	 * when the CAPTCHA is completed; we check that with the reCAPTCHA API.
	 */
Scott committed
135 136
	public function validate_post(&$error)
	{
Scott committed
137
		require_once $this->directory.'recaptchalib.php';
Scott committed
138

Amiya committed
139 140 141 142 143
		if (ini_get('allow_url_fopen'))
			$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key'));
		else
			$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key') , new ReCaptchaSocketPostRequestMethod());

Scott committed
144 145
		$remoteIp = qa_remote_ip_address();
		$userResponse = qa_post_text('g-recaptcha-response');
Scott committed
146

Scott committed
147
		$recResponse = $recaptcha->verifyResponse($remoteIp, $userResponse);
Scott committed
148

Scott committed
149 150 151
		foreach ($recResponse->errorCodes as $code) {
			if (isset($this->errorCodeMessages[$code]))
				$error .= $this->errorCodeMessages[$code] . "\n";
Scott committed
152 153
		}

Scott committed
154
		return $recResponse->success;
Scott committed
155
	}
Scott committed
156
}