qa-recaptcha-captcha.php 5 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 66
			'fields' => array(
				'public' => array(
					'label' => 'reCAPTCHA public key:',
Scott committed
67
					'value' => $pub,
Scott committed
68
					'tags' => 'name="recaptcha_public_key_field"',
Scott committed
69 70
				),

Scott committed
71 72
				'private' => array(
					'label' => 'reCAPTCHA private 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
	/**
	 * Return HTML for reCAPTCHA, including non-JS fallback. New reCAPTCHA auto-detects the user's language.
	 */
Scott committed
104 105
	public function form_html(&$qa_content, $error)
	{
Scott committed
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
		$pub = qa_opt('recaptcha_public_key');

		$htmlLines = array(
			// currently we cannot add async/defer attributes via $qa_content so we insert script here
			'<script src="https://www.google.com/recaptcha/api.js" async defer></script>',
			'<div class="g-recaptcha" data-sitekey="'.$pub.'"></div>',

			// non-JS falback
			'<noscript>',
			'  <div style="width: 302px; height: 352px;">',
			'    <div style="width: 302px; height: 352px; position: relative;">',
			'      <div style="width: 302px; height: 352px; position: absolute;">',
			'        <iframe src="https://www.google.com/recaptcha/api/fallback?k='.$pub.'"',
			'                frameborder="0" scrolling="no"',
			'                style="width: 302px; height:352px; border-style: none;">',
			'        </iframe>',
			'      </div>',
			'      <div style="width: 250px; height: 80px; position: absolute; border-style: none;',
			'                  bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">',
			'        <textarea id="g-recaptcha-response" name="g-recaptcha-response"',
			'                  class="g-recaptcha-response"',
			'                  style="width: 250px; height: 80px; border: 1px solid #c1c1c1;',
			'                         margin: 0px; padding: 0px; resize: none;" value=""></textarea>',
			'      </div>',
			'    </div>',
			'  </div>',
			'</noscript>',
Scott committed
133
		);
Scott committed
134

Scott committed
135
		return implode("\n", $htmlLines);
Scott committed
136
	}
Scott committed
137

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

Scott committed
146 147 148
		$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key'));
		$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
}