includes/http.php
changeset 1227 bdac73ed481e
parent 1224 a54e149f4a78
child 1234 903bb153c265
--- a/includes/http.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/http.php	Sun Mar 28 23:10:46 2010 -0400
@@ -88,844 +88,844 @@
 
 class Request_HTTP
 {
-  
-  /**
-   * Switch to enable or disable debugging. You want this off on production sites.
-   * @var bool
-   */
-  
-  var $debug = false;
-  
-  /**
-   * The host the request will be sent to.
-   * @var string
-   */
-  
-  var $host = '';
-  
-  /**
-   * The TCP port our connection is (will be) on.
-   * @var int
-   */
-  
-  var $port = 80;
-  
-  /**
-   * The request method. Can be GET or POST, defaults to GET.
-   * @var string
-   */
-  
-  var $method = 'GET';
-  
-  /**
-   * The URI to the remote script.
-   * @var string
-   */
-  
-  var $uri = '';
-  
-  /**
-   * The parameters to be sent on GET.
-   * @var array (associative)
-   */
-  
-  var $parms_get = array();
-  
-  /**
-   * The parameters to be sent on POST. Ignored if $this->method == GET.
-   * @var array (associative)
-   */
-  
-  var $parms_post = array();
-  
-  /**
-   * The list of cookies that will be sent.
-   * @var array (associative)
-   */
-  
-  var $cookies_out = array();
-  
-  /**
-   * Additional request headers.
-   * @var array (associative)
-   */
-  
-  var $headers = array();
-  
-  /**
-   * Follow server-side redirects; defaults to true.
-   * @var bool
-   */
-  
-  var $follow_redirects = true;
-  
-  /**
-   * Cached response.
-   * @var string, or bool:false if the request hasn't been sent yet
-   */
-  
-  var $response = false;
-  
-  /**
-   * Cached response code
-   * @var int set to -1 if request hasn't been sent yet
-   */
-  
-  var $response_code = -1;
-  
-  /**
-   * Cached response code string
-   * @var string or bool:false if the request hasn't been sent yet
-   */
-  
-  var $response_string = false;
-  
-  /**
-   * Resource for the socket. False if a connection currently isn't going.
-   * @var resource
-   */
-  
-  var $socket = false;
-  
-  /**
-   * True if SSL is on, defaults to false
-   * @var bool
-   */
-  
-  var $ssl = false;
-  
-  /**
-   * 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.
-   * @var int
-   */
-  
-  var $state = 0;
-  
-  /**
-   * Constructor.
-   * @param string Hostname to send to
-   * @param string URI (/index.php)
-   * @param string Request method - GET or POST.
-   * @param int Optional. The port to open the request on. Defaults to 80.
-   * @param bool If true, uses SSL (and defaults the port to 443)
-   */
-  
-  function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
-  {
-    if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
-      throw new Exception(__CLASS__ . ': Invalid hostname');
-    if ( $ssl )
-    {
-      $this->ssl = true;
-      $port = ( $port === 'default' ) ? 443 : $port;
-    }
-    else
-    {
-      $this->ssl = false;
-      $port = ( $port === 'default' ) ? 80 : $port;
-    }
-    // Yes - this really does support IPv6 URLs!
-    $this->host = $host;
-    $this->uri = $uri;
-    if ( is_int($port) && $port >= 1 && $port <= 65535 )
-      $this->port = $port;
-    else
-      throw new Exception(__CLASS__ . ': Invalid port');
-    $method = strtoupper($method);
-    if ( $method == 'GET' || $method == 'POST' )
-      $this->method = $method;
-    else
-      throw new Exception(__CLASS__ . ': Invalid request method');
-      
-    $newline = "\r\n";
-    $php_ver = PHP_VERSION;
-    $server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
-    $this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
-  }
-  
-  /**
-   * Sets one or more cookies to be sent to the server.
-   * @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
-   * @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.
-   */
-  
-  function add_cookie($cookiename, $cookievalue = false)
-  {
-    if ( is_array($cookiename) && !$cookievalue )
-    {
-      foreach ( $cookiename as $name => $value )
-      {
-        $this->cookies_out[$name] = $value;
-      }
-    }
-    else if ( is_string($cookiename) && is_string($cookievalue) )
-    {
-      $this->cookies_out[$cookiename] = $cookievalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Sets one or more request header values.
-   * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
-   * @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.
-   */
-  
-  function add_header($headername, $headervalue = false)
-  {
-    if ( is_array($headername) && !$headervalue )
-    {
-      foreach ( $headername as $name => $value )
-      {
-        $this->headers[$name] = $value;
-      }
-    }
-    else if ( is_string($headername) && is_string($headervalue) )
-    {
-      $this->headers[$headername] = $headervalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Adds one or more values to be passed on GET.
-   * @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
-   * @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.
-   */
-  
-  function add_get($getname, $getvalue = false)
-  {
-    if ( is_array($getname) && !$getvalue )
-    {
-      foreach ( $getname as $name => $value )
-      {
-        $this->parms_get[$name] = $value;
-      }
-    }
-    else if ( is_string($getname) && is_string($getvalue) )
-    {
-      $this->parms_get[$getname] = $getvalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Adds one or more values to be passed on POST.
-   * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
-   * @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.
-   */
-  
-  function add_post($postname, $postvalue = false)
-  {
-    if ( is_array($postname) && !$postvalue )
-    {
-      foreach ( $postname as $name => $value )
-      {
-        $this->parms_post[$name] = $value;
-      }
-    }
-    else if ( is_string($postname) && is_string($postvalue) )
-    {
-      $this->parms_post[$postname] = $postvalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Internal function to open up the socket.
-   * @access private
-   */
-  
-  function _sock_open(&$connection)
-  {
-    // Open connection
-    $ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
-    $connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
-    if ( !$connection )
-      throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
-    
-    // 1 = socket open
-    $this->state = 1;
-  }
-  
-  /**
-   * Internal function to actually write the request into the socket.
-   * @access private
-   */
-  
-  function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
-  {
-    $newline = "\r\n";
-    
-    if ( $this->debug )
-      echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
-    
-    if ( $this->debug )
-    {
-      echo '<hr /><div style="white-space: nowrap;">';
-      echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
-      echo "<p><b>Headers:</b></p><pre>$headers</pre>";
-      echo "<p><b>Cookies:</b> $cookies</p>";
-      echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
-      echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
-      echo "<pre>";
-    }
-    
-    $portline = ( $this->port == 80 ) ? '' : ":$this->port";
-    
-    $this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
-    $this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
-    $this->_fputs($connection, $headers);
-    $this->_fputs($connection, $cookies);
-    
-    if ( $this->method == 'POST' )
-    {
-      // POST-specific parameters
-      $post_length = strlen($post);
-      $this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
-      $this->_fputs($connection, "Content-length: {$post_length}{$newline}");
-    }
-    
-    $this->_fputs($connection, "Connection: close{$newline}");
-    $this->_fputs($connection, "{$newline}");
-    
-    if ( $this->method == 'POST' )
-    {
-      $this->_fputs($connection, $post);
-    }
-    
-    if ( $this->debug )
-      echo '</pre><p>Request written. Fetching response.</p>';
-    
-    // 2 = request written
-    $this->state = 2;
-  }
-  
-  /**
-   * Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
-   * @access private
-   */
-  
-  function sock_close(&$connection)
-  {
-    if ( $this->debug )
-    {
-      echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
-      echo htmlspecialchars($this->response);
-      echo '</pre></div><hr />';
-    }
-    
-    fclose($connection);
-    $this->state = 0;
-  }
-  
-  /**
-   * Internal function to grab the response code and status string
-   * @access string
-   */
-  
-  function _parse_response_code($buffer)
-  {
-    // Retrieve response code and status
-    $pos_newline = strpos($buffer, "\n");
-    $pos_carriage_return = strpos($buffer, "\r");
-    $pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
-    
-    // First line is in format of:
-    // HTTP/1.1 ### Blah blah blah(\r?)\n
-    $response_code = substr($buffer, 9, 3);
-    $response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
-    $this->response_code = intval($response_code);
-    $this->response_string = $response_string;
-  }
-  
-  /**
-   * Internal function to send the request.
-   * @access private
-   */
-  
-  function _send_request()
-  {
-    $this->concat_headers($headers, $cookies, $get, $post);
-    
-    if ( $this->state < 1 )
-    {
-      $this->_sock_open($this->socket);
-    }
-    if ( $this->state < 2 )
-    {
-      $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-    }
-    if ( $this->state == 2 )
-    {
-      $buffer = $this->_read_until_newlines($this->socket);
-      $this->state = 3;
-      $this->_parse_response_code($buffer);
-      $this->response = $buffer;
-    }
-    // obey redirects
-    $i = 0;
-    while ( $i < 20 && $this->follow_redirects )
-    {
-      $incoming_headers = $this->get_response_headers_array();
-      if ( !$incoming_headers )
-        break;
-      if ( isset($incoming_headers['Location']) )
-      {
-        // we've been redirected...
-        $new_uri = $this->_resolve_uri($incoming_headers['Location']);
-        if ( !$new_uri )
-        {
-          // ... bad URI, ignore Location header.
-          break;
-        }
-        // change location
-        $this->host = $new_uri['host'];
-        $this->port = $new_uri['port'];
-        $this->uri  = $new_uri['uri'];
-        $get = '';
-        
-        // reset
-        $this->sock_close($this->socket);
-        $this->_sock_open($this->socket);
-        $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-        $buffer = $this->_read_until_newlines($this->socket);
-        $this->state = 3;
-        $this->_parse_response_code($buffer);
-        $this->response = $buffer;
-        $i++;
-      }
-      else
-      {
-        break;
-      }
-    }
-    if ( $i == 20 )
-    {
-      throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
-    }
-    
-    if ( $this->state == 3 )
-    {
-      // Determine transfer encoding
-      $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
-      if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
-      {
-        $size = intval($match[1]);
-        if ( $this->debug )
-        {
-          echo "Pulling response using fread(), size $size\n";
-        }
-        $this->response .= fread($this->socket, $size);
-      }
-      else
-      {
-        if ( $this->debug )
-          echo "Pulling response using chunked handler\n";
-          
-        $buffer = '';
-        while ( !feof($this->socket) )
-        {
-          $part = fgets($this->socket, 1024);
-          if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
-          {
-            $chunklen = hexdec($match[1]);
-            $part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
-            // remove the last newline from $part
-            $part = preg_replace("/\r?\n\$/", "", $part);
-          }
-          $buffer .= $part;
-        }
-        $this->response .= $buffer;
-      }
-    }
-    $this->state = 4;
-    
-    $this->sock_close($this->socket);
-    $this->socket = false;
-  }
-  
-  /**
-   * Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
-   * @access private
-   */
-  
-  function _send_request_headers_only()
-  {
-    $this->concat_headers($headers, $cookies, $get, $post);
-    
-    if ( $this->state < 1 )
-    {
-      $this->_sock_open($this->socket);
-    }
-    if ( $this->state < 2 )
-    {
-      $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-    }
-    if ( $this->state == 2 )
-    {
-      $buffer = $this->_read_until_newlines($this->socket);
-      $this->state = 3;
-      $this->_parse_response_code($buffer);
-      $this->response = $buffer;
-    }
-  }
-  
-  /**
-   * Internal function to read from a socket until two consecutive newlines are hit.
-   * @access private
-   */
-  
-  function _read_until_newlines($sock)
-  {
-    $prev_char = '';
-    $prev1_char = '';
-    $prev2_char = '';
-    $buf = '';
-    while ( !feof($sock) )
-    {
-      $chr = fread($sock, 1);
-      $buf .= $chr;
-      if ( ( $chr == "\n" && $prev_char == "\n" ) ||
-           ( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
-      {
-        return $buf;
-      }
-      $prev2_char = $prev1_char;
-      $prev1_char = $prev_char;
-      $prev_char = $chr;
-    }
-    return $buf;
-  }
-  
-  /**
-   * Returns the response text. If the request hasn't been sent, it will be sent here.
-   * @return string
-   */
-  
-  function get_response()
-  {
-    if ( $this->state == 4 )
-      return $this->response;
-    $this->_send_request();
-    return $this->response;
-  }
-  
-  /**
-   * 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.
-   * @param string File to write to
-   * @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.
-   * @param int Maximum file size. Defaults to 0, which means no limit.
-   * @return bool True on success, false on failure
-   */
-  
-  function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
-  {
-    if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
-    {
-      return false;
-    }
-    $handle = @fopen($file, 'w');
-    if ( !$handle )
-      return false;
-    $chunklen = intval($chunklen);
-    if ( $chunklen < 1 )
-      return false;
-    if ( $this->state == 4 )
-    {
-      // we already have the response, so cheat
-      $response = $this->get_response_body();
-      fwrite($handle, $response);
-    }
-    else
-    {
-      // read data from the socket, write it immediately, and unset to free memory
-      $headers = $this->get_response_headers();
-      $transferred_bytes = 0;
-      $bandwidth_exceeded = false;
-      // if transfer-encoding is chunked, read using chunk sizes the server specifies
-      $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
-      if ( $is_chunked )
-      {
-        $buffer = '';
-        while ( !feof($this->socket) )
-        {
-          $part = fgets($this->socket, ( 1024 * $chunklen ));
-          // Theoretically if the encoding is really chunked then this should always match.
-          if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
-          {
-            $chunk_length = hexdec($match[1]);
-            $part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
-            // remove the last newline from $part
-            $part = preg_replace("/\r?\n\$/m", "", $part);
-          }
-          
-          $transferred_bytes += strlen($part);
-          if ( $max_file_size && $transferred_bytes > $max_file_size )
-          {
-            // truncate output to $max_file_size bytes
-            $partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
-            $part = substr($part, 0, $partlen);
-            $bandwidth_exceeded = true;
-          }
-          fwrite($handle, $part);
-          if ( $bandwidth_exceeded )
-          {
-            break;
-          }
-        }
-      }
-      else
-      {
-        $first_chunk = fread($this->socket, ( 1024 * $chunklen ));
-        fwrite($handle, $first_chunk);
-        while ( !feof($this->socket) )
-        {
-          $chunk = fread($this->socket, ( 1024 * $chunklen ));
-          
-          $transferred_bytes += strlen($chunk);
-          if ( $max_file_size && $transferred_bytes > $max_file_size )
-          {
-            // truncate output to $max_file_size bytes
-            $partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
-            $chunk = substr($chunk, 0, $partlen);
-            $bandwidth_exceeded = true;
-          }
-          
-          fwrite($handle, $chunk);
-          unset($chunk);
-          
-          if ( $bandwidth_exceeded )
-          {
-            break;
-          }
-        }
-      }
-    }
-    fclose($handle);
-    // close socket and reset state, since we haven't cached the response
-    $this->sock_close($this->socket);
-    $this->state = 0;
-    return ($bandwidth_exceeded) ? false : true;
-  }
-  
-  /**
-   * 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.
-   * @param string
-   * @return array
-   */
-  
-  function _resolve_uri($uri)
-  {
-    // long ass regexp w00t
-    if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
-    {
-      // bad target URI
-      return false;
-    }
-    $hostpart = $match[1];
-    if ( empty($hostpart) )
-    {
-      // use existing host
-      $host = $this->host;
-      $port = $this->port;
-    }
-    else
-    {
-      $host = $match[1];
-      $port = empty($match[2]) ? 80 : intval($match[2]);
-    }
-    // is this an absolute URI, or relative?
-    if ( empty($match[3]) )
-    {
-      // relative
-      $uri = dirname($this->uri) . $match[4];
-    }
-    else
-    {
-      // absolute
-      $uri = '/' . $match[4];
-    }
-    return array(
-        'host' => $host,
-        'port' => $port,
-        'uri'  => $uri
-      );
-  }
-  
-  /**
-   * Returns only the response headers.
-   * @return string
-   */
-  
-  function get_response_headers()
-  {
-    if ( $this->state == 3 )
-    {
-      return $this->response;
-    }
-    else if ( $this->state == 4 )
-    {
-      $pos_end = strpos($this->response, "\r\n\r\n");
-      if ( empty($pos_end) )
-      {
-        $pos_end = strpos($this->response, "\n\n");
-      }
-      $data = substr($this->response, 0, $pos_end);
-      return $data;
-    }
-    else
-    {
-      $this->_send_request_headers_only();
-      return $this->response;
-    }
-  }
-  
-  /**
-   * Returns only the response headers, as an associative array.
-   * @return array
-   */
-  
-  function get_response_headers_array()
-  {
-    $data = $this->get_response_headers();
-    preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
-    $headers = array();
-    for ( $i = 0; $i < count($matches[0]); $i++ )
-    {
-      $headers[ $matches[2][$i] ] = $matches[3][$i];
-    }
-    return $headers;
-  }
-  
-  /**
-   * Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
-   * @return string
-   */
-  
-  function get_response_body()
-  {
-    $data = $this->get_response();
-    $pos_start = strpos($data, "\r\n\r\n") + 4;
-    if ( $pos_start == 4 )
-    {
-      $pos_start = strpos($data, "\n\n") + 4;
-    }
-    $data = substr($data, $pos_start);
-    return $data;
-  }
-  
-  /**
-   * 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.
-   * @return array
-   */
-  
-  function get_cookies()
-  {
-    $data = $this->get_response();
-    $data = str_replace("\r\n", "\n", $data);
-    $pos = strpos($data, "\n\n");
-    $headers = substr($data, 0, $pos);
-    preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
-    if ( count($cookiematch[0]) < 1 )
-      return array();
-    $cookies = array();
-    foreach ( $cookiematch[0] as $i => $cookie )
-    {
-      $cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
-    }
-    return $cookies;
-  }
-  
-  /**
-   * Internal method to write data to a socket with debugging possibility.
-   * @access private
-   */
-  
-  function _fputs($socket, $data)
-  {
-    if ( $this->debug )
-      echo htmlspecialchars($data);
-    
-    return fputs($socket, $data);
-  }
-  
-  /**
-   * Internal function to stringify cookies, headers, get, and post.
-   * @access private
-   */
-  
-  function concat_headers(&$headers, &$cookies, &$get, &$post)
-  {
-    $headers = '';
-    $cookies = '';
-    foreach ( $this->headers as $name => $value )
-    {
-      $value = str_replace('\\n', '\\\\n', $value);
-      $value = str_replace("\n", '\\n', $value);
-      $headers .= "$name: $value\r\n";
-    }
-    unset($value);
-    if ( count($this->cookies_out) > 0 )
-    {
-      $i = 0;
-      $cookie_header = 'Cookie: ';
-      foreach ( $this->cookies_out as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $cookie_header .= '; ';
-        $value = str_replace(';', rawurlencode(';'), $value);
-        $value = str_replace('\\n', '\\\\n', $value);
-        $value = str_replace("\n", '\\n', $value);
-        $cookie_header .= "$name=$value";
-      }
-      $cookie_header .= "\r\n";
-      $cookies = $cookie_header;
-      unset($value, $cookie_header);
-    }
-    if ( count($this->parms_get) > 0 )
-    {
-      $get = '?';
-      $i = 0;
-      foreach ( $this->parms_get as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $get .= '&';
-        $value = urlencode($value);
-        if ( !empty($value) || is_string($value) )
-          $get .= "$name=$value";
-        else
-          $get .= "$name";
-      }
-    }
-    if ( count($this->parms_post) > 0 )
-    {
-      $post = '';
-      $i = 0;
-      foreach ( $this->parms_post as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $post .= '&';
-        $value = urlencode($value);
-        $post .= "$name=$value";
-      }
-    }
-  }
-  
+	
+	/**
+ 	* Switch to enable or disable debugging. You want this off on production sites.
+ 	* @var bool
+ 	*/
+	
+	var $debug = false;
+	
+	/**
+ 	* The host the request will be sent to.
+ 	* @var string
+ 	*/
+	
+	var $host = '';
+	
+	/**
+ 	* The TCP port our connection is (will be) on.
+ 	* @var int
+ 	*/
+	
+	var $port = 80;
+	
+	/**
+ 	* The request method. Can be GET or POST, defaults to GET.
+ 	* @var string
+ 	*/
+	
+	var $method = 'GET';
+	
+	/**
+ 	* The URI to the remote script.
+ 	* @var string
+ 	*/
+	
+	var $uri = '';
+	
+	/**
+ 	* The parameters to be sent on GET.
+ 	* @var array (associative)
+ 	*/
+	
+	var $parms_get = array();
+	
+	/**
+ 	* The parameters to be sent on POST. Ignored if $this->method == GET.
+ 	* @var array (associative)
+ 	*/
+	
+	var $parms_post = array();
+	
+	/**
+ 	* The list of cookies that will be sent.
+ 	* @var array (associative)
+ 	*/
+	
+	var $cookies_out = array();
+	
+	/**
+ 	* Additional request headers.
+ 	* @var array (associative)
+ 	*/
+	
+	var $headers = array();
+	
+	/**
+ 	* Follow server-side redirects; defaults to true.
+ 	* @var bool
+ 	*/
+	
+	var $follow_redirects = true;
+	
+	/**
+ 	* Cached response.
+ 	* @var string, or bool:false if the request hasn't been sent yet
+ 	*/
+	
+	var $response = false;
+	
+	/**
+ 	* Cached response code
+ 	* @var int set to -1 if request hasn't been sent yet
+ 	*/
+	
+	var $response_code = -1;
+	
+	/**
+ 	* Cached response code string
+ 	* @var string or bool:false if the request hasn't been sent yet
+ 	*/
+	
+	var $response_string = false;
+	
+	/**
+ 	* Resource for the socket. False if a connection currently isn't going.
+ 	* @var resource
+ 	*/
+	
+	var $socket = false;
+	
+	/**
+ 	* True if SSL is on, defaults to false
+ 	* @var bool
+ 	*/
+	
+	var $ssl = false;
+	
+	/**
+ 	* 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.
+ 	* @var int
+ 	*/
+	
+	var $state = 0;
+	
+	/**
+ 	* Constructor.
+ 	* @param string Hostname to send to
+ 	* @param string URI (/index.php)
+ 	* @param string Request method - GET or POST.
+ 	* @param int Optional. The port to open the request on. Defaults to 80.
+ 	* @param bool If true, uses SSL (and defaults the port to 443)
+ 	*/
+	
+	function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
+	{
+		if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
+			throw new Exception(__CLASS__ . ': Invalid hostname');
+		if ( $ssl )
+		{
+			$this->ssl = true;
+			$port = ( $port === 'default' ) ? 443 : $port;
+		}
+		else
+		{
+			$this->ssl = false;
+			$port = ( $port === 'default' ) ? 80 : $port;
+		}
+		// Yes - this really does support IPv6 URLs!
+		$this->host = $host;
+		$this->uri = $uri;
+		if ( is_int($port) && $port >= 1 && $port <= 65535 )
+			$this->port = $port;
+		else
+			throw new Exception(__CLASS__ . ': Invalid port');
+		$method = strtoupper($method);
+		if ( $method == 'GET' || $method == 'POST' )
+			$this->method = $method;
+		else
+			throw new Exception(__CLASS__ . ': Invalid request method');
+			
+		$newline = "\r\n";
+		$php_ver = PHP_VERSION;
+		$server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
+		$this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
+	}
+	
+	/**
+ 	* Sets one or more cookies to be sent to the server.
+ 	* @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
+ 	* @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.
+ 	*/
+	
+	function add_cookie($cookiename, $cookievalue = false)
+	{
+		if ( is_array($cookiename) && !$cookievalue )
+		{
+			foreach ( $cookiename as $name => $value )
+			{
+				$this->cookies_out[$name] = $value;
+			}
+		}
+		else if ( is_string($cookiename) && is_string($cookievalue) )
+		{
+			$this->cookies_out[$cookiename] = $cookievalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Sets one or more request header values.
+ 	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
+ 	* @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.
+ 	*/
+	
+	function add_header($headername, $headervalue = false)
+	{
+		if ( is_array($headername) && !$headervalue )
+		{
+			foreach ( $headername as $name => $value )
+			{
+				$this->headers[$name] = $value;
+			}
+		}
+		else if ( is_string($headername) && is_string($headervalue) )
+		{
+			$this->headers[$headername] = $headervalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Adds one or more values to be passed on GET.
+ 	* @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
+ 	* @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.
+ 	*/
+	
+	function add_get($getname, $getvalue = false)
+	{
+		if ( is_array($getname) && !$getvalue )
+		{
+			foreach ( $getname as $name => $value )
+			{
+				$this->parms_get[$name] = $value;
+			}
+		}
+		else if ( is_string($getname) && is_string($getvalue) )
+		{
+			$this->parms_get[$getname] = $getvalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Adds one or more values to be passed on POST.
+ 	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
+ 	* @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.
+ 	*/
+	
+	function add_post($postname, $postvalue = false)
+	{
+		if ( is_array($postname) && !$postvalue )
+		{
+			foreach ( $postname as $name => $value )
+			{
+				$this->parms_post[$name] = $value;
+			}
+		}
+		else if ( is_string($postname) && is_string($postvalue) )
+		{
+			$this->parms_post[$postname] = $postvalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Internal function to open up the socket.
+ 	* @access private
+ 	*/
+	
+	function _sock_open(&$connection)
+	{
+		// Open connection
+		$ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
+		$connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
+		if ( !$connection )
+			throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
+		
+		// 1 = socket open
+		$this->state = 1;
+	}
+	
+	/**
+ 	* Internal function to actually write the request into the socket.
+ 	* @access private
+ 	*/
+	
+	function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
+	{
+		$newline = "\r\n";
+		
+		if ( $this->debug )
+			echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
+		
+		if ( $this->debug )
+		{
+			echo '<hr /><div style="white-space: nowrap;">';
+			echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
+			echo "<p><b>Headers:</b></p><pre>$headers</pre>";
+			echo "<p><b>Cookies:</b> $cookies</p>";
+			echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
+			echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
+			echo "<pre>";
+		}
+		
+		$portline = ( $this->port == 80 ) ? '' : ":$this->port";
+		
+		$this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
+		$this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
+		$this->_fputs($connection, $headers);
+		$this->_fputs($connection, $cookies);
+		
+		if ( $this->method == 'POST' )
+		{
+			// POST-specific parameters
+			$post_length = strlen($post);
+			$this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
+			$this->_fputs($connection, "Content-length: {$post_length}{$newline}");
+		}
+		
+		$this->_fputs($connection, "Connection: close{$newline}");
+		$this->_fputs($connection, "{$newline}");
+		
+		if ( $this->method == 'POST' )
+		{
+			$this->_fputs($connection, $post);
+		}
+		
+		if ( $this->debug )
+			echo '</pre><p>Request written. Fetching response.</p>';
+		
+		// 2 = request written
+		$this->state = 2;
+	}
+	
+	/**
+ 	* Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
+ 	* @access private
+ 	*/
+	
+	function sock_close(&$connection)
+	{
+		if ( $this->debug )
+		{
+			echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
+			echo htmlspecialchars($this->response);
+			echo '</pre></div><hr />';
+		}
+		
+		fclose($connection);
+		$this->state = 0;
+	}
+	
+	/**
+ 	* Internal function to grab the response code and status string
+ 	* @access string
+ 	*/
+	
+	function _parse_response_code($buffer)
+	{
+		// Retrieve response code and status
+		$pos_newline = strpos($buffer, "\n");
+		$pos_carriage_return = strpos($buffer, "\r");
+		$pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
+		
+		// First line is in format of:
+		// HTTP/1.1 ### Blah blah blah(\r?)\n
+		$response_code = substr($buffer, 9, 3);
+		$response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
+		$this->response_code = intval($response_code);
+		$this->response_string = $response_string;
+	}
+	
+	/**
+ 	* Internal function to send the request.
+ 	* @access private
+ 	*/
+	
+	function _send_request()
+	{
+		$this->concat_headers($headers, $cookies, $get, $post);
+		
+		if ( $this->state < 1 )
+		{
+			$this->_sock_open($this->socket);
+		}
+		if ( $this->state < 2 )
+		{
+			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+		}
+		if ( $this->state == 2 )
+		{
+			$buffer = $this->_read_until_newlines($this->socket);
+			$this->state = 3;
+			$this->_parse_response_code($buffer);
+			$this->response = $buffer;
+		}
+		// obey redirects
+		$i = 0;
+		while ( $i < 20 && $this->follow_redirects )
+		{
+			$incoming_headers = $this->get_response_headers_array();
+			if ( !$incoming_headers )
+				break;
+			if ( isset($incoming_headers['Location']) )
+			{
+				// we've been redirected...
+				$new_uri = $this->_resolve_uri($incoming_headers['Location']);
+				if ( !$new_uri )
+				{
+					// ... bad URI, ignore Location header.
+					break;
+				}
+				// change location
+				$this->host = $new_uri['host'];
+				$this->port = $new_uri['port'];
+				$this->uri  = $new_uri['uri'];
+				$get = '';
+				
+				// reset
+				$this->sock_close($this->socket);
+				$this->_sock_open($this->socket);
+				$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+				$buffer = $this->_read_until_newlines($this->socket);
+				$this->state = 3;
+				$this->_parse_response_code($buffer);
+				$this->response = $buffer;
+				$i++;
+			}
+			else
+			{
+				break;
+			}
+		}
+		if ( $i == 20 )
+		{
+			throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
+		}
+		
+		if ( $this->state == 3 )
+		{
+			// Determine transfer encoding
+			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
+			if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
+			{
+				$size = intval($match[1]);
+				if ( $this->debug )
+				{
+					echo "Pulling response using fread(), size $size\n";
+				}
+				$this->response .= fread($this->socket, $size);
+			}
+			else
+			{
+				if ( $this->debug )
+					echo "Pulling response using chunked handler\n";
+					
+				$buffer = '';
+				while ( !feof($this->socket) )
+				{
+					$part = fgets($this->socket, 1024);
+					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+					{
+						$chunklen = hexdec($match[1]);
+						$part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
+						// remove the last newline from $part
+						$part = preg_replace("/\r?\n\$/", "", $part);
+					}
+					$buffer .= $part;
+				}
+				$this->response .= $buffer;
+			}
+		}
+		$this->state = 4;
+		
+		$this->sock_close($this->socket);
+		$this->socket = false;
+	}
+	
+	/**
+ 	* Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
+ 	* @access private
+ 	*/
+	
+	function _send_request_headers_only()
+	{
+		$this->concat_headers($headers, $cookies, $get, $post);
+		
+		if ( $this->state < 1 )
+		{
+			$this->_sock_open($this->socket);
+		}
+		if ( $this->state < 2 )
+		{
+			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+		}
+		if ( $this->state == 2 )
+		{
+			$buffer = $this->_read_until_newlines($this->socket);
+			$this->state = 3;
+			$this->_parse_response_code($buffer);
+			$this->response = $buffer;
+		}
+	}
+	
+	/**
+ 	* Internal function to read from a socket until two consecutive newlines are hit.
+ 	* @access private
+ 	*/
+	
+	function _read_until_newlines($sock)
+	{
+		$prev_char = '';
+		$prev1_char = '';
+		$prev2_char = '';
+		$buf = '';
+		while ( !feof($sock) )
+		{
+			$chr = fread($sock, 1);
+			$buf .= $chr;
+			if ( ( $chr == "\n" && $prev_char == "\n" ) ||
+ 					( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
+			{
+				return $buf;
+			}
+			$prev2_char = $prev1_char;
+			$prev1_char = $prev_char;
+			$prev_char = $chr;
+		}
+		return $buf;
+	}
+	
+	/**
+ 	* Returns the response text. If the request hasn't been sent, it will be sent here.
+ 	* @return string
+ 	*/
+	
+	function get_response()
+	{
+		if ( $this->state == 4 )
+			return $this->response;
+		$this->_send_request();
+		return $this->response;
+	}
+	
+	/**
+ 	* 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.
+ 	* @param string File to write to
+ 	* @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.
+ 	* @param int Maximum file size. Defaults to 0, which means no limit.
+ 	* @return bool True on success, false on failure
+ 	*/
+	
+	function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
+	{
+		if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
+		{
+			return false;
+		}
+		$handle = @fopen($file, 'w');
+		if ( !$handle )
+			return false;
+		$chunklen = intval($chunklen);
+		if ( $chunklen < 1 )
+			return false;
+		if ( $this->state == 4 )
+		{
+			// we already have the response, so cheat
+			$response = $this->get_response_body();
+			fwrite($handle, $response);
+		}
+		else
+		{
+			// read data from the socket, write it immediately, and unset to free memory
+			$headers = $this->get_response_headers();
+			$transferred_bytes = 0;
+			$bandwidth_exceeded = false;
+			// if transfer-encoding is chunked, read using chunk sizes the server specifies
+			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
+			if ( $is_chunked )
+			{
+				$buffer = '';
+				while ( !feof($this->socket) )
+				{
+					$part = fgets($this->socket, ( 1024 * $chunklen ));
+					// Theoretically if the encoding is really chunked then this should always match.
+					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+					{
+						$chunk_length = hexdec($match[1]);
+						$part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
+						// remove the last newline from $part
+						$part = preg_replace("/\r?\n\$/m", "", $part);
+					}
+					
+					$transferred_bytes += strlen($part);
+					if ( $max_file_size && $transferred_bytes > $max_file_size )
+					{
+						// truncate output to $max_file_size bytes
+						$partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
+						$part = substr($part, 0, $partlen);
+						$bandwidth_exceeded = true;
+					}
+					fwrite($handle, $part);
+					if ( $bandwidth_exceeded )
+					{
+						break;
+					}
+				}
+			}
+			else
+			{
+				$first_chunk = fread($this->socket, ( 1024 * $chunklen ));
+				fwrite($handle, $first_chunk);
+				while ( !feof($this->socket) )
+				{
+					$chunk = fread($this->socket, ( 1024 * $chunklen ));
+					
+					$transferred_bytes += strlen($chunk);
+					if ( $max_file_size && $transferred_bytes > $max_file_size )
+					{
+						// truncate output to $max_file_size bytes
+						$partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
+						$chunk = substr($chunk, 0, $partlen);
+						$bandwidth_exceeded = true;
+					}
+					
+					fwrite($handle, $chunk);
+					unset($chunk);
+					
+					if ( $bandwidth_exceeded )
+					{
+						break;
+					}
+				}
+			}
+		}
+		fclose($handle);
+		// close socket and reset state, since we haven't cached the response
+		$this->sock_close($this->socket);
+		$this->state = 0;
+		return ($bandwidth_exceeded) ? false : true;
+	}
+	
+	/**
+ 	* 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.
+ 	* @param string
+ 	* @return array
+ 	*/
+	
+	function _resolve_uri($uri)
+	{
+		// long ass regexp w00t
+		if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
+		{
+			// bad target URI
+			return false;
+		}
+		$hostpart = $match[1];
+		if ( empty($hostpart) )
+		{
+			// use existing host
+			$host = $this->host;
+			$port = $this->port;
+		}
+		else
+		{
+			$host = $match[1];
+			$port = empty($match[2]) ? 80 : intval($match[2]);
+		}
+		// is this an absolute URI, or relative?
+		if ( empty($match[3]) )
+		{
+			// relative
+			$uri = dirname($this->uri) . $match[4];
+		}
+		else
+		{
+			// absolute
+			$uri = '/' . $match[4];
+		}
+		return array(
+				'host' => $host,
+				'port' => $port,
+				'uri'  => $uri
+			);
+	}
+	
+	/**
+ 	* Returns only the response headers.
+ 	* @return string
+ 	*/
+	
+	function get_response_headers()
+	{
+		if ( $this->state == 3 )
+		{
+			return $this->response;
+		}
+		else if ( $this->state == 4 )
+		{
+			$pos_end = strpos($this->response, "\r\n\r\n");
+			if ( empty($pos_end) )
+			{
+				$pos_end = strpos($this->response, "\n\n");
+			}
+			$data = substr($this->response, 0, $pos_end);
+			return $data;
+		}
+		else
+		{
+			$this->_send_request_headers_only();
+			return $this->response;
+		}
+	}
+	
+	/**
+ 	* Returns only the response headers, as an associative array.
+ 	* @return array
+ 	*/
+	
+	function get_response_headers_array()
+	{
+		$data = $this->get_response_headers();
+		preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
+		$headers = array();
+		for ( $i = 0; $i < count($matches[0]); $i++ )
+		{
+			$headers[ $matches[2][$i] ] = $matches[3][$i];
+		}
+		return $headers;
+	}
+	
+	/**
+ 	* Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
+ 	* @return string
+ 	*/
+	
+	function get_response_body()
+	{
+		$data = $this->get_response();
+		$pos_start = strpos($data, "\r\n\r\n") + 4;
+		if ( $pos_start == 4 )
+		{
+			$pos_start = strpos($data, "\n\n") + 4;
+		}
+		$data = substr($data, $pos_start);
+		return $data;
+	}
+	
+	/**
+ 	* 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.
+ 	* @return array
+ 	*/
+	
+	function get_cookies()
+	{
+		$data = $this->get_response();
+		$data = str_replace("\r\n", "\n", $data);
+		$pos = strpos($data, "\n\n");
+		$headers = substr($data, 0, $pos);
+		preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
+		if ( count($cookiematch[0]) < 1 )
+			return array();
+		$cookies = array();
+		foreach ( $cookiematch[0] as $i => $cookie )
+		{
+			$cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
+		}
+		return $cookies;
+	}
+	
+	/**
+ 	* Internal method to write data to a socket with debugging possibility.
+ 	* @access private
+ 	*/
+	
+	function _fputs($socket, $data)
+	{
+		if ( $this->debug )
+			echo htmlspecialchars($data);
+		
+		return fputs($socket, $data);
+	}
+	
+	/**
+ 	* Internal function to stringify cookies, headers, get, and post.
+ 	* @access private
+ 	*/
+	
+	function concat_headers(&$headers, &$cookies, &$get, &$post)
+	{
+		$headers = '';
+		$cookies = '';
+		foreach ( $this->headers as $name => $value )
+		{
+			$value = str_replace('\\n', '\\\\n', $value);
+			$value = str_replace("\n", '\\n', $value);
+			$headers .= "$name: $value\r\n";
+		}
+		unset($value);
+		if ( count($this->cookies_out) > 0 )
+		{
+			$i = 0;
+			$cookie_header = 'Cookie: ';
+			foreach ( $this->cookies_out as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$cookie_header .= '; ';
+				$value = str_replace(';', rawurlencode(';'), $value);
+				$value = str_replace('\\n', '\\\\n', $value);
+				$value = str_replace("\n", '\\n', $value);
+				$cookie_header .= "$name=$value";
+			}
+			$cookie_header .= "\r\n";
+			$cookies = $cookie_header;
+			unset($value, $cookie_header);
+		}
+		if ( count($this->parms_get) > 0 )
+		{
+			$get = '?';
+			$i = 0;
+			foreach ( $this->parms_get as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$get .= '&';
+				$value = urlencode($value);
+				if ( !empty($value) || is_string($value) )
+					$get .= "$name=$value";
+				else
+					$get .= "$name";
+			}
+		}
+		if ( count($this->parms_post) > 0 )
+		{
+			$post = '';
+			$i = 0;
+			foreach ( $this->parms_post as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$post .= '&';
+				$value = urlencode($value);
+				$post .= "$name=$value";
+			}
+		}
+	}
+	
 }