qa-recaptcha-captcha.php 4.52 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
		$this->directory = $directory;

		// human-readable error messages (though these are not currently displayed anywhere)
		$this->errorCodeMessages = array(
34 35 36 37
			'missing-input-secret' => qa_lang_html('recaptcha/missing_secret'),
			'invalid-input-secret' => qa_lang_html('recaptcha/invalid_secret'),
			'missing-input-response' => qa_lang_html('recaptcha/missing_response'),
			'invalid-input-response' => qa_lang_html('recaptcha/invalid_response'),
Scott committed
38
		);
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
		$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';
Scott committed
58
			$error = strtr(qa_lang_html('recaptcha/must_sign_up'), array(
59 60 61
				'^1' => '<a href="'.qa_html(ReCaptcha::getSignupUrl()).'" target="_blank">',
				'^2' => '</a>'
			));
Scott committed
62 63 64
		}

		$form = array(
65
			'ok' => $saved ? qa_lang_html('admin/options_saved') : null,
Scott committed
66

Scott committed
67 68
			'fields' => array(
				'public' => array(
69
					'label' => qa_lang_html('recaptcha/site_key'),
Scott committed
70
					'value' => $pub,
Scott committed
71
					'tags' => 'name="recaptcha_public_key_field"',
Scott committed
72 73
				),

Scott committed
74
				'private' => array(
75
					'label' => qa_lang_html('recaptcha/secret_key'),
Scott committed
76
					'value' => $pri,
Scott committed
77
					'tags' => 'name="recaptcha_private_key_field"',
Scott committed
78
					'error' => $error,
Scott committed
79
				),
Scott committed
80
			),
Scott committed
81

Scott committed
82 83
			'buttons' => array(
				array(
84
					'label' => qa_lang_html('main/save_button'),
Scott committed
85 86 87 88
					'tags' => 'name="recaptcha_save_button"',
				),
			),
		);
Scott committed
89

Scott committed
90 91
		return $form;
	}
Scott committed
92

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

Scott committed
101 102
		return strlen($pub) && strlen($pri);
	}
Scott committed
103

Scott committed
104
	/**
105 106
	 * 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
107
	 */
Scott committed
108 109
	public function form_html(&$qa_content, $error)
	{
Scott committed
110 111
		$pub = qa_opt('recaptcha_public_key');

112 113 114 115 116 117 118 119 120 121 122 123
		// 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
124
		);
Scott committed
125

126 127 128 129
		$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;
130 131

		return '';
Scott committed
132
	}
Scott committed
133

Scott committed
134 135 136 137
	/**
	 * 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
138 139
	public function validate_post(&$error)
	{
Scott committed
140
		require_once $this->directory.'recaptchalib.php';
Scott committed
141

Amiya committed
142 143 144
		if (ini_get('allow_url_fopen'))
			$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key'));
		else
145
			$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key'), new ReCaptchaSocketPostRequestMethod());
Amiya committed
146

Scott committed
147 148
		$remoteIp = qa_remote_ip_address();
		$userResponse = qa_post_text('g-recaptcha-response');
Scott committed
149

Scott committed
150
		$recResponse = $recaptcha->verifyResponse($remoteIp, $userResponse);
Scott committed
151

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

Scott committed
157
		return $recResponse->success;
Scott committed
158
	}
Scott committed
159
}