1<?php 2 3/** 4 * This module contains the plain non-curl HTTP fetcher 5 * implementation. 6 * 7 * PHP versions 4 and 5 8 * 9 * LICENSE: See the COPYING file included in this distribution. 10 * 11 * @package OpenID 12 * @author JanRain, Inc. <openid@janrain.com> 13 * @copyright 2005-2008 Janrain, Inc. 14 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache 15 */ 16 17/** 18 * Interface import 19 */ 20require_once "Auth/Yadis/HTTPFetcher.php"; 21 22/** 23 * This class implements a plain, hand-built socket-based fetcher 24 * which will be used in the event that CURL is unavailable. 25 * 26 * @package OpenID 27 */ 28class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher { 29 /** 30 * Does this fetcher support SSL URLs? 31 */ 32 function supportsSSL() 33 { 34 return function_exists('openssl_open'); 35 } 36 37 /** 38 * @param string $url 39 * @param array|null $extra_headers 40 * @return Auth_Yadis_HTTPResponse|null|bool 41 */ 42 function get($url, $extra_headers = null) 43 { 44 if (!$this->canFetchURL($url)) { 45 return null; 46 } 47 48 $redir = true; 49 50 $stop = time() + $this->timeout; 51 $off = $this->timeout; 52 $headers = []; 53 $code = ''; 54 $body = ''; 55 56 while ($redir && ($off > 0)) { 57 58 $parts = parse_url($url); 59 60 $specify_port = true; 61 62 // Set a default port. 63 if (!array_key_exists('port', $parts)) { 64 $specify_port = false; 65 if ($parts['scheme'] == 'http') { 66 $parts['port'] = 80; 67 } elseif ($parts['scheme'] == 'https') { 68 $parts['port'] = 443; 69 } else { 70 return null; 71 } 72 } 73 74 if (!array_key_exists('path', $parts)) { 75 $parts['path'] = '/'; 76 } 77 78 $host = $parts['host']; 79 80 if ($parts['scheme'] == 'https') { 81 $host = 'ssl://' . $host; 82 } 83 84 $user_agent = Auth_OpenID_USER_AGENT; 85 86 $headers = [ 87 "GET " . $parts['path'] . 88 (array_key_exists('query', $parts) ? 89 "?" . $parts['query'] : "") . 90 " HTTP/1.0", 91 "User-Agent: $user_agent", 92 "Host: " . $parts['host'] . 93 ($specify_port ? ":" . $parts['port'] : ""), 94 "Port: " . $parts['port'], 95 ]; 96 97 $errno = 0; 98 $errstr = ''; 99 100 if ($extra_headers) { 101 foreach ($extra_headers as $h) { 102 $headers[] = $h; 103 } 104 } 105 106 @$sock = fsockopen($host, $parts['port'], $errno, $errstr, 107 $this->timeout); 108 if ($sock === false) { 109 return false; 110 } 111 112 stream_set_timeout($sock, $this->timeout); 113 114 fputs($sock, implode("\r\n", $headers) . "\r\n\r\n"); 115 116 $data = ""; 117 $kilobytes = 0; 118 while (!feof($sock) && 119 $kilobytes < Auth_OpenID_FETCHER_MAX_RESPONSE_KB ) { 120 $data .= fgets($sock, 1024); 121 $kilobytes += 1; 122 } 123 124 fclose($sock); 125 126 // Split response into header and body sections 127 list($headers, $body) = explode("\r\n\r\n", $data, 2); 128 $headers = explode("\r\n", $headers); 129 130 $http_code = explode(" ", $headers[0]); 131 $code = $http_code[1]; 132 133 if (in_array($code, ['301', '302'])) { 134 $url = $this->_findRedirect($headers, $url); 135 $redir = true; 136 } else { 137 $redir = false; 138 } 139 140 $off = $stop - time(); 141 } 142 143 $new_headers = []; 144 145 foreach ($headers as $header) { 146 if (preg_match("/:/", $header)) { 147 $parts = explode(": ", $header, 2); 148 149 if (count($parts) == 2) { 150 list($name, $value) = $parts; 151 $new_headers[$name] = $value; 152 } 153 } 154 155 } 156 157 return new Auth_Yadis_HTTPResponse($url, $code, $new_headers, $body); 158 } 159 160 function post($url, $body, $extra_headers = null) 161 { 162 if (!$this->canFetchURL($url)) { 163 return null; 164 } 165 166 $parts = parse_url($url); 167 168 $headers = []; 169 170 $post_path = $parts['path']; 171 if (isset($parts['query'])) { 172 $post_path .= '?' . $parts['query']; 173 } 174 175 $headers[] = "POST ".$post_path." HTTP/1.0"; 176 $headers[] = "Host: " . $parts['host']; 177 $headers[] = "Content-type: application/x-www-form-urlencoded"; 178 $headers[] = "Content-length: " . strval(strlen($body)); 179 180 if ($extra_headers && 181 is_array($extra_headers)) { 182 $headers = array_merge($headers, $extra_headers); 183 } 184 185 // Join all headers together. 186 $all_headers = implode("\r\n", $headers); 187 188 // Add headers, two newlines, and request body. 189 $request = $all_headers . "\r\n\r\n" . $body; 190 191 // Set a default port. 192 if (!array_key_exists('port', $parts)) { 193 if ($parts['scheme'] == 'http') { 194 $parts['port'] = 80; 195 } elseif ($parts['scheme'] == 'https') { 196 $parts['port'] = 443; 197 } else { 198 return null; 199 } 200 } 201 202 if ($parts['scheme'] == 'https') { 203 $parts['host'] = sprintf("ssl://%s", $parts['host']); 204 } 205 206 // Connect to the remote server. 207 $errno = 0; 208 $errstr = ''; 209 210 $sock = fsockopen($parts['host'], $parts['port'], $errno, $errstr, 211 $this->timeout); 212 213 if ($sock === false) { 214 return null; 215 } 216 217 stream_set_timeout($sock, $this->timeout); 218 219 // Write the POST request. 220 fputs($sock, $request); 221 222 // Get the response from the server. 223 $response = ""; 224 while (!feof($sock)) { 225 if ($data = fgets($sock, 128)) { 226 $response .= $data; 227 } else { 228 break; 229 } 230 } 231 232 // Split the request into headers and body. 233 list($headers, $response_body) = explode("\r\n\r\n", $response, 2); 234 235 $headers = explode("\r\n", $headers); 236 237 // Expect the first line of the headers data to be something 238 // like HTTP/1.1 200 OK. Split the line on spaces and take 239 // the second token, which should be the return code. 240 $http_code = explode(" ", $headers[0]); 241 $code = $http_code[1]; 242 243 $new_headers = []; 244 245 foreach ($headers as $header) { 246 if (preg_match("/:/", $header)) { 247 list($name, $value) = explode(": ", $header, 2); 248 $new_headers[$name] = $value; 249 } 250 251 } 252 253 return new Auth_Yadis_HTTPResponse($url, $code, 254 $new_headers, $response_body); 255 } 256} 257 258