includes/http.php
changeset 1227 bdac73ed481e
parent 1224 a54e149f4a78
child 1234 903bb153c265
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    86  * @copyright 2007 Dan Fuhry
    86  * @copyright 2007 Dan Fuhry
    87  */
    87  */
    88 
    88 
    89 class Request_HTTP
    89 class Request_HTTP
    90 {
    90 {
    91   
    91 	
    92   /**
    92 	/**
    93    * Switch to enable or disable debugging. You want this off on production sites.
    93  	* Switch to enable or disable debugging. You want this off on production sites.
    94    * @var bool
    94  	* @var bool
    95    */
    95  	*/
    96   
    96 	
    97   var $debug = false;
    97 	var $debug = false;
    98   
    98 	
    99   /**
    99 	/**
   100    * The host the request will be sent to.
   100  	* The host the request will be sent to.
   101    * @var string
   101  	* @var string
   102    */
   102  	*/
   103   
   103 	
   104   var $host = '';
   104 	var $host = '';
   105   
   105 	
   106   /**
   106 	/**
   107    * The TCP port our connection is (will be) on.
   107  	* The TCP port our connection is (will be) on.
   108    * @var int
   108  	* @var int
   109    */
   109  	*/
   110   
   110 	
   111   var $port = 80;
   111 	var $port = 80;
   112   
   112 	
   113   /**
   113 	/**
   114    * The request method. Can be GET or POST, defaults to GET.
   114  	* The request method. Can be GET or POST, defaults to GET.
   115    * @var string
   115  	* @var string
   116    */
   116  	*/
   117   
   117 	
   118   var $method = 'GET';
   118 	var $method = 'GET';
   119   
   119 	
   120   /**
   120 	/**
   121    * The URI to the remote script.
   121  	* The URI to the remote script.
   122    * @var string
   122  	* @var string
   123    */
   123  	*/
   124   
   124 	
   125   var $uri = '';
   125 	var $uri = '';
   126   
   126 	
   127   /**
   127 	/**
   128    * The parameters to be sent on GET.
   128  	* The parameters to be sent on GET.
   129    * @var array (associative)
   129  	* @var array (associative)
   130    */
   130  	*/
   131   
   131 	
   132   var $parms_get = array();
   132 	var $parms_get = array();
   133   
   133 	
   134   /**
   134 	/**
   135    * The parameters to be sent on POST. Ignored if $this->method == GET.
   135  	* The parameters to be sent on POST. Ignored if $this->method == GET.
   136    * @var array (associative)
   136  	* @var array (associative)
   137    */
   137  	*/
   138   
   138 	
   139   var $parms_post = array();
   139 	var $parms_post = array();
   140   
   140 	
   141   /**
   141 	/**
   142    * The list of cookies that will be sent.
   142  	* The list of cookies that will be sent.
   143    * @var array (associative)
   143  	* @var array (associative)
   144    */
   144  	*/
   145   
   145 	
   146   var $cookies_out = array();
   146 	var $cookies_out = array();
   147   
   147 	
   148   /**
   148 	/**
   149    * Additional request headers.
   149  	* Additional request headers.
   150    * @var array (associative)
   150  	* @var array (associative)
   151    */
   151  	*/
   152   
   152 	
   153   var $headers = array();
   153 	var $headers = array();
   154   
   154 	
   155   /**
   155 	/**
   156    * Follow server-side redirects; defaults to true.
   156  	* Follow server-side redirects; defaults to true.
   157    * @var bool
   157  	* @var bool
   158    */
   158  	*/
   159   
   159 	
   160   var $follow_redirects = true;
   160 	var $follow_redirects = true;
   161   
   161 	
   162   /**
   162 	/**
   163    * Cached response.
   163  	* Cached response.
   164    * @var string, or bool:false if the request hasn't been sent yet
   164  	* @var string, or bool:false if the request hasn't been sent yet
   165    */
   165  	*/
   166   
   166 	
   167   var $response = false;
   167 	var $response = false;
   168   
   168 	
   169   /**
   169 	/**
   170    * Cached response code
   170  	* Cached response code
   171    * @var int set to -1 if request hasn't been sent yet
   171  	* @var int set to -1 if request hasn't been sent yet
   172    */
   172  	*/
   173   
   173 	
   174   var $response_code = -1;
   174 	var $response_code = -1;
   175   
   175 	
   176   /**
   176 	/**
   177    * Cached response code string
   177  	* Cached response code string
   178    * @var string or bool:false if the request hasn't been sent yet
   178  	* @var string or bool:false if the request hasn't been sent yet
   179    */
   179  	*/
   180   
   180 	
   181   var $response_string = false;
   181 	var $response_string = false;
   182   
   182 	
   183   /**
   183 	/**
   184    * Resource for the socket. False if a connection currently isn't going.
   184  	* Resource for the socket. False if a connection currently isn't going.
   185    * @var resource
   185  	* @var resource
   186    */
   186  	*/
   187   
   187 	
   188   var $socket = false;
   188 	var $socket = false;
   189   
   189 	
   190   /**
   190 	/**
   191    * True if SSL is on, defaults to false
   191  	* True if SSL is on, defaults to false
   192    * @var bool
   192  	* @var bool
   193    */
   193  	*/
   194   
   194 	
   195   var $ssl = false;
   195 	var $ssl = false;
   196   
   196 	
   197   /**
   197 	/**
   198    * The state of our request. 0 means it hasn't been made yet. 1 means the socket is open, 2 means the socket is open and the request has been written, 3 means the headers have been fetched, and 4 means the request is completed.
   198  	* The state of our request. 0 means it hasn't been made yet. 1 means the socket is open, 2 means the socket is open and the request has been written, 3 means the headers have been fetched, and 4 means the request is completed.
   199    * @var int
   199  	* @var int
   200    */
   200  	*/
   201   
   201 	
   202   var $state = 0;
   202 	var $state = 0;
   203   
   203 	
   204   /**
   204 	/**
   205    * Constructor.
   205  	* Constructor.
   206    * @param string Hostname to send to
   206  	* @param string Hostname to send to
   207    * @param string URI (/index.php)
   207  	* @param string URI (/index.php)
   208    * @param string Request method - GET or POST.
   208  	* @param string Request method - GET or POST.
   209    * @param int Optional. The port to open the request on. Defaults to 80.
   209  	* @param int Optional. The port to open the request on. Defaults to 80.
   210    * @param bool If true, uses SSL (and defaults the port to 443)
   210  	* @param bool If true, uses SSL (and defaults the port to 443)
   211    */
   211  	*/
   212   
   212 	
   213   function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
   213 	function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
   214   {
   214 	{
   215     if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
   215 		if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
   216       throw new Exception(__CLASS__ . ': Invalid hostname');
   216 			throw new Exception(__CLASS__ . ': Invalid hostname');
   217     if ( $ssl )
   217 		if ( $ssl )
   218     {
   218 		{
   219       $this->ssl = true;
   219 			$this->ssl = true;
   220       $port = ( $port === 'default' ) ? 443 : $port;
   220 			$port = ( $port === 'default' ) ? 443 : $port;
   221     }
   221 		}
   222     else
   222 		else
   223     {
   223 		{
   224       $this->ssl = false;
   224 			$this->ssl = false;
   225       $port = ( $port === 'default' ) ? 80 : $port;
   225 			$port = ( $port === 'default' ) ? 80 : $port;
   226     }
   226 		}
   227     // Yes - this really does support IPv6 URLs!
   227 		// Yes - this really does support IPv6 URLs!
   228     $this->host = $host;
   228 		$this->host = $host;
   229     $this->uri = $uri;
   229 		$this->uri = $uri;
   230     if ( is_int($port) && $port >= 1 && $port <= 65535 )
   230 		if ( is_int($port) && $port >= 1 && $port <= 65535 )
   231       $this->port = $port;
   231 			$this->port = $port;
   232     else
   232 		else
   233       throw new Exception(__CLASS__ . ': Invalid port');
   233 			throw new Exception(__CLASS__ . ': Invalid port');
   234     $method = strtoupper($method);
   234 		$method = strtoupper($method);
   235     if ( $method == 'GET' || $method == 'POST' )
   235 		if ( $method == 'GET' || $method == 'POST' )
   236       $this->method = $method;
   236 			$this->method = $method;
   237     else
   237 		else
   238       throw new Exception(__CLASS__ . ': Invalid request method');
   238 			throw new Exception(__CLASS__ . ': Invalid request method');
   239       
   239 			
   240     $newline = "\r\n";
   240 		$newline = "\r\n";
   241     $php_ver = PHP_VERSION;
   241 		$php_ver = PHP_VERSION;
   242     $server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
   242 		$server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
   243     $this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
   243 		$this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
   244   }
   244 	}
   245   
   245 	
   246   /**
   246 	/**
   247    * Sets one or more cookies to be sent to the server.
   247  	* Sets one or more cookies to be sent to the server.
   248    * @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
   248  	* @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
   249    * @param string or bool If a string, the cookie value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   249  	* @param string or bool If a string, the cookie value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   250    */
   250  	*/
   251   
   251 	
   252   function add_cookie($cookiename, $cookievalue = false)
   252 	function add_cookie($cookiename, $cookievalue = false)
   253   {
   253 	{
   254     if ( is_array($cookiename) && !$cookievalue )
   254 		if ( is_array($cookiename) && !$cookievalue )
   255     {
   255 		{
   256       foreach ( $cookiename as $name => $value )
   256 			foreach ( $cookiename as $name => $value )
   257       {
   257 			{
   258         $this->cookies_out[$name] = $value;
   258 				$this->cookies_out[$name] = $value;
   259       }
   259 			}
   260     }
   260 		}
   261     else if ( is_string($cookiename) && is_string($cookievalue) )
   261 		else if ( is_string($cookiename) && is_string($cookievalue) )
   262     {
   262 		{
   263       $this->cookies_out[$cookiename] = $cookievalue;
   263 			$this->cookies_out[$cookiename] = $cookievalue;
   264     }
   264 		}
   265     else
   265 		else
   266     {
   266 		{
   267       throw new Exception(__METHOD__ . ': Invalid argument(s)');
   267 			throw new Exception(__METHOD__ . ': Invalid argument(s)');
   268     }
   268 		}
   269   }
   269 	}
   270   
   270 	
   271   /**
   271 	/**
   272    * Sets one or more request header values.
   272  	* Sets one or more request header values.
   273    * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
   273  	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
   274    * @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   274  	* @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   275    */
   275  	*/
   276   
   276 	
   277   function add_header($headername, $headervalue = false)
   277 	function add_header($headername, $headervalue = false)
   278   {
   278 	{
   279     if ( is_array($headername) && !$headervalue )
   279 		if ( is_array($headername) && !$headervalue )
   280     {
   280 		{
   281       foreach ( $headername as $name => $value )
   281 			foreach ( $headername as $name => $value )
   282       {
   282 			{
   283         $this->headers[$name] = $value;
   283 				$this->headers[$name] = $value;
   284       }
   284 			}
   285     }
   285 		}
   286     else if ( is_string($headername) && is_string($headervalue) )
   286 		else if ( is_string($headername) && is_string($headervalue) )
   287     {
   287 		{
   288       $this->headers[$headername] = $headervalue;
   288 			$this->headers[$headername] = $headervalue;
   289     }
   289 		}
   290     else
   290 		else
   291     {
   291 		{
   292       throw new Exception(__METHOD__ . ': Invalid argument(s)');
   292 			throw new Exception(__METHOD__ . ': Invalid argument(s)');
   293     }
   293 		}
   294   }
   294 	}
   295   
   295 	
   296   /**
   296 	/**
   297    * Adds one or more values to be passed on GET.
   297  	* Adds one or more values to be passed on GET.
   298    * @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
   298  	* @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
   299    * @param string or bool If a string, the parameter value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   299  	* @param string or bool If a string, the parameter value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   300    */
   300  	*/
   301   
   301 	
   302   function add_get($getname, $getvalue = false)
   302 	function add_get($getname, $getvalue = false)
   303   {
   303 	{
   304     if ( is_array($getname) && !$getvalue )
   304 		if ( is_array($getname) && !$getvalue )
   305     {
   305 		{
   306       foreach ( $getname as $name => $value )
   306 			foreach ( $getname as $name => $value )
   307       {
   307 			{
   308         $this->parms_get[$name] = $value;
   308 				$this->parms_get[$name] = $value;
   309       }
   309 			}
   310     }
   310 		}
   311     else if ( is_string($getname) && is_string($getvalue) )
   311 		else if ( is_string($getname) && is_string($getvalue) )
   312     {
   312 		{
   313       $this->parms_get[$getname] = $getvalue;
   313 			$this->parms_get[$getname] = $getvalue;
   314     }
   314 		}
   315     else
   315 		else
   316     {
   316 		{
   317       throw new Exception(__METHOD__ . ': Invalid argument(s)');
   317 			throw new Exception(__METHOD__ . ': Invalid argument(s)');
   318     }
   318 		}
   319   }
   319 	}
   320   
   320 	
   321   /**
   321 	/**
   322    * Adds one or more values to be passed on POST.
   322  	* Adds one or more values to be passed on POST.
   323    * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
   323  	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
   324    * @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   324  	* @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
   325    */
   325  	*/
   326   
   326 	
   327   function add_post($postname, $postvalue = false)
   327 	function add_post($postname, $postvalue = false)
   328   {
   328 	{
   329     if ( is_array($postname) && !$postvalue )
   329 		if ( is_array($postname) && !$postvalue )
   330     {
   330 		{
   331       foreach ( $postname as $name => $value )
   331 			foreach ( $postname as $name => $value )
   332       {
   332 			{
   333         $this->parms_post[$name] = $value;
   333 				$this->parms_post[$name] = $value;
   334       }
   334 			}
   335     }
   335 		}
   336     else if ( is_string($postname) && is_string($postvalue) )
   336 		else if ( is_string($postname) && is_string($postvalue) )
   337     {
   337 		{
   338       $this->parms_post[$postname] = $postvalue;
   338 			$this->parms_post[$postname] = $postvalue;
   339     }
   339 		}
   340     else
   340 		else
   341     {
   341 		{
   342       throw new Exception(__METHOD__ . ': Invalid argument(s)');
   342 			throw new Exception(__METHOD__ . ': Invalid argument(s)');
   343     }
   343 		}
   344   }
   344 	}
   345   
   345 	
   346   /**
   346 	/**
   347    * Internal function to open up the socket.
   347  	* Internal function to open up the socket.
   348    * @access private
   348  	* @access private
   349    */
   349  	*/
   350   
   350 	
   351   function _sock_open(&$connection)
   351 	function _sock_open(&$connection)
   352   {
   352 	{
   353     // Open connection
   353 		// Open connection
   354     $ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
   354 		$ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
   355     $connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
   355 		$connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
   356     if ( !$connection )
   356 		if ( !$connection )
   357       throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
   357 			throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
   358     
   358 		
   359     // 1 = socket open
   359 		// 1 = socket open
   360     $this->state = 1;
   360 		$this->state = 1;
   361   }
   361 	}
   362   
   362 	
   363   /**
   363 	/**
   364    * Internal function to actually write the request into the socket.
   364  	* Internal function to actually write the request into the socket.
   365    * @access private
   365  	* @access private
   366    */
   366  	*/
   367   
   367 	
   368   function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
   368 	function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
   369   {
   369 	{
   370     $newline = "\r\n";
   370 		$newline = "\r\n";
   371     
   371 		
   372     if ( $this->debug )
   372 		if ( $this->debug )
   373       echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
   373 			echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
   374     
   374 		
   375     if ( $this->debug )
   375 		if ( $this->debug )
   376     {
   376 		{
   377       echo '<hr /><div style="white-space: nowrap;">';
   377 			echo '<hr /><div style="white-space: nowrap;">';
   378       echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
   378 			echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
   379       echo "<p><b>Headers:</b></p><pre>$headers</pre>";
   379 			echo "<p><b>Headers:</b></p><pre>$headers</pre>";
   380       echo "<p><b>Cookies:</b> $cookies</p>";
   380 			echo "<p><b>Cookies:</b> $cookies</p>";
   381       echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
   381 			echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
   382       echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
   382 			echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
   383       echo "<pre>";
   383 			echo "<pre>";
   384     }
   384 		}
   385     
   385 		
   386     $portline = ( $this->port == 80 ) ? '' : ":$this->port";
   386 		$portline = ( $this->port == 80 ) ? '' : ":$this->port";
   387     
   387 		
   388     $this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
   388 		$this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
   389     $this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
   389 		$this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
   390     $this->_fputs($connection, $headers);
   390 		$this->_fputs($connection, $headers);
   391     $this->_fputs($connection, $cookies);
   391 		$this->_fputs($connection, $cookies);
   392     
   392 		
   393     if ( $this->method == 'POST' )
   393 		if ( $this->method == 'POST' )
   394     {
   394 		{
   395       // POST-specific parameters
   395 			// POST-specific parameters
   396       $post_length = strlen($post);
   396 			$post_length = strlen($post);
   397       $this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
   397 			$this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
   398       $this->_fputs($connection, "Content-length: {$post_length}{$newline}");
   398 			$this->_fputs($connection, "Content-length: {$post_length}{$newline}");
   399     }
   399 		}
   400     
   400 		
   401     $this->_fputs($connection, "Connection: close{$newline}");
   401 		$this->_fputs($connection, "Connection: close{$newline}");
   402     $this->_fputs($connection, "{$newline}");
   402 		$this->_fputs($connection, "{$newline}");
   403     
   403 		
   404     if ( $this->method == 'POST' )
   404 		if ( $this->method == 'POST' )
   405     {
   405 		{
   406       $this->_fputs($connection, $post);
   406 			$this->_fputs($connection, $post);
   407     }
   407 		}
   408     
   408 		
   409     if ( $this->debug )
   409 		if ( $this->debug )
   410       echo '</pre><p>Request written. Fetching response.</p>';
   410 			echo '</pre><p>Request written. Fetching response.</p>';
   411     
   411 		
   412     // 2 = request written
   412 		// 2 = request written
   413     $this->state = 2;
   413 		$this->state = 2;
   414   }
   414 	}
   415   
   415 	
   416   /**
   416 	/**
   417    * Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
   417  	* Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
   418    * @access private
   418  	* @access private
   419    */
   419  	*/
   420   
   420 	
   421   function sock_close(&$connection)
   421 	function sock_close(&$connection)
   422   {
   422 	{
   423     if ( $this->debug )
   423 		if ( $this->debug )
   424     {
   424 		{
   425       echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
   425 			echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
   426       echo htmlspecialchars($this->response);
   426 			echo htmlspecialchars($this->response);
   427       echo '</pre></div><hr />';
   427 			echo '</pre></div><hr />';
   428     }
   428 		}
   429     
   429 		
   430     fclose($connection);
   430 		fclose($connection);
   431     $this->state = 0;
   431 		$this->state = 0;
   432   }
   432 	}
   433   
   433 	
   434   /**
   434 	/**
   435    * Internal function to grab the response code and status string
   435  	* Internal function to grab the response code and status string
   436    * @access string
   436  	* @access string
   437    */
   437  	*/
   438   
   438 	
   439   function _parse_response_code($buffer)
   439 	function _parse_response_code($buffer)
   440   {
   440 	{
   441     // Retrieve response code and status
   441 		// Retrieve response code and status
   442     $pos_newline = strpos($buffer, "\n");
   442 		$pos_newline = strpos($buffer, "\n");
   443     $pos_carriage_return = strpos($buffer, "\r");
   443 		$pos_carriage_return = strpos($buffer, "\r");
   444     $pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
   444 		$pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
   445     
   445 		
   446     // First line is in format of:
   446 		// First line is in format of:
   447     // HTTP/1.1 ### Blah blah blah(\r?)\n
   447 		// HTTP/1.1 ### Blah blah blah(\r?)\n
   448     $response_code = substr($buffer, 9, 3);
   448 		$response_code = substr($buffer, 9, 3);
   449     $response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
   449 		$response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
   450     $this->response_code = intval($response_code);
   450 		$this->response_code = intval($response_code);
   451     $this->response_string = $response_string;
   451 		$this->response_string = $response_string;
   452   }
   452 	}
   453   
   453 	
   454   /**
   454 	/**
   455    * Internal function to send the request.
   455  	* Internal function to send the request.
   456    * @access private
   456  	* @access private
   457    */
   457  	*/
   458   
   458 	
   459   function _send_request()
   459 	function _send_request()
   460   {
   460 	{
   461     $this->concat_headers($headers, $cookies, $get, $post);
   461 		$this->concat_headers($headers, $cookies, $get, $post);
   462     
   462 		
   463     if ( $this->state < 1 )
   463 		if ( $this->state < 1 )
   464     {
   464 		{
   465       $this->_sock_open($this->socket);
   465 			$this->_sock_open($this->socket);
   466     }
   466 		}
   467     if ( $this->state < 2 )
   467 		if ( $this->state < 2 )
   468     {
   468 		{
   469       $this->_write_request($this->socket, $headers, $cookies, $get, $post);
   469 			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
   470     }
   470 		}
   471     if ( $this->state == 2 )
   471 		if ( $this->state == 2 )
   472     {
   472 		{
   473       $buffer = $this->_read_until_newlines($this->socket);
   473 			$buffer = $this->_read_until_newlines($this->socket);
   474       $this->state = 3;
   474 			$this->state = 3;
   475       $this->_parse_response_code($buffer);
   475 			$this->_parse_response_code($buffer);
   476       $this->response = $buffer;
   476 			$this->response = $buffer;
   477     }
   477 		}
   478     // obey redirects
   478 		// obey redirects
   479     $i = 0;
   479 		$i = 0;
   480     while ( $i < 20 && $this->follow_redirects )
   480 		while ( $i < 20 && $this->follow_redirects )
   481     {
   481 		{
   482       $incoming_headers = $this->get_response_headers_array();
   482 			$incoming_headers = $this->get_response_headers_array();
   483       if ( !$incoming_headers )
   483 			if ( !$incoming_headers )
   484         break;
   484 				break;
   485       if ( isset($incoming_headers['Location']) )
   485 			if ( isset($incoming_headers['Location']) )
   486       {
   486 			{
   487         // we've been redirected...
   487 				// we've been redirected...
   488         $new_uri = $this->_resolve_uri($incoming_headers['Location']);
   488 				$new_uri = $this->_resolve_uri($incoming_headers['Location']);
   489         if ( !$new_uri )
   489 				if ( !$new_uri )
   490         {
   490 				{
   491           // ... bad URI, ignore Location header.
   491 					// ... bad URI, ignore Location header.
   492           break;
   492 					break;
   493         }
   493 				}
   494         // change location
   494 				// change location
   495         $this->host = $new_uri['host'];
   495 				$this->host = $new_uri['host'];
   496         $this->port = $new_uri['port'];
   496 				$this->port = $new_uri['port'];
   497         $this->uri  = $new_uri['uri'];
   497 				$this->uri  = $new_uri['uri'];
   498         $get = '';
   498 				$get = '';
   499         
   499 				
   500         // reset
   500 				// reset
   501         $this->sock_close($this->socket);
   501 				$this->sock_close($this->socket);
   502         $this->_sock_open($this->socket);
   502 				$this->_sock_open($this->socket);
   503         $this->_write_request($this->socket, $headers, $cookies, $get, $post);
   503 				$this->_write_request($this->socket, $headers, $cookies, $get, $post);
   504         $buffer = $this->_read_until_newlines($this->socket);
   504 				$buffer = $this->_read_until_newlines($this->socket);
   505         $this->state = 3;
   505 				$this->state = 3;
   506         $this->_parse_response_code($buffer);
   506 				$this->_parse_response_code($buffer);
   507         $this->response = $buffer;
   507 				$this->response = $buffer;
   508         $i++;
   508 				$i++;
   509       }
   509 			}
   510       else
   510 			else
   511       {
   511 			{
   512         break;
   512 				break;
   513       }
   513 			}
   514     }
   514 		}
   515     if ( $i == 20 )
   515 		if ( $i == 20 )
   516     {
   516 		{
   517       throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
   517 			throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
   518     }
   518 		}
   519     
   519 		
   520     if ( $this->state == 3 )
   520 		if ( $this->state == 3 )
   521     {
   521 		{
   522       // Determine transfer encoding
   522 			// Determine transfer encoding
   523       $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
   523 			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
   524       if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
   524 			if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
   525       {
   525 			{
   526         $size = intval($match[1]);
   526 				$size = intval($match[1]);
   527         if ( $this->debug )
   527 				if ( $this->debug )
   528         {
   528 				{
   529           echo "Pulling response using fread(), size $size\n";
   529 					echo "Pulling response using fread(), size $size\n";
   530         }
   530 				}
   531         $this->response .= fread($this->socket, $size);
   531 				$this->response .= fread($this->socket, $size);
   532       }
   532 			}
   533       else
   533 			else
   534       {
   534 			{
   535         if ( $this->debug )
   535 				if ( $this->debug )
   536           echo "Pulling response using chunked handler\n";
   536 					echo "Pulling response using chunked handler\n";
   537           
   537 					
   538         $buffer = '';
   538 				$buffer = '';
   539         while ( !feof($this->socket) )
   539 				while ( !feof($this->socket) )
   540         {
   540 				{
   541           $part = fgets($this->socket, 1024);
   541 					$part = fgets($this->socket, 1024);
   542           if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
   542 					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
   543           {
   543 					{
   544             $chunklen = hexdec($match[1]);
   544 						$chunklen = hexdec($match[1]);
   545             $part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
   545 						$part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
   546             // remove the last newline from $part
   546 						// remove the last newline from $part
   547             $part = preg_replace("/\r?\n\$/", "", $part);
   547 						$part = preg_replace("/\r?\n\$/", "", $part);
   548           }
   548 					}
   549           $buffer .= $part;
   549 					$buffer .= $part;
   550         }
   550 				}
   551         $this->response .= $buffer;
   551 				$this->response .= $buffer;
   552       }
   552 			}
   553     }
   553 		}
   554     $this->state = 4;
   554 		$this->state = 4;
   555     
   555 		
   556     $this->sock_close($this->socket);
   556 		$this->sock_close($this->socket);
   557     $this->socket = false;
   557 		$this->socket = false;
   558   }
   558 	}
   559   
   559 	
   560   /**
   560 	/**
   561    * Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
   561  	* Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
   562    * @access private
   562  	* @access private
   563    */
   563  	*/
   564   
   564 	
   565   function _send_request_headers_only()
   565 	function _send_request_headers_only()
   566   {
   566 	{
   567     $this->concat_headers($headers, $cookies, $get, $post);
   567 		$this->concat_headers($headers, $cookies, $get, $post);
   568     
   568 		
   569     if ( $this->state < 1 )
   569 		if ( $this->state < 1 )
   570     {
   570 		{
   571       $this->_sock_open($this->socket);
   571 			$this->_sock_open($this->socket);
   572     }
   572 		}
   573     if ( $this->state < 2 )
   573 		if ( $this->state < 2 )
   574     {
   574 		{
   575       $this->_write_request($this->socket, $headers, $cookies, $get, $post);
   575 			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
   576     }
   576 		}
   577     if ( $this->state == 2 )
   577 		if ( $this->state == 2 )
   578     {
   578 		{
   579       $buffer = $this->_read_until_newlines($this->socket);
   579 			$buffer = $this->_read_until_newlines($this->socket);
   580       $this->state = 3;
   580 			$this->state = 3;
   581       $this->_parse_response_code($buffer);
   581 			$this->_parse_response_code($buffer);
   582       $this->response = $buffer;
   582 			$this->response = $buffer;
   583     }
   583 		}
   584   }
   584 	}
   585   
   585 	
   586   /**
   586 	/**
   587    * Internal function to read from a socket until two consecutive newlines are hit.
   587  	* Internal function to read from a socket until two consecutive newlines are hit.
   588    * @access private
   588  	* @access private
   589    */
   589  	*/
   590   
   590 	
   591   function _read_until_newlines($sock)
   591 	function _read_until_newlines($sock)
   592   {
   592 	{
   593     $prev_char = '';
   593 		$prev_char = '';
   594     $prev1_char = '';
   594 		$prev1_char = '';
   595     $prev2_char = '';
   595 		$prev2_char = '';
   596     $buf = '';
   596 		$buf = '';
   597     while ( !feof($sock) )
   597 		while ( !feof($sock) )
   598     {
   598 		{
   599       $chr = fread($sock, 1);
   599 			$chr = fread($sock, 1);
   600       $buf .= $chr;
   600 			$buf .= $chr;
   601       if ( ( $chr == "\n" && $prev_char == "\n" ) ||
   601 			if ( ( $chr == "\n" && $prev_char == "\n" ) ||
   602            ( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
   602  					( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
   603       {
   603 			{
   604         return $buf;
   604 				return $buf;
   605       }
   605 			}
   606       $prev2_char = $prev1_char;
   606 			$prev2_char = $prev1_char;
   607       $prev1_char = $prev_char;
   607 			$prev1_char = $prev_char;
   608       $prev_char = $chr;
   608 			$prev_char = $chr;
   609     }
   609 		}
   610     return $buf;
   610 		return $buf;
   611   }
   611 	}
   612   
   612 	
   613   /**
   613 	/**
   614    * Returns the response text. If the request hasn't been sent, it will be sent here.
   614  	* Returns the response text. If the request hasn't been sent, it will be sent here.
   615    * @return string
   615  	* @return string
   616    */
   616  	*/
   617   
   617 	
   618   function get_response()
   618 	function get_response()
   619   {
   619 	{
   620     if ( $this->state == 4 )
   620 		if ( $this->state == 4 )
   621       return $this->response;
   621 			return $this->response;
   622     $this->_send_request();
   622 		$this->_send_request();
   623     return $this->response;
   623 		return $this->response;
   624   }
   624 	}
   625   
   625 	
   626   /**
   626 	/**
   627    * Writes the response body to a file. This is good for conserving memory when downloading large files. If the file already exists it will be overwritten.
   627  	* Writes the response body to a file. This is good for conserving memory when downloading large files. If the file already exists it will be overwritten.
   628    * @param string File to write to
   628  	* @param string File to write to
   629    * @param int Chunk size in KB to read from the socket. Optional and should only be needed in circumstances when extreme memory conservation is needed. Defaults to 768.
   629  	* @param int Chunk size in KB to read from the socket. Optional and should only be needed in circumstances when extreme memory conservation is needed. Defaults to 768.
   630    * @param int Maximum file size. Defaults to 0, which means no limit.
   630  	* @param int Maximum file size. Defaults to 0, which means no limit.
   631    * @return bool True on success, false on failure
   631  	* @return bool True on success, false on failure
   632    */
   632  	*/
   633   
   633 	
   634   function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
   634 	function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
   635   {
   635 	{
   636     if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
   636 		if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
   637     {
   637 		{
   638       return false;
   638 			return false;
   639     }
   639 		}
   640     $handle = @fopen($file, 'w');
   640 		$handle = @fopen($file, 'w');
   641     if ( !$handle )
   641 		if ( !$handle )
   642       return false;
   642 			return false;
   643     $chunklen = intval($chunklen);
   643 		$chunklen = intval($chunklen);
   644     if ( $chunklen < 1 )
   644 		if ( $chunklen < 1 )
   645       return false;
   645 			return false;
   646     if ( $this->state == 4 )
   646 		if ( $this->state == 4 )
   647     {
   647 		{
   648       // we already have the response, so cheat
   648 			// we already have the response, so cheat
   649       $response = $this->get_response_body();
   649 			$response = $this->get_response_body();
   650       fwrite($handle, $response);
   650 			fwrite($handle, $response);
   651     }
   651 		}
   652     else
   652 		else
   653     {
   653 		{
   654       // read data from the socket, write it immediately, and unset to free memory
   654 			// read data from the socket, write it immediately, and unset to free memory
   655       $headers = $this->get_response_headers();
   655 			$headers = $this->get_response_headers();
   656       $transferred_bytes = 0;
   656 			$transferred_bytes = 0;
   657       $bandwidth_exceeded = false;
   657 			$bandwidth_exceeded = false;
   658       // if transfer-encoding is chunked, read using chunk sizes the server specifies
   658 			// if transfer-encoding is chunked, read using chunk sizes the server specifies
   659       $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
   659 			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
   660       if ( $is_chunked )
   660 			if ( $is_chunked )
   661       {
   661 			{
   662         $buffer = '';
   662 				$buffer = '';
   663         while ( !feof($this->socket) )
   663 				while ( !feof($this->socket) )
   664         {
   664 				{
   665           $part = fgets($this->socket, ( 1024 * $chunklen ));
   665 					$part = fgets($this->socket, ( 1024 * $chunklen ));
   666           // Theoretically if the encoding is really chunked then this should always match.
   666 					// Theoretically if the encoding is really chunked then this should always match.
   667           if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
   667 					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
   668           {
   668 					{
   669             $chunk_length = hexdec($match[1]);
   669 						$chunk_length = hexdec($match[1]);
   670             $part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
   670 						$part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
   671             // remove the last newline from $part
   671 						// remove the last newline from $part
   672             $part = preg_replace("/\r?\n\$/m", "", $part);
   672 						$part = preg_replace("/\r?\n\$/m", "", $part);
   673           }
   673 					}
   674           
   674 					
   675           $transferred_bytes += strlen($part);
   675 					$transferred_bytes += strlen($part);
   676           if ( $max_file_size && $transferred_bytes > $max_file_size )
   676 					if ( $max_file_size && $transferred_bytes > $max_file_size )
   677           {
   677 					{
   678             // truncate output to $max_file_size bytes
   678 						// truncate output to $max_file_size bytes
   679             $partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
   679 						$partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
   680             $part = substr($part, 0, $partlen);
   680 						$part = substr($part, 0, $partlen);
   681             $bandwidth_exceeded = true;
   681 						$bandwidth_exceeded = true;
   682           }
   682 					}
   683           fwrite($handle, $part);
   683 					fwrite($handle, $part);
   684           if ( $bandwidth_exceeded )
   684 					if ( $bandwidth_exceeded )
   685           {
   685 					{
   686             break;
   686 						break;
   687           }
   687 					}
   688         }
   688 				}
   689       }
   689 			}
   690       else
   690 			else
   691       {
   691 			{
   692         $first_chunk = fread($this->socket, ( 1024 * $chunklen ));
   692 				$first_chunk = fread($this->socket, ( 1024 * $chunklen ));
   693         fwrite($handle, $first_chunk);
   693 				fwrite($handle, $first_chunk);
   694         while ( !feof($this->socket) )
   694 				while ( !feof($this->socket) )
   695         {
   695 				{
   696           $chunk = fread($this->socket, ( 1024 * $chunklen ));
   696 					$chunk = fread($this->socket, ( 1024 * $chunklen ));
   697           
   697 					
   698           $transferred_bytes += strlen($chunk);
   698 					$transferred_bytes += strlen($chunk);
   699           if ( $max_file_size && $transferred_bytes > $max_file_size )
   699 					if ( $max_file_size && $transferred_bytes > $max_file_size )
   700           {
   700 					{
   701             // truncate output to $max_file_size bytes
   701 						// truncate output to $max_file_size bytes
   702             $partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
   702 						$partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
   703             $chunk = substr($chunk, 0, $partlen);
   703 						$chunk = substr($chunk, 0, $partlen);
   704             $bandwidth_exceeded = true;
   704 						$bandwidth_exceeded = true;
   705           }
   705 					}
   706           
   706 					
   707           fwrite($handle, $chunk);
   707 					fwrite($handle, $chunk);
   708           unset($chunk);
   708 					unset($chunk);
   709           
   709 					
   710           if ( $bandwidth_exceeded )
   710 					if ( $bandwidth_exceeded )
   711           {
   711 					{
   712             break;
   712 						break;
   713           }
   713 					}
   714         }
   714 				}
   715       }
   715 			}
   716     }
   716 		}
   717     fclose($handle);
   717 		fclose($handle);
   718     // close socket and reset state, since we haven't cached the response
   718 		// close socket and reset state, since we haven't cached the response
   719     $this->sock_close($this->socket);
   719 		$this->sock_close($this->socket);
   720     $this->state = 0;
   720 		$this->state = 0;
   721     return ($bandwidth_exceeded) ? false : true;
   721 		return ($bandwidth_exceeded) ? false : true;
   722   }
   722 	}
   723   
   723 	
   724   /**
   724 	/**
   725    * Resolves, based on current settings and URI, a URI string to an array consisting of a host, port, and new URI. Returns false on error.
   725  	* Resolves, based on current settings and URI, a URI string to an array consisting of a host, port, and new URI. Returns false on error.
   726    * @param string
   726  	* @param string
   727    * @return array
   727  	* @return array
   728    */
   728  	*/
   729   
   729 	
   730   function _resolve_uri($uri)
   730 	function _resolve_uri($uri)
   731   {
   731 	{
   732     // long ass regexp w00t
   732 		// long ass regexp w00t
   733     if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
   733 		if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
   734     {
   734 		{
   735       // bad target URI
   735 			// bad target URI
   736       return false;
   736 			return false;
   737     }
   737 		}
   738     $hostpart = $match[1];
   738 		$hostpart = $match[1];
   739     if ( empty($hostpart) )
   739 		if ( empty($hostpart) )
   740     {
   740 		{
   741       // use existing host
   741 			// use existing host
   742       $host = $this->host;
   742 			$host = $this->host;
   743       $port = $this->port;
   743 			$port = $this->port;
   744     }
   744 		}
   745     else
   745 		else
   746     {
   746 		{
   747       $host = $match[1];
   747 			$host = $match[1];
   748       $port = empty($match[2]) ? 80 : intval($match[2]);
   748 			$port = empty($match[2]) ? 80 : intval($match[2]);
   749     }
   749 		}
   750     // is this an absolute URI, or relative?
   750 		// is this an absolute URI, or relative?
   751     if ( empty($match[3]) )
   751 		if ( empty($match[3]) )
   752     {
   752 		{
   753       // relative
   753 			// relative
   754       $uri = dirname($this->uri) . $match[4];
   754 			$uri = dirname($this->uri) . $match[4];
   755     }
   755 		}
   756     else
   756 		else
   757     {
   757 		{
   758       // absolute
   758 			// absolute
   759       $uri = '/' . $match[4];
   759 			$uri = '/' . $match[4];
   760     }
   760 		}
   761     return array(
   761 		return array(
   762         'host' => $host,
   762 				'host' => $host,
   763         'port' => $port,
   763 				'port' => $port,
   764         'uri'  => $uri
   764 				'uri'  => $uri
   765       );
   765 			);
   766   }
   766 	}
   767   
   767 	
   768   /**
   768 	/**
   769    * Returns only the response headers.
   769  	* Returns only the response headers.
   770    * @return string
   770  	* @return string
   771    */
   771  	*/
   772   
   772 	
   773   function get_response_headers()
   773 	function get_response_headers()
   774   {
   774 	{
   775     if ( $this->state == 3 )
   775 		if ( $this->state == 3 )
   776     {
   776 		{
   777       return $this->response;
   777 			return $this->response;
   778     }
   778 		}
   779     else if ( $this->state == 4 )
   779 		else if ( $this->state == 4 )
   780     {
   780 		{
   781       $pos_end = strpos($this->response, "\r\n\r\n");
   781 			$pos_end = strpos($this->response, "\r\n\r\n");
   782       if ( empty($pos_end) )
   782 			if ( empty($pos_end) )
   783       {
   783 			{
   784         $pos_end = strpos($this->response, "\n\n");
   784 				$pos_end = strpos($this->response, "\n\n");
   785       }
   785 			}
   786       $data = substr($this->response, 0, $pos_end);
   786 			$data = substr($this->response, 0, $pos_end);
   787       return $data;
   787 			return $data;
   788     }
   788 		}
   789     else
   789 		else
   790     {
   790 		{
   791       $this->_send_request_headers_only();
   791 			$this->_send_request_headers_only();
   792       return $this->response;
   792 			return $this->response;
   793     }
   793 		}
   794   }
   794 	}
   795   
   795 	
   796   /**
   796 	/**
   797    * Returns only the response headers, as an associative array.
   797  	* Returns only the response headers, as an associative array.
   798    * @return array
   798  	* @return array
   799    */
   799  	*/
   800   
   800 	
   801   function get_response_headers_array()
   801 	function get_response_headers_array()
   802   {
   802 	{
   803     $data = $this->get_response_headers();
   803 		$data = $this->get_response_headers();
   804     preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
   804 		preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
   805     $headers = array();
   805 		$headers = array();
   806     for ( $i = 0; $i < count($matches[0]); $i++ )
   806 		for ( $i = 0; $i < count($matches[0]); $i++ )
   807     {
   807 		{
   808       $headers[ $matches[2][$i] ] = $matches[3][$i];
   808 			$headers[ $matches[2][$i] ] = $matches[3][$i];
   809     }
   809 		}
   810     return $headers;
   810 		return $headers;
   811   }
   811 	}
   812   
   812 	
   813   /**
   813 	/**
   814    * Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
   814  	* Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
   815    * @return string
   815  	* @return string
   816    */
   816  	*/
   817   
   817 	
   818   function get_response_body()
   818 	function get_response_body()
   819   {
   819 	{
   820     $data = $this->get_response();
   820 		$data = $this->get_response();
   821     $pos_start = strpos($data, "\r\n\r\n") + 4;
   821 		$pos_start = strpos($data, "\r\n\r\n") + 4;
   822     if ( $pos_start == 4 )
   822 		if ( $pos_start == 4 )
   823     {
   823 		{
   824       $pos_start = strpos($data, "\n\n") + 4;
   824 			$pos_start = strpos($data, "\n\n") + 4;
   825     }
   825 		}
   826     $data = substr($data, $pos_start);
   826 		$data = substr($data, $pos_start);
   827     return $data;
   827 		return $data;
   828   }
   828 	}
   829   
   829 	
   830   /**
   830 	/**
   831    * Returns all cookies requested to be set by the server as an associative array. If the request hasn't been sent, it will be sent here.
   831  	* Returns all cookies requested to be set by the server as an associative array. If the request hasn't been sent, it will be sent here.
   832    * @return array
   832  	* @return array
   833    */
   833  	*/
   834   
   834 	
   835   function get_cookies()
   835 	function get_cookies()
   836   {
   836 	{
   837     $data = $this->get_response();
   837 		$data = $this->get_response();
   838     $data = str_replace("\r\n", "\n", $data);
   838 		$data = str_replace("\r\n", "\n", $data);
   839     $pos = strpos($data, "\n\n");
   839 		$pos = strpos($data, "\n\n");
   840     $headers = substr($data, 0, $pos);
   840 		$headers = substr($data, 0, $pos);
   841     preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
   841 		preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
   842     if ( count($cookiematch[0]) < 1 )
   842 		if ( count($cookiematch[0]) < 1 )
   843       return array();
   843 			return array();
   844     $cookies = array();
   844 		$cookies = array();
   845     foreach ( $cookiematch[0] as $i => $cookie )
   845 		foreach ( $cookiematch[0] as $i => $cookie )
   846     {
   846 		{
   847       $cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
   847 			$cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
   848     }
   848 		}
   849     return $cookies;
   849 		return $cookies;
   850   }
   850 	}
   851   
   851 	
   852   /**
   852 	/**
   853    * Internal method to write data to a socket with debugging possibility.
   853  	* Internal method to write data to a socket with debugging possibility.
   854    * @access private
   854  	* @access private
   855    */
   855  	*/
   856   
   856 	
   857   function _fputs($socket, $data)
   857 	function _fputs($socket, $data)
   858   {
   858 	{
   859     if ( $this->debug )
   859 		if ( $this->debug )
   860       echo htmlspecialchars($data);
   860 			echo htmlspecialchars($data);
   861     
   861 		
   862     return fputs($socket, $data);
   862 		return fputs($socket, $data);
   863   }
   863 	}
   864   
   864 	
   865   /**
   865 	/**
   866    * Internal function to stringify cookies, headers, get, and post.
   866  	* Internal function to stringify cookies, headers, get, and post.
   867    * @access private
   867  	* @access private
   868    */
   868  	*/
   869   
   869 	
   870   function concat_headers(&$headers, &$cookies, &$get, &$post)
   870 	function concat_headers(&$headers, &$cookies, &$get, &$post)
   871   {
   871 	{
   872     $headers = '';
   872 		$headers = '';
   873     $cookies = '';
   873 		$cookies = '';
   874     foreach ( $this->headers as $name => $value )
   874 		foreach ( $this->headers as $name => $value )
   875     {
   875 		{
   876       $value = str_replace('\\n', '\\\\n', $value);
   876 			$value = str_replace('\\n', '\\\\n', $value);
   877       $value = str_replace("\n", '\\n', $value);
   877 			$value = str_replace("\n", '\\n', $value);
   878       $headers .= "$name: $value\r\n";
   878 			$headers .= "$name: $value\r\n";
   879     }
   879 		}
   880     unset($value);
   880 		unset($value);
   881     if ( count($this->cookies_out) > 0 )
   881 		if ( count($this->cookies_out) > 0 )
   882     {
   882 		{
   883       $i = 0;
   883 			$i = 0;
   884       $cookie_header = 'Cookie: ';
   884 			$cookie_header = 'Cookie: ';
   885       foreach ( $this->cookies_out as $name => $value )
   885 			foreach ( $this->cookies_out as $name => $value )
   886       {
   886 			{
   887         $i++;
   887 				$i++;
   888         if ( $i > 1 )
   888 				if ( $i > 1 )
   889           $cookie_header .= '; ';
   889 					$cookie_header .= '; ';
   890         $value = str_replace(';', rawurlencode(';'), $value);
   890 				$value = str_replace(';', rawurlencode(';'), $value);
   891         $value = str_replace('\\n', '\\\\n', $value);
   891 				$value = str_replace('\\n', '\\\\n', $value);
   892         $value = str_replace("\n", '\\n', $value);
   892 				$value = str_replace("\n", '\\n', $value);
   893         $cookie_header .= "$name=$value";
   893 				$cookie_header .= "$name=$value";
   894       }
   894 			}
   895       $cookie_header .= "\r\n";
   895 			$cookie_header .= "\r\n";
   896       $cookies = $cookie_header;
   896 			$cookies = $cookie_header;
   897       unset($value, $cookie_header);
   897 			unset($value, $cookie_header);
   898     }
   898 		}
   899     if ( count($this->parms_get) > 0 )
   899 		if ( count($this->parms_get) > 0 )
   900     {
   900 		{
   901       $get = '?';
   901 			$get = '?';
   902       $i = 0;
   902 			$i = 0;
   903       foreach ( $this->parms_get as $name => $value )
   903 			foreach ( $this->parms_get as $name => $value )
   904       {
   904 			{
   905         $i++;
   905 				$i++;
   906         if ( $i > 1 )
   906 				if ( $i > 1 )
   907           $get .= '&';
   907 					$get .= '&';
   908         $value = urlencode($value);
   908 				$value = urlencode($value);
   909         if ( !empty($value) || is_string($value) )
   909 				if ( !empty($value) || is_string($value) )
   910           $get .= "$name=$value";
   910 					$get .= "$name=$value";
   911         else
   911 				else
   912           $get .= "$name";
   912 					$get .= "$name";
   913       }
   913 			}
   914     }
   914 		}
   915     if ( count($this->parms_post) > 0 )
   915 		if ( count($this->parms_post) > 0 )
   916     {
   916 		{
   917       $post = '';
   917 			$post = '';
   918       $i = 0;
   918 			$i = 0;
   919       foreach ( $this->parms_post as $name => $value )
   919 			foreach ( $this->parms_post as $name => $value )
   920       {
   920 			{
   921         $i++;
   921 				$i++;
   922         if ( $i > 1 )
   922 				if ( $i > 1 )
   923           $post .= '&';
   923 					$post .= '&';
   924         $value = urlencode($value);
   924 				$value = urlencode($value);
   925         $post .= "$name=$value";
   925 				$post .= "$name=$value";
   926       }
   926 			}
   927     }
   927 		}
   928   }
   928 	}
   929   
   929 	
   930 }
   930 }
   931 
   931