diff --git a/src/Pluf/Form/Field/ReCaptcha.php b/src/Pluf/Form/Field/ReCaptcha.php new file mode 100644 index 0000000..4724e57 --- /dev/null +++ b/src/Pluf/Form/Field/ReCaptcha.php @@ -0,0 +1,170 @@ + + * $ssl = (!empty($extra['request']->SERVER['HTTPS']) + * and $extra['request']->SERVER['HTTPS'] != 'off'); + * + * $this->fields['recaptcha'] = new Pluf_Form_Field_ReCaptcha( + * array('required' => true, + * 'label' => __('Please solve this challenge'), + * 'privkey' => 'PRIVATE_RECAPTCHA_KEY_HERE', + * 'remoteip' => $extra['request']->remote_addr, + * 'widget_attrs' => array( + * 'pubkey' => 'PUBLIC_RECAPTCHA_KEY_HERE', + * ), + * )); + * + * + * Then in your template, you simply need to add the ReCaptcha field: + * + *
+ * {if $form.f.recaptcha.errors}{$form.f.recaptcha.fieldErrors}{/if}
+ * {$form.f.recaptcha|safe}
+ *
+ *
+ * Based on http://recaptcha.googlecode.com/files/recaptcha-php-1.10.zip
+ *
+ * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
+ * AUTHORS:
+ * Mike Crawford
+ * Ben Maurer
+ */
+class Pluf_Form_Field_ReCaptcha extends Pluf_Form_Field
+{
+ public $widget = 'Pluf_Form_Widget_ReCaptcha';
+ public $privkey = '';
+ public $remoteip = '';
+ public $extra_params = array();
+
+ public function clean($value)
+ {
+ // will throw the Pluf_Form_Invalid exception in case of
+ // error.
+ self::checkAnswer($this->privkey, $this->remoteip,
+ $value[0], $value[1], $this->extra_params);
+ return $value;
+ }
+
+ /**
+ * Submits an HTTP POST to a reCAPTCHA server
+ *
+ * @param string Host
+ * @param string Path
+ * @param array Data
+ * @param int port (80
+ * @return array response
+ */
+ public static function httpPost($host, $path, $data, $port=80)
+ {
+
+ $req = self::qsencode($data);
+ $http_request = "POST $path HTTP/1.0\r\n";
+ $http_request .= "Host: $host\r\n";
+ $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
+ $http_request .= "Content-Length: " . strlen($req) . "\r\n";
+ $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
+ $http_request .= "\r\n";
+ $http_request .= $req;
+
+ if (false === ($fs=@fsockopen($host, $port, $errno, $errstr, 10))) {
+ throw new Pluf_Form_Invalid(__('Cannot connect to the reCaptcha server for validation.'));
+ }
+ fwrite($fs, $http_request);
+ $response = '';
+ while (!feof($fs)) {
+ $response .= fgets($fs, 1160); // One TCP-IP packet
+ }
+ fclose($fs);
+ return explode("\r\n\r\n", $response, 2);
+ }
+
+ /**
+ * Encodes the given data into a query string format
+ *
+ * @param array Array of string elements to be encoded
+ * @return string Encoded request
+ */
+ public static function qsencode($data)
+ {
+ $d = array();
+ foreach ($data as $key => $value) {
+ $d[] = $key.'='.urlencode(stripslashes($value));
+ }
+ return implode('&', $d);
+ }
+
+ /**
+ * Calls an HTTP POST function to verify if the user's guess was correct
+ * @param string $privkey
+ * @param string $remoteip
+ * @param string $challenge
+ * @param string $response
+ * @param array $extra_params an array of extra variables to post to the server
+ * @return ReCaptchaResponse
+ */
+ public static function checkAnswer($privkey, $remoteip, $challenge, $response, $extra_params=array())
+ {
+ if ($privkey == '') {
+ throw new Pluf_Form_Invalid(__('To use reCAPTCHA you must set your API key.'));
+ }
+ if ($remoteip == '') {
+ throw new Pluf_Form_Invalid(__('For security reasons, you must pass the remote ip to reCAPTCHA.'));
+ }
+ //discard spam submissions
+ if (strlen($challenge) == 0 || strlen($response) == 0) {
+ return false;
+ }
+
+ $response = self::httpPost('api-verify.recaptcha.net', '/verify',
+ array(
+ 'privatekey' => $privkey,
+ 'remoteip' => $remoteip,
+ 'challenge' => $challenge,
+ 'response' => $response
+ ) + $extra_params
+ );
+
+ $answers = explode("\n", $response[1]);
+ if (trim($answers[0]) == 'true') {
+ return true;
+ } else {
+ throw new Pluf_Form_Invalid($answers[1]);
+ }
+ }
+}
diff --git a/src/Pluf/Form/Widget/ReCaptcha.php b/src/Pluf/Form/Widget/ReCaptcha.php
new file mode 100644
index 0000000..5d1e0c0
--- /dev/null
+++ b/src/Pluf/Form/Widget/ReCaptcha.php
@@ -0,0 +1,106 @@
+attrs['pubkey']));
+ }
+
+ /**
+ * Gets the challenge HTML (javascript and non-javascript
+ * version). This is called from the browser, and the resulting
+ * reCAPTCHA HTML widget is embedded within the HTML form it was
+ * called from.
+ *
+ * @param string A public key for reCAPTCHA
+ * @param string The error given by reCAPTCHA (null)
+ * @param boolean Should the request be made over ssl? (false)
+ * @return string The HTML to be embedded in the user's form.
+ */
+ public static function getHtml($pubkey, $error=null, $use_ssl=false)
+ {
+ $server = ($use_ssl) ? 'https://api-secure.recaptcha.net'
+ : 'http://api.recaptcha.net';
+ $errorpart = ($error) ? '&error='.$error : '';
+
+ return '
+ ';
+ }
+
+ /**
+ * Get the form data from the reCaptcha fields.
+ *
+ * We need to get back two fields from the POST request
+ * 'recaptcha_challenge_field' and 'recaptcha_response_field'.
+ *
+ * They are hardcoded, so we do not even bother checking something
+ * else.
+ *
+ * @param string Name of the form
+ * @param array Submitted form data
+ * @return array Challenge and answer
+ */
+ public function valueFromFormData($name, $data)
+ {
+ $res = array('', '');
+ $res[0] = isset($data['recaptcha_challenge_field'])
+ ? $data['recaptcha_challenge_field'] : '';
+ $res[1] = isset($data['recaptcha_response_field'])
+ ? $data['recaptcha_response_field'] : '';
+ return $res;
+ }
+}