1<?php
2/**
3 * This is a PHP library that handles calling reCAPTCHA.
4 *
5 * @copyright Copyright (c) 2015, Google Inc.
6 * @link      http://www.google.com/recaptcha
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27namespace ReCaptcha\RequestMethod;
28
29use ReCaptcha\RequestMethod;
30use ReCaptcha\RequestParameters;
31
32/**
33 * Sends a POST request to the reCAPTCHA service, but makes use of fsockopen()
34 * instead of get_file_contents(). This is to account for people who may be on
35 * servers where allow_furl_open is disabled.
36 */
37class SocketPost implements RequestMethod
38{
39    /**
40     * reCAPTCHA service host.
41     * @const string
42     */
43    const RECAPTCHA_HOST = 'www.google.com';
44
45    /**
46     * @const string reCAPTCHA service path
47     */
48    const SITE_VERIFY_PATH = '/recaptcha/api/siteverify';
49
50    /**
51     * @const string Bad request error
52     */
53    const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}';
54
55    /**
56     * @const string Bad response error
57     */
58    const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}';
59
60    /**
61     * Socket to the reCAPTCHA service
62     * @var Socket
63     */
64    private $socket;
65
66    /**
67     * Constructor
68     *
69     * @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing
70     */
71    public function __construct(Socket $socket = null)
72    {
73        if (!is_null($socket)) {
74            $this->socket = $socket;
75        } else {
76            $this->socket = new Socket();
77        }
78    }
79
80    /**
81     * Submit the POST request with the specified parameters.
82     *
83     * @param RequestParameters $params Request parameters
84     * @return string Body of the reCAPTCHA response
85     */
86    public function submit(RequestParameters $params)
87    {
88        $errno = 0;
89        $errstr = '';
90
91        if ($this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30) !== false) {
92            $content = $params->toQueryString();
93
94            $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
95            $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
96            $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
97            $request .= "Content-length: " . strlen($content) . "\r\n";
98            $request .= "Connection: close\r\n\r\n";
99            $request .= $content . "\r\n\r\n";
100
101            $this->socket->fwrite($request);
102            $response = '';
103
104            while (!$this->socket->feof()) {
105                $response .= $this->socket->fgets(4096);
106            }
107
108            $this->socket->fclose();
109
110            if (0 === strpos($response, 'HTTP/1.1 200 OK')) {
111                $parts = preg_split("#\n\s*\n#Uis", $response);
112                return $parts[1];
113            }
114
115            return self::BAD_RESPONSE;
116        }
117
118        return self::BAD_REQUEST;
119    }
120}
121