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 31 private $headers = []; 32 private $data = ''; 33 34 function __construct() 35 { 36 $this->reset(); 37 } 38 39 function reset() 40 { 41 $this->headers = []; 42 $this->data = ""; 43 } 44 45 /** 46 * @access private 47 * @param string $ch 48 * @param string $header 49 * @return int 50 */ 51 function _writeHeader($ch, $header) 52 { 53 array_push($this->headers, rtrim($header)); 54 return strlen($header); 55 } 56 57 /** 58 * @access private 59 * @param string $ch 60 * @param string $data 61 * @return int 62 */ 63 function _writeData($ch, $data) 64 { 65 if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) { 66 return 0; 67 } else { 68 $this->data .= $data; 69 return strlen($data); 70 } 71 } 72 73 /** 74 * Does this fetcher support SSL URLs? 75 */ 76 function supportsSSL() 77 { 78 $v = curl_version(); 79 if(is_array($v)) { 80 return in_array('https', $v['protocols']); 81 } elseif (is_string($v)) { 82 return preg_match('/OpenSSL/i', $v); 83 } else { 84 return 0; 85 } 86 } 87 88 /** 89 * @param string $url 90 * @param array|null $extra_headers 91 * @return Auth_Yadis_HTTPResponse|null 92 */ 93 function get($url, $extra_headers = null) 94 { 95 if (!$this->canFetchURL($url)) { 96 return null; 97 } 98 99 $stop = time() + $this->timeout; 100 $off = $this->timeout; 101 102 $redir = true; 103 104 while ($redir && ($off > 0)) { 105 $this->reset(); 106 107 $c = curl_init(); 108 if (defined('Auth_OpenID_DISABLE_SSL_VERIFYPEER') 109 && Auth_OpenID_DISABLE_SSL_VERIFYPEER === true) { 110 trigger_error( 111 'You have disabled SSL verifcation, this is a TERRIBLE ' . 112 'idea in almost all cases. Set Auth_OpenID_DISABLE_SSL_' . 113 'VERIFYPEER to false if you want to be safe again', 114 E_USER_WARNING); 115 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false); 116 } 117 118 if ($c === false) { 119 Auth_OpenID::log( 120 "curl_init returned false; could not " . 121 "initialize for URL '%s'", $url); 122 return null; 123 } 124 125 if (defined('CURLOPT_NOSIGNAL')) { 126 curl_setopt($c, CURLOPT_NOSIGNAL, true); 127 } 128 129 if (!$this->allowedURL($url)) { 130 Auth_OpenID::log("Fetching URL not allowed: %s", 131 $url); 132 return null; 133 } 134 135 curl_setopt($c, CURLOPT_WRITEFUNCTION, 136 [$this, "_writeData"]); 137 curl_setopt($c, CURLOPT_HEADERFUNCTION, 138 [$this, "_writeHeader"]); 139 140 if ($extra_headers) { 141 curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers); 142 } 143 144 $cv = curl_version(); 145 if(is_array($cv)) { 146 $curl_user_agent = 'curl/'.$cv['version']; 147 } else { 148 $curl_user_agent = $cv; 149 } 150 curl_setopt($c, CURLOPT_USERAGENT, 151 Auth_OpenID_USER_AGENT.' '.$curl_user_agent); 152 curl_setopt($c, CURLOPT_TIMEOUT, $off); 153 curl_setopt($c, CURLOPT_URL, $url); 154 155 if (defined('Auth_OpenID_VERIFY_HOST')) { 156 // set SSL verification options only if Auth_OpenID_VERIFY_HOST 157 // is explicitly set, otherwise use system default. 158 if (Auth_OpenID_VERIFY_HOST) { 159 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); 160 curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); 161 if (defined('Auth_OpenID_CAINFO')) { 162 curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO); 163 } 164 } else { 165 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false); 166 } 167 } 168 if (defined('Auth_OpenID_HTTP_PROXY')) { 169 curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY); 170 } 171 172 curl_exec($c); 173 174 $code = curl_getinfo($c, CURLINFO_HTTP_CODE); 175 $body = $this->data; 176 $headers = $this->headers; 177 178 if (!$code) { 179 Auth_OpenID::log("Got no response code when fetching %s", $url); 180 Auth_OpenID::log("CURL error (%s): %s", 181 curl_errno($c), curl_error($c)); 182 return null; 183 } 184 185 if (in_array($code, [301, 302, 303, 307])) { 186 $url = $this->_findRedirect($headers, $url); 187 $redir = true; 188 } else { 189 $redir = false; 190 curl_close($c); 191 192 if (defined('Auth_OpenID_VERIFY_HOST') && 193 Auth_OpenID_VERIFY_HOST == true && 194 $this->isHTTPS($url)) { 195 Auth_OpenID::log('OpenID: Verified SSL host %s using '. 196 'curl/get', $url); 197 } 198 $new_headers = []; 199 200 foreach ($headers as $header) { 201 if (strpos($header, ': ')) { 202 list($name, $value) = explode(': ', $header, 2); 203 $new_headers[$name] = $value; 204 } 205 } 206 207 return new Auth_Yadis_HTTPResponse($url, $code, 208 $new_headers, $body); 209 } 210 211 $off = $stop - time(); 212 } 213 214 return null; 215 } 216 217 function post($url, $body, $extra_headers = null) 218 { 219 if (!$this->canFetchURL($url)) { 220 return null; 221 } 222 223 $this->reset(); 224 225 $c = curl_init(); 226 227 if (defined('CURLOPT_NOSIGNAL')) { 228 curl_setopt($c, CURLOPT_NOSIGNAL, true); 229 } 230 231 if (defined('Auth_OpenID_HTTP_PROXY')) { 232 curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY); 233 } 234 235 curl_setopt($c, CURLOPT_POST, true); 236 curl_setopt($c, CURLOPT_POSTFIELDS, $body); 237 curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout); 238 curl_setopt($c, CURLOPT_URL, $url); 239 curl_setopt($c, CURLOPT_WRITEFUNCTION, 240 [$this, "_writeData"]); 241 242 if (defined('Auth_OpenID_VERIFY_HOST')) { 243 // set SSL verification options only if Auth_OpenID_VERIFY_HOST 244 // is explicitly set, otherwise use system default. 245 if (Auth_OpenID_VERIFY_HOST) { 246 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); 247 curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); 248 if (defined('Auth_OpenID_CAINFO')) { 249 curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO); 250 } 251 } else { 252 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false); 253 } 254 } 255 256 curl_exec($c); 257 258 $code = curl_getinfo($c, CURLINFO_HTTP_CODE); 259 260 if (!$code) { 261 Auth_OpenID::log("Got no response code when fetching %s", $url); 262 Auth_OpenID::log("CURL error (%s): %s", 263 curl_errno($c), curl_error($c)); 264 return null; 265 } 266 267 if (defined('Auth_OpenID_VERIFY_HOST') && 268 Auth_OpenID_VERIFY_HOST == true && 269 $this->isHTTPS($url)) { 270 Auth_OpenID::log('OpenID: Verified SSL host %s using '. 271 'curl/post', $url); 272 } 273 $body = $this->data; 274 275 curl_close($c); 276 277 $new_headers = $extra_headers; 278 279 foreach ($this->headers as $header) { 280 if (strpos($header, ': ')) { 281 list($name, $value) = explode(': ', $header, 2); 282 $new_headers[$name] = $value; 283 } 284 285 } 286 287 return new Auth_Yadis_HTTPResponse($url, $code, 288 $new_headers, $body); 289 } 290} 291 292