1<?php
2
3/**
4 * This module contains the CURL-based HTTP fetcher implementation.
5 *
6 * PHP versions 4 and 5
7 *
8 * LICENSE: See the COPYING file included in this distribution.
9 *
10 * @package OpenID
11 * @author JanRain, Inc. <openid@janrain.com>
12 * @copyright 2005-2008 Janrain, Inc.
13 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
14 */
15
16/**
17 * Interface import
18 */
19require_once "Auth/Yadis/HTTPFetcher.php";
20
21require_once "Auth/OpenID.php";
22
23/**
24 * A paranoid {@link Auth_Yadis_HTTPFetcher} class which uses CURL
25 * for fetching.
26 *
27 * @package OpenID
28 */
29class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
30    function Auth_Yadis_ParanoidHTTPFetcher()
31    {
32        $this->reset();
33    }
34
35    function reset()
36    {
37        $this->headers = array();
38        $this->data = "";
39    }
40
41    /**
42     * @access private
43     */
44    function _writeHeader($ch, $header)
45    {
46        array_push($this->headers, rtrim($header));
47        return strlen($header);
48    }
49
50    /**
51     * @access private
52     */
53    function _writeData($ch, $data)
54    {
55        if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) {
56            return 0;
57        } else {
58            $this->data .= $data;
59            return strlen($data);
60        }
61    }
62
63    /**
64     * Does this fetcher support SSL URLs?
65     */
66    function supportsSSL()
67    {
68        $v = curl_version();
69        if(is_array($v)) {
70            return in_array('https', $v['protocols']);
71        } elseif (is_string($v)) {
72            return preg_match('/OpenSSL/i', $v);
73        } else {
74            return 0;
75        }
76    }
77
78    function get($url, $extra_headers = null)
79    {
80        if (!$this->canFetchURL($url)) {
81            return null;
82        }
83
84        $stop = time() + $this->timeout;
85        $off = $this->timeout;
86
87        $redir = true;
88
89        while ($redir && ($off > 0)) {
90            $this->reset();
91
92            $c = curl_init();
93
94            if ($c === false) {
95                Auth_OpenID::log(
96                    "curl_init returned false; could not " .
97                    "initialize for URL '%s'", $url);
98                return null;
99            }
100
101            if (defined('CURLOPT_NOSIGNAL')) {
102                curl_setopt($c, CURLOPT_NOSIGNAL, true);
103            }
104
105            if (!$this->allowedURL($url)) {
106                Auth_OpenID::log("Fetching URL not allowed: %s",
107                                 $url);
108                return null;
109            }
110
111            curl_setopt($c, CURLOPT_WRITEFUNCTION,
112                        array($this, "_writeData"));
113            curl_setopt($c, CURLOPT_HEADERFUNCTION,
114                        array($this, "_writeHeader"));
115
116            if ($extra_headers) {
117                curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers);
118            }
119
120            $cv = curl_version();
121            if(is_array($cv)) {
122              $curl_user_agent = 'curl/'.$cv['version'];
123            } else {
124              $curl_user_agent = $cv;
125            }
126            curl_setopt($c, CURLOPT_USERAGENT,
127                        Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
128            curl_setopt($c, CURLOPT_TIMEOUT, $off);
129            curl_setopt($c, CURLOPT_URL, $url);
130
131            if (defined('Auth_OpenID_VERIFY_HOST')) {
132                curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
133                curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
134            }
135            curl_exec($c);
136
137            $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
138            $body = $this->data;
139            $headers = $this->headers;
140
141            if (!$code) {
142                Auth_OpenID::log("Got no response code when fetching %s", $url);
143                Auth_OpenID::log("CURL error (%s): %s",
144                                 curl_errno($c), curl_error($c));
145                return null;
146            }
147
148            if (in_array($code, array(301, 302, 303, 307))) {
149                $url = $this->_findRedirect($headers, $url);
150                $redir = true;
151            } else {
152                $redir = false;
153                curl_close($c);
154
155                if (defined('Auth_OpenID_VERIFY_HOST') &&
156                    $this->isHTTPS($url)) {
157                    Auth_OpenID::log('OpenID: Verified SSL host %s using '.
158                                     'curl/get', $url);
159                }
160                $new_headers = array();
161
162                foreach ($headers as $header) {
163                    if (strpos($header, ': ')) {
164                        list($name, $value) = explode(': ', $header, 2);
165                        $new_headers[$name] = $value;
166                    }
167                }
168
169                Auth_OpenID::log(
170                    "Successfully fetched '%s': GET response code %s",
171                    $url, $code);
172
173                return new Auth_Yadis_HTTPResponse($url, $code,
174                                                    $new_headers, $body);
175            }
176
177            $off = $stop - time();
178        }
179
180        return null;
181    }
182
183    function post($url, $body, $extra_headers = null)
184    {
185        if (!$this->canFetchURL($url)) {
186            return null;
187        }
188
189        $this->reset();
190
191        $c = curl_init();
192
193        if (defined('CURLOPT_NOSIGNAL')) {
194            curl_setopt($c, CURLOPT_NOSIGNAL, true);
195        }
196
197        curl_setopt($c, CURLOPT_POST, true);
198        curl_setopt($c, CURLOPT_POSTFIELDS, $body);
199        curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
200        curl_setopt($c, CURLOPT_URL, $url);
201        curl_setopt($c, CURLOPT_WRITEFUNCTION,
202                    array($this, "_writeData"));
203
204        if (defined('Auth_OpenID_VERIFY_HOST')) {
205            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
206            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
207        }
208
209        curl_exec($c);
210
211        $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
212
213        if (!$code) {
214            Auth_OpenID::log("Got no response code when fetching %s", $url);
215            Auth_OpenID::log("CURL error (%s): %s",
216                             curl_errno($c), curl_error($c));
217            return null;
218        }
219
220        if (defined('Auth_OpenID_VERIFY_HOST') && $this->isHTTPS($url)) {
221            Auth_OpenID::log('OpenID: Verified SSL host %s using '.
222                             'curl/post', $url);
223        }
224        $body = $this->data;
225
226        curl_close($c);
227
228        $new_headers = $extra_headers;
229
230        foreach ($this->headers as $header) {
231            if (strpos($header, ': ')) {
232                list($name, $value) = explode(': ', $header, 2);
233                $new_headers[$name] = $value;
234            }
235
236        }
237
238        Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
239                         $url, $code);
240
241        return new Auth_Yadis_HTTPResponse($url, $code,
242                                           $new_headers, $body);
243    }
244}
245
246