Skip to content
Browse files

API CHANGE Using curl instead of fsockopen() for HTTP communication i…

…nside RecaptchaField

ENHANCEMENT Making RecaptchaField http requests mockable by introducing RecaptchaField_HTTPClient. Added unit tests
MINOR Added package phpdoc info
  • Loading branch information...
1 parent b1cfbae commit cedae82b46159bd6f2954e6964216f322471c7ab @chillu committed May 14, 2010
Showing with 106 additions and 39 deletions.
  1. +1 −0 README.md
  2. +3 −1 _config.php
  3. +59 −35 code/RecaptchaField.php
  4. +3 −3 code/RecaptchaProtector.php
  5. +40 −0 tests/unit/RecatpchaFieldTest.php
View
1 README.md
@@ -13,6 +13,7 @@ by giving them a challenge to decrypt an image.
## Requirements
* SilverStripe 2.3 or newer
+ * curl PHP module
* Requires [SpamProtectionModule](http://silverstripe.org/spam-protection-module/)
## Developer Documentation
View
4 _config.php
@@ -1,3 +1,5 @@
<?php
-
+/**
+ * @package recaptcha
+ */
?>
View
94 code/RecaptchaField.php
@@ -1,5 +1,9 @@
<?php
/**
+ * @package recaptcha
+ */
+
+/**
* Provides an {@link FormField} which allows form to validate for non-bot submissions
* by giving them a challenge to decrypt an image.
* Generation and validation of captchas is handled on external server.
@@ -16,8 +20,6 @@
*
* @see http://recaptcha.net
* @see http://recaptcha.net/api/getkey
- *
- * @module recaptcha
*/
class RecaptchaField extends SpamProtectorField {
@@ -110,6 +112,11 @@ class RecaptchaField extends SpamProtectorField {
public static $recaptcha_ajax_url = "http://api.recaptcha.net/js/recaptcha_ajax.js";
/**
+ * @var string
+ */
+ public static $httpclient_class = 'RecaptchaField_HTTPClient';
+
+ /**
* All languages in which the recaptcha widget is available.
*
* @see http://recaptcha.net/apidocs/captcha/client.html
@@ -280,9 +287,7 @@ public function validate($validator) {
}
// get the payload of the response and split it by newlines
- $response = explode("\r\n\r\n", $response, 2);
-
- list($isValid, $error) = explode("\n", $response[1]);
+ list($isValid, $error) = explode("\n", $response, 2);
if($isValid != 'true') {
if(trim($error) != 'incorrect-captcha-sol') {
@@ -319,43 +324,62 @@ public function validate($validator) {
* @return string Raw HTTP-response
*/
protected function recaptchaHTTPPost($challengeStr, $responseStr) {
- $host = self::$api_verify_server;
- $port = 80;
- $path = '/verify';
- $req = http_build_query(array(
+ $postVars = array(
'privatekey' => self::$private_api_key,
'remoteip' => $_SERVER["REMOTE_ADDR"],
'challenge' => $challengeStr,
'response' => $responseStr,
- ));
-
- $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;
-
- $fs = fsockopen($host, $port, $errno, $errstr, 10);
- if(!$fs) {
- user_error('RecaptchaField::recaptchaHTTPPost(): Could not open socket');
- return false;
+ );
+ $client = $this->getHTTPClient();
+ $response = $client->post(self::$api_verify_server . '/verify', $postVars);
+
+ return $response->getBody();
+ }
+
+ /**
+ * @param RecaptchaField_HTTPClient
+ */
+ function setHTTPClient($client) {
+ $this->client = $client;
+ }
+
+ /**
+ * @return RecaptchaField_HTTPClient
+ */
+ function getHTTPClient() {
+ if(!$this->client) {
+ $class = self::$httpclient_class;
+ $this->client = new $class();
}
+
+ return $this->client;
+ }
+}
- stream_set_timeout($fs, 10); // time out after 10 seconds for read/write
- fwrite($fs, $http_request);
+/**
+ * Simple HTTP client, mainly to make it mockable.
+ */
+class RecaptchaField_HTTPClient extends Object {
+
+ /**
+ * @param String $url
+ * @param Array $data
+ * @return String HTTPResponse
+ */
+ function post($url, $postVars) {
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'reCAPTCHA/PHP');
+ // we need application/x-www-form-urlencoded
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postVars));
+ $response = curl_exec($ch);
- $response = '';
- $timed_out = false;
- while(!$timed_out && !feof($fs)) {
- $response .= fgets($fs, 1160); // One TCP-IP packet
- $timed_out = stream_get_meta_data($fs);
- $timed_out = $timed_out['timed_out'];
+ if(class_exists('SS_HTTPResponse')) {
+ return new SS_HTTPResponse($response);
+ } else {
+ // 2.3 backwards compat
+ return new HTTPResponse($response);
}
-
- fclose($fs);
-
- return $response;
}
}
View
6 code/RecaptchaProtector.php
@@ -1,11 +1,11 @@
<?php
-
/**
- * Protecter class to handle spam protection interface
- *
* @package recaptcha
*/
+/**
+ * Protecter class to handle spam protection interface
+ */
class RecaptchaProtector implements SpamProtector {
/**
View
40 tests/unit/RecatpchaFieldTest.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @package recaptcha
+ */
+
+class RecatpchaFieldTest extends SapphireTest {
+
+ function testValidate() {
+ $form = new Form(new Controller(), 'Form', new FieldSet(), new FieldSet());
+ $f = new RecaptchaField('MyField');
+ $f->setHTTPClient(new RecatpchaFieldTest_HTTPClient());
+ $f->setForm($form);
+ $v = new RequiredFields();
+
+ $_REQUEST['recaptcha_challenge_field'] = 'valid';
+ $_REQUEST['recaptcha_response_field'] = 'response';
+ $this->assertTrue($f->validate($v));
+
+ $_REQUEST['recaptcha_challenge_field'] = 'invalid';
+ $_REQUEST['recaptcha_response_field'] = 'response';
+ $this->assertFalse($f->validate($v));
+
+ unset($_REQUEST['recaptcha_challenge_field']);
+ unset($_REQUEST['recaptcha_response_field']);
+ }
+}
+
+class RecatpchaFieldTest_HTTPClient extends RecaptchaField_HTTPClient implements TestOnly {
+ function post($url, $postVars) {
+ if($postVars['challenge'] == 'valid') {
+ return new SS_HTTPResponse("true\nNo errors");
+ }
+
+ if($postVars['challenge'] == 'invalid') {
+ return new SS_HTTPResponse("false\nincorrect-captcha-sol");
+ }
+
+ return new SS_HTTPResponse();
+ }
+}

0 comments on commit cedae82

Please sign in to comment.
Something went wrong with that request. Please try again.