Index: branches/version-2_13-dev/data/module/HTTP/Request.php
===================================================================
--- branches/version-2_13-dev/data/module/HTTP/Request.php	(revision 23125)
+++ branches/version-2_13-dev/data/module/HTTP/Request.php	(revision 23141)
@@ -1,2 +1,3 @@
+<<<<<<< .working
 <?php
 /**
@@ -41,5 +42,5 @@
  * @copyright   2002-2007 Richard Heyes
  * @license     http://opensource.org/licenses/bsd-license.php New BSD License
- * @version     CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
+ * @version     CVS: $Id$
  * @link        http://pear.php.net/package/HTTP_Request/
  */
@@ -1520,2 +1521,1532 @@
 } // End class HTTP_Response
 ?>
+=======
+<?php
+/**
+ * Class for performing HTTP requests
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category    HTTP
+ * @package     HTTP_Request
+ * @author      Richard Heyes <richard@phpguru.org>
+ * @author      Alexey Borzov <avb@php.net>
+ * @copyright   2002-2007 Richard Heyes
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id$
+ * @link        http://pear.php.net/package/HTTP_Request/
+ */
+
+/**
+ * PEAR and PEAR_Error classes (for error handling)
+ */
+require_once 'PEAR.php';
+/**
+ * Socket class
+ */
+require_once 'Net/Socket.php';
+/**
+ * URL handling class
+ */
+require_once 'Net/URL.php';
+
+/**#@+
+ * Constants for HTTP request methods
+ */
+define('HTTP_REQUEST_METHOD_GET',     'GET',     true);
+define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true);
+define('HTTP_REQUEST_METHOD_POST',    'POST',    true);
+define('HTTP_REQUEST_METHOD_PUT',     'PUT',     true);
+define('HTTP_REQUEST_METHOD_DELETE',  'DELETE',  true);
+define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
+define('HTTP_REQUEST_METHOD_TRACE',   'TRACE',   true);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP request error codes
+ */
+define('HTTP_REQUEST_ERROR_FILE',             1);
+define('HTTP_REQUEST_ERROR_URL',              2);
+define('HTTP_REQUEST_ERROR_PROXY',            4);
+define('HTTP_REQUEST_ERROR_REDIRECTS',        8);
+define('HTTP_REQUEST_ERROR_RESPONSE',        16);
+define('HTTP_REQUEST_ERROR_GZIP_METHOD',     32);
+define('HTTP_REQUEST_ERROR_GZIP_READ',       64);
+define('HTTP_REQUEST_ERROR_GZIP_DATA',      128);
+define('HTTP_REQUEST_ERROR_GZIP_CRC',       256);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP protocol versions
+ */
+define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
+define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
+/**#@-*/
+
+if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
+   /**
+    * Whether string functions are overloaded by their mbstring equivalents
+    */
+    define('HTTP_REQUEST_MBSTRING', true);
+} else {
+   /**
+    * @ignore
+    */
+    define('HTTP_REQUEST_MBSTRING', false);
+}
+
+/**
+ * Class for performing HTTP requests
+ *
+ * Simple example (fetches yahoo.com and displays it):
+ * <code>
+ * $a = &new HTTP_Request('http://www.yahoo.com/');
+ * $a->sendRequest();
+ * echo $a->getResponseBody();
+ * </code>
+ *
+ * @category    HTTP
+ * @package     HTTP_Request
+ * @author      Richard Heyes <richard@phpguru.org>
+ * @author      Alexey Borzov <avb@php.net>
+ * @version     Release: 1.4.4
+ */
+class HTTP_Request
+{
+   /**#@+
+    * @access private
+    */
+    /**
+    * Instance of Net_URL
+    * @var Net_URL
+    */
+    var $_url;
+
+    /**
+    * Type of request
+    * @var string
+    */
+    var $_method;
+
+    /**
+    * HTTP Version
+    * @var string
+    */
+    var $_http;
+
+    /**
+    * Request headers
+    * @var array
+    */
+    var $_requestHeaders;
+
+    /**
+    * Basic Auth Username
+    * @var string
+    */
+    var $_user;
+
+    /**
+    * Basic Auth Password
+    * @var string
+    */
+    var $_pass;
+
+    /**
+    * Socket object
+    * @var Net_Socket
+    */
+    var $_sock;
+
+    /**
+    * Proxy server
+    * @var string
+    */
+    var $_proxy_host;
+
+    /**
+    * Proxy port
+    * @var integer
+    */
+    var $_proxy_port;
+
+    /**
+    * Proxy username
+    * @var string
+    */
+    var $_proxy_user;
+
+    /**
+    * Proxy password
+    * @var string
+    */
+    var $_proxy_pass;
+
+    /**
+    * Post data
+    * @var array
+    */
+    var $_postData;
+
+   /**
+    * Request body
+    * @var string
+    */
+    var $_body;
+
+   /**
+    * A list of methods that MUST NOT have a request body, per RFC 2616
+    * @var array
+    */
+    var $_bodyDisallowed = array('TRACE');
+
+   /**
+    * Methods having defined semantics for request body
+    *
+    * Content-Length header (indicating that the body follows, section 4.3 of
+    * RFC 2616) will be sent for these methods even if no body was added
+    *
+    * @var array
+    */
+    var $_bodyRequired = array('POST', 'PUT');
+
+   /**
+    * Files to post
+    * @var array
+    */
+    var $_postFiles = array();
+
+    /**
+    * Connection timeout.
+    * @var float
+    */
+    var $_timeout;
+
+    /**
+    * HTTP_Response object
+    * @var HTTP_Response
+    */
+    var $_response;
+
+    /**
+    * Whether to allow redirects
+    * @var boolean
+    */
+    var $_allowRedirects;
+
+    /**
+    * Maximum redirects allowed
+    * @var integer
+    */
+    var $_maxRedirects;
+
+    /**
+    * Current number of redirects
+    * @var integer
+    */
+    var $_redirects;
+
+   /**
+    * Whether to append brackets [] to array variables
+    * @var bool
+    */
+    var $_useBrackets = true;
+
+   /**
+    * Attached listeners
+    * @var array
+    */
+    var $_listeners = array();
+
+   /**
+    * Whether to save response body in response object property
+    * @var bool
+    */
+    var $_saveBody = true;
+
+   /**
+    * Timeout for reading from socket (array(seconds, microseconds))
+    * @var array
+    */
+    var $_readTimeout = null;
+
+   /**
+    * Options to pass to Net_Socket::connect. See stream_context_create
+    * @var array
+    */
+    var $_socketOptions = null;
+   /**#@-*/
+
+    /**
+    * Constructor
+    *
+    * Sets up the object
+    * @param    string  The url to fetch/access
+    * @param    array   Associative array of parameters which can have the following keys:
+    * <ul>
+    *   <li>method         - Method to use, GET, POST etc (string)</li>
+    *   <li>http           - HTTP Version to use, 1.0 or 1.1 (string)</li>
+    *   <li>user           - Basic Auth username (string)</li>
+    *   <li>pass           - Basic Auth password (string)</li>
+    *   <li>proxy_host     - Proxy server host (string)</li>
+    *   <li>proxy_port     - Proxy server port (integer)</li>
+    *   <li>proxy_user     - Proxy auth username (string)</li>
+    *   <li>proxy_pass     - Proxy auth password (string)</li>
+    *   <li>timeout        - Connection timeout in seconds (float)</li>
+    *   <li>allowRedirects - Whether to follow redirects or not (bool)</li>
+    *   <li>maxRedirects   - Max number of redirects to follow (integer)</li>
+    *   <li>useBrackets    - Whether to append [] to array variable names (bool)</li>
+    *   <li>saveBody       - Whether to save response body in response object property (bool)</li>
+    *   <li>readTimeout    - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
+    *   <li>socketOptions  - Options to pass to Net_Socket object (array)</li>
+    * </ul>
+    * @access public
+    */
+    function HTTP_Request($url = '', $params = array())
+    {
+        $this->_method         =  HTTP_REQUEST_METHOD_GET;
+        $this->_http           =  HTTP_REQUEST_HTTP_VER_1_1;
+        $this->_requestHeaders = array();
+        $this->_postData       = array();
+        $this->_body           = null;
+
+        $this->_user = null;
+        $this->_pass = null;
+
+        $this->_proxy_host = null;
+        $this->_proxy_port = null;
+        $this->_proxy_user = null;
+        $this->_proxy_pass = null;
+
+        $this->_allowRedirects = false;
+        $this->_maxRedirects   = 3;
+        $this->_redirects      = 0;
+
+        $this->_timeout  = null;
+        $this->_response = null;
+
+        foreach ($params as $key => $value) {
+            $this->{'_' . $key} = $value;
+        }
+
+        if (!empty($url)) {
+            $this->setURL($url);
+        }
+
+        // Default useragent
+        $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
+
+        // We don't do keep-alives by default
+        $this->addHeader('Connection', 'close');
+
+        // Basic authentication
+        if (!empty($this->_user)) {
+            $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
+        }
+
+        // Proxy authentication (see bug #5913)
+        if (!empty($this->_proxy_user)) {
+            $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
+        }
+
+        // Use gzip encoding if possible
+        if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
+            $this->addHeader('Accept-Encoding', 'gzip');
+        }
+    }
+
+    /**
+    * Generates a Host header for HTTP/1.1 requests
+    *
+    * @access private
+    * @return string
+    */
+    function _generateHostHeader()
+    {
+        if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
+            $host = $this->_url->host . ':' . $this->_url->port;
+
+        } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
+            $host = $this->_url->host . ':' . $this->_url->port;
+
+        } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
+            $host = $this->_url->host . ':' . $this->_url->port;
+
+        } else {
+            $host = $this->_url->host;
+        }
+
+        return $host;
+    }
+
+    /**
+    * Resets the object to its initial state (DEPRECATED).
+    * Takes the same parameters as the constructor.
+    *
+    * @param  string $url    The url to be requested
+    * @param  array  $params Associative array of parameters
+    *                        (see constructor for details)
+    * @access public
+    * @deprecated deprecated since 1.2, call the constructor if this is necessary
+    */
+    function reset($url, $params = array())
+    {
+        $this->HTTP_Request($url, $params);
+    }
+
+    /**
+    * Sets the URL to be requested
+    *
+    * @param  string The url to be requested
+    * @access public
+    */
+    function setURL($url)
+    {
+        $this->_url = &new Net_URL($url, $this->_useBrackets);
+
+        if (!empty($this->_url->user) || !empty($this->_url->pass)) {
+            $this->setBasicAuth($this->_url->user, $this->_url->pass);
+        }
+
+        if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
+            $this->addHeader('Host', $this->_generateHostHeader());
+        }
+
+        // set '/' instead of empty path rather than check later (see bug #8662)
+        if (empty($this->_url->path)) {
+            $this->_url->path = '/';
+        }
+    }
+
+   /**
+    * Returns the current request URL
+    *
+    * @return   string  Current request URL
+    * @access   public
+    */
+    function getUrl()
+    {
+        return empty($this->_url)? '': $this->_url->getUrl();
+    }
+
+    /**
+    * Sets a proxy to be used
+    *
+    * @param string     Proxy host
+    * @param int        Proxy port
+    * @param string     Proxy username
+    * @param string     Proxy password
+    * @access public
+    */
+    function setProxy($host, $port = 8080, $user = null, $pass = null)
+    {
+        $this->_proxy_host = $host;
+        $this->_proxy_port = $port;
+        $this->_proxy_user = $user;
+        $this->_proxy_pass = $pass;
+
+        if (!empty($user)) {
+            $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+        }
+    }
+
+    /**
+    * Sets basic authentication parameters
+    *
+    * @param string     Username
+    * @param string     Password
+    */
+    function setBasicAuth($user, $pass)
+    {
+        $this->_user = $user;
+        $this->_pass = $pass;
+
+        $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+    }
+
+    /**
+    * Sets the method to be used, GET, POST etc.
+    *
+    * @param string     Method to use. Use the defined constants for this
+    * @access public
+    */
+    function setMethod($method)
+    {
+        $this->_method = $method;
+    }
+
+    /**
+    * Sets the HTTP version to use, 1.0 or 1.1
+    *
+    * @param string     Version to use. Use the defined constants for this
+    * @access public
+    */
+    function setHttpVer($http)
+    {
+        $this->_http = $http;
+    }
+
+    /**
+    * Adds a request header
+    *
+    * @param string     Header name
+    * @param string     Header value
+    * @access public
+    */
+    function addHeader($name, $value)
+    {
+        $this->_requestHeaders[strtolower($name)] = $value;
+    }
+
+    /**
+    * Removes a request header
+    *
+    * @param string     Header name to remove
+    * @access public
+    */
+    function removeHeader($name)
+    {
+        if (isset($this->_requestHeaders[strtolower($name)])) {
+            unset($this->_requestHeaders[strtolower($name)]);
+        }
+    }
+
+    /**
+    * Adds a querystring parameter
+    *
+    * @param string     Querystring parameter name
+    * @param string     Querystring parameter value
+    * @param bool       Whether the value is already urlencoded or not, default = not
+    * @access public
+    */
+    function addQueryString($name, $value, $preencoded = false)
+    {
+        $this->_url->addQueryString($name, $value, $preencoded);
+    }
+
+    /**
+    * Sets the querystring to literally what you supply
+    *
+    * @param string     The querystring data. Should be of the format foo=bar&x=y etc
+    * @param bool       Whether data is already urlencoded or not, default = already encoded
+    * @access public
+    */
+    function addRawQueryString($querystring, $preencoded = true)
+    {
+        $this->_url->addRawQueryString($querystring, $preencoded);
+    }
+
+    /**
+    * Adds postdata items
+    *
+    * @param string     Post data name
+    * @param string     Post data value
+    * @param bool       Whether data is already urlencoded or not, default = not
+    * @access public
+    */
+    function addPostData($name, $value, $preencoded = false)
+    {
+        if ($preencoded) {
+            $this->_postData[$name] = $value;
+        } else {
+            $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
+        }
+    }
+	
+    function addPostDataArray($array, $preencoded = false)
+    {
+		foreach($array as $key => $val){
+			$this->addPostData($key, $val, $preencoded);
+		}
+    }	
+
+   /**
+    * Recursively applies the callback function to the value
+    *
+    * @param    mixed   Callback function
+    * @param    mixed   Value to process
+    * @access   private
+    * @return   mixed   Processed value
+    */
+    function _arrayMapRecursive($callback, $value)
+    {
+        if (!is_array($value)) {
+            return call_user_func($callback, $value);
+        } else {
+            $map = array();
+            foreach ($value as $k => $v) {
+                $map[$k] = $this->_arrayMapRecursive($callback, $v);
+            }
+            return $map;
+        }
+    }
+
+   /**
+    * Adds a file to form-based file upload
+    *
+    * Used to emulate file upload via a HTML form. The method also sets
+    * Content-Type of HTTP request to 'multipart/form-data'.
+    *
+    * If you just want to send the contents of a file as the body of HTTP
+    * request you should use setBody() method.
+    *
+    * @access public
+    * @param  string    name of file-upload field
+    * @param  mixed     file name(s)
+    * @param  mixed     content-type(s) of file(s) being uploaded
+    * @return bool      true on success
+    * @throws PEAR_Error
+    */
+    function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
+    {
+        if (!is_array($fileName) && !is_readable($fileName)) {
+            return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
+        } elseif (is_array($fileName)) {
+            foreach ($fileName as $name) {
+                if (!is_readable($name)) {
+                    return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
+                }
+            }
+        }
+        $this->addHeader('Content-Type', 'multipart/form-data');
+        $this->_postFiles[$inputName] = array(
+            'name' => $fileName,
+            'type' => $contentType
+        );
+        return true;
+    }
+
+    /**
+    * Adds raw postdata (DEPRECATED)
+    *
+    * @param string     The data
+    * @param bool       Whether data is preencoded or not, default = already encoded
+    * @access public
+    * @deprecated       deprecated since 1.3.0, method setBody() should be used instead
+    */
+    function addRawPostData($postdata, $preencoded = true)
+    {
+        $this->_body = $preencoded ? $postdata : urlencode($postdata);
+    }
+
+   /**
+    * Sets the request body (for POST, PUT and similar requests)
+    *
+    * @param    string  Request body
+    * @access   public
+    */
+    function setBody($body)
+    {
+        $this->_body = $body;
+    }
+
+    /**
+    * Clears any postdata that has been added (DEPRECATED).
+    *
+    * Useful for multiple request scenarios.
+    *
+    * @access public
+    * @deprecated deprecated since 1.2
+    */
+    function clearPostData()
+    {
+        $this->_postData = null;
+    }
+
+    /**
+    * Appends a cookie to "Cookie:" header
+    *
+    * @param string $name cookie name
+    * @param string $value cookie value
+    * @access public
+    */
+    function addCookie($name, $value)
+    {
+        $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
+        $this->addHeader('Cookie', $cookies . $name . '=' . $value);
+    }
+
+    /**
+    * Clears any cookies that have been added (DEPRECATED).
+    *
+    * Useful for multiple request scenarios
+    *
+    * @access public
+    * @deprecated deprecated since 1.2
+    */
+    function clearCookies()
+    {
+        $this->removeHeader('Cookie');
+    }
+
+    /**
+    * Sends the request
+    *
+    * @access public
+    * @param  bool   Whether to store response body in Response object property,
+    *                set this to false if downloading a LARGE file and using a Listener
+    * @return mixed  PEAR error on error, true otherwise
+    */
+    function sendRequest($saveBody = true)
+    {
+        if (!is_a($this->_url, 'Net_URL')) {
+            return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
+        }
+
+        $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
+        $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
+
+        if (strcasecmp($this->_url->protocol, 'https') == 0) {
+            // Bug #14127, don't try connecting to HTTPS sites without OpenSSL
+            if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) {
+                return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
+                                        HTTP_REQUEST_ERROR_URL);
+            } elseif (isset($this->_proxy_host)) {
+                return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
+            }
+            $host = 'ssl://' . $host;
+        }
+
+        // magic quotes may fuck up file uploads and chunked response processing
+        $magicQuotes = ini_get('magic_quotes_runtime');
+        ini_set('magic_quotes_runtime', false);
+
+        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
+        // connection token to a proxy server...
+        if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
+            'Keep-Alive' == $this->_requestHeaders['connection'])
+        {
+            $this->removeHeader('connection');
+        }
+
+        $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
+                     (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
+        $sockets   = &PEAR::getStaticProperty('HTTP_Request', 'sockets');
+        $sockKey   = $host . ':' . $port;
+        unset($this->_sock);
+
+        // There is a connected socket in the "static" property?
+        if ($keepAlive && !empty($sockets[$sockKey]) &&
+            !empty($sockets[$sockKey]->fp))
+        {
+            $this->_sock =& $sockets[$sockKey];
+            $err = null;
+        } else {
+            $this->_notify('connect');
+            $this->_sock =& new Net_Socket();
+            $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
+        }
+        PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
+
+        if (!PEAR::isError($err)) {
+            if (!empty($this->_readTimeout)) {
+                $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
+            }
+
+            $this->_notify('sentRequest');
+
+            // Read the response
+            $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
+            $err = $this->_response->process(
+                $this->_saveBody && $saveBody,
+                HTTP_REQUEST_METHOD_HEAD != $this->_method
+            );
+
+            if ($keepAlive) {
+                $keepAlive = (isset($this->_response->_headers['content-length'])
+                              || (isset($this->_response->_headers['transfer-encoding'])
+                                  && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
+                if ($keepAlive) {
+                    if (isset($this->_response->_headers['connection'])) {
+                        $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
+                    } else {
+                        $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
+                    }
+                }
+            }
+        }
+
+        ini_set('magic_quotes_runtime', $magicQuotes);
+
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        if (!$keepAlive) {
+            $this->disconnect();
+        // Store the connected socket in "static" property
+        } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
+            $sockets[$sockKey] =& $this->_sock;
+        }
+
+        // Check for redirection
+        if (    $this->_allowRedirects
+            AND $this->_redirects <= $this->_maxRedirects
+            AND $this->getResponseCode() > 300
+            AND $this->getResponseCode() < 399
+            AND !empty($this->_response->_headers['location'])) {
+
+
+            $redirect = $this->_response->_headers['location'];
+
+            // Absolute URL
+            if (preg_match('/^https?:\/\//i', $redirect)) {
+                $this->_url = &new Net_URL($redirect);
+                $this->addHeader('Host', $this->_generateHostHeader());
+            // Absolute path
+            } elseif ($redirect{0} == '/') {
+                $this->_url->path = $redirect;
+
+            // Relative path
+            } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
+                if (substr($this->_url->path, -1) == '/') {
+                    $redirect = $this->_url->path . $redirect;
+                } else {
+                    $redirect = dirname($this->_url->path) . '/' . $redirect;
+                }
+                $redirect = Net_URL::resolvePath($redirect);
+                $this->_url->path = $redirect;
+
+            // Filename, no path
+            } else {
+                if (substr($this->_url->path, -1) == '/') {
+                    $redirect = $this->_url->path . $redirect;
+                } else {
+                    $redirect = dirname($this->_url->path) . '/' . $redirect;
+                }
+                $this->_url->path = $redirect;
+            }
+
+            $this->_redirects++;
+            return $this->sendRequest($saveBody);
+
+        // Too many redirects
+        } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
+            return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
+        }
+
+        return true;
+    }
+
+    /**
+     * Disconnect the socket, if connected. Only useful if using Keep-Alive.
+     *
+     * @access public
+     */
+    function disconnect()
+    {
+        if (!empty($this->_sock) && !empty($this->_sock->fp)) {
+            $this->_notify('disconnect');
+            $this->_sock->disconnect();
+        }
+    }
+
+    /**
+    * Returns the response code
+    *
+    * @access public
+    * @return mixed     Response code, false if not set
+    */
+    function getResponseCode()
+    {
+        return isset($this->_response->_code) ? $this->_response->_code : false;
+    }
+
+    /**
+    * Returns the response reason phrase
+    *
+    * @access public
+    * @return mixed     Response reason phrase, false if not set
+    */
+    function getResponseReason()
+    {
+        return isset($this->_response->_reason) ? $this->_response->_reason : false;
+    }
+
+    /**
+    * Returns either the named header or all if no name given
+    *
+    * @access public
+    * @param string     The header name to return, do not set to get all headers
+    * @return mixed     either the value of $headername (false if header is not present)
+    *                   or an array of all headers
+    */
+    function getResponseHeader($headername = null)
+    {
+        if (!isset($headername)) {
+            return isset($this->_response->_headers)? $this->_response->_headers: array();
+        } else {
+            $headername = strtolower($headername);
+            return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
+        }
+    }
+
+    /**
+    * Returns the body of the response
+    *
+    * @access public
+    * @return mixed     response body, false if not set
+    */
+    function getResponseBody()
+    {
+        return isset($this->_response->_body) ? $this->_response->_body : false;
+    }
+
+    /**
+    * Returns cookies set in response
+    *
+    * @access public
+    * @return mixed     array of response cookies, false if none are present
+    */
+    function getResponseCookies()
+    {
+        return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
+    }
+
+    /**
+    * Builds the request string
+    *
+    * @access private
+    * @return string The request string
+    */
+    function _buildRequest()
+    {
+        $separator = ini_get('arg_separator.output');
+        ini_set('arg_separator.output', '&');
+        $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
+        ini_set('arg_separator.output', $separator);
+
+        $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
+        $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
+        $path = $this->_url->path . $querystring;
+        $url  = $host . $port . $path;
+
+        if (!strlen($url)) {
+            $url = '/';
+        }
+
+        $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
+
+        if (in_array($this->_method, $this->_bodyDisallowed) ||
+            (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
+             (empty($this->_postData) && empty($this->_postFiles)))))
+        {
+            $this->removeHeader('Content-Type');
+        } else {
+            if (empty($this->_requestHeaders['content-type'])) {
+                // Add default content-type
+                $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
+            } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
+                $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
+                $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
+            }
+        }
+
+        // Request Headers
+        if (!empty($this->_requestHeaders)) {
+            foreach ($this->_requestHeaders as $name => $value) {
+                $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+                $request      .= $canonicalName . ': ' . $value . "\r\n";
+            }
+        }
+
+        // Method does not allow a body, simply add a final CRLF
+        if (in_array($this->_method, $this->_bodyDisallowed)) {
+
+            $request .= "\r\n";
+
+        // Post data if it's an array
+        } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
+                  (!empty($this->_postData) || !empty($this->_postFiles))) {
+
+            // "normal" POST request
+            if (!isset($boundary)) {
+                $postdata = implode('&', array_map(
+                    create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
+                    $this->_flattenArray('', $this->_postData)
+                ));
+
+            // multipart request, probably with file uploads
+            } else {
+                $postdata = '';
+                if (!empty($this->_postData)) {
+                    $flatData = $this->_flattenArray('', $this->_postData);
+                    foreach ($flatData as $item) {
+                        $postdata .= '--' . $boundary . "\r\n";
+                        $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
+                        $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
+                    }
+                }
+                foreach ($this->_postFiles as $name => $value) {
+                    if (is_array($value['name'])) {
+                        $varname       = $name . ($this->_useBrackets? '[]': '');
+                    } else {
+                        $varname       = $name;
+                        $value['name'] = array($value['name']);
+                    }
+                    foreach ($value['name'] as $key => $filename) {
+                        $fp       = fopen($filename, 'r');
+                        $basename = basename($filename);
+                        $type     = is_array($value['type'])? @$value['type'][$key]: $value['type'];
+
+                        $postdata .= '--' . $boundary . "\r\n";
+                        $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
+                        $postdata .= "\r\nContent-Type: " . $type;
+                        $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n";
+                        fclose($fp);
+                    }
+                }
+                $postdata .= '--' . $boundary . "--\r\n";
+            }
+            $request .= 'Content-Length: ' .
+                        (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
+                        "\r\n\r\n";
+            $request .= $postdata;
+
+        // Explicitly set request body
+        } elseif (0 < strlen($this->_body)) {
+
+            $request .= 'Content-Length: ' .
+                        (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
+                        "\r\n\r\n";
+            $request .= $this->_body;
+
+        // No body: send a Content-Length header nonetheless (request #12900),
+        // but do that only for methods that require a body (bug #14740)
+        } else {
+
+            if (in_array($this->_method, $this->_bodyRequired)) {
+                $request .= "Content-Length: 0\r\n";
+            }
+            $request .= "\r\n";
+        }
+
+        return $request;
+    }
+
+   /**
+    * Helper function to change the (probably multidimensional) associative array
+    * into the simple one.
+    *
+    * @param    string  name for item
+    * @param    mixed   item's values
+    * @return   array   array with the following items: array('item name', 'item value');
+    * @access   private
+    */
+    function _flattenArray($name, $values)
+    {
+        if (!is_array($values)) {
+            return array(array($name, $values));
+        } else {
+            $ret = array();
+            foreach ($values as $k => $v) {
+                if (empty($name)) {
+                    $newName = $k;
+                } elseif ($this->_useBrackets) {
+                    $newName = $name . '[' . $k . ']';
+                } else {
+                    $newName = $name;
+                }
+                $ret = array_merge($ret, $this->_flattenArray($newName, $v));
+            }
+            return $ret;
+        }
+    }
+
+
+   /**
+    * Adds a Listener to the list of listeners that are notified of
+    * the object's events
+    *
+    * Events sent by HTTP_Request object
+    * - 'connect': on connection to server
+    * - 'sentRequest': after the request was sent
+    * - 'disconnect': on disconnection from server
+    *
+    * Events sent by HTTP_Response object
+    * - 'gotHeaders': after receiving response headers (headers are passed in $data)
+    * - 'tick': on receiving a part of response body (the part is passed in $data)
+    * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
+    * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
+    *
+    * @param    HTTP_Request_Listener   listener to attach
+    * @return   boolean                 whether the listener was successfully attached
+    * @access   public
+    */
+    function attach(&$listener)
+    {
+        if (!is_a($listener, 'HTTP_Request_Listener')) {
+            return false;
+        }
+        $this->_listeners[$listener->getId()] =& $listener;
+        return true;
+    }
+
+
+   /**
+    * Removes a Listener from the list of listeners
+    *
+    * @param    HTTP_Request_Listener   listener to detach
+    * @return   boolean                 whether the listener was successfully detached
+    * @access   public
+    */
+    function detach(&$listener)
+    {
+        if (!is_a($listener, 'HTTP_Request_Listener') ||
+            !isset($this->_listeners[$listener->getId()])) {
+            return false;
+        }
+        unset($this->_listeners[$listener->getId()]);
+        return true;
+    }
+
+
+   /**
+    * Notifies all registered listeners of an event.
+    *
+    * @param    string  Event name
+    * @param    mixed   Additional data
+    * @access   private
+    * @see      HTTP_Request::attach()
+    */
+    function _notify($event, $data = null)
+    {
+        foreach (array_keys($this->_listeners) as $id) {
+            $this->_listeners[$id]->update($this, $event, $data);
+        }
+    }
+}
+
+
+/**
+ * Response class to complement the Request class
+ *
+ * @category    HTTP
+ * @package     HTTP_Request
+ * @author      Richard Heyes <richard@phpguru.org>
+ * @author      Alexey Borzov <avb@php.net>
+ * @version     Release: 1.4.4
+ */
+class HTTP_Response
+{
+    /**
+    * Socket object
+    * @var Net_Socket
+    */
+    var $_sock;
+
+    /**
+    * Protocol
+    * @var string
+    */
+    var $_protocol;
+
+    /**
+    * Return code
+    * @var string
+    */
+    var $_code;
+
+    /**
+    * Response reason phrase
+    * @var string
+    */
+    var $_reason;
+
+    /**
+    * Response headers
+    * @var array
+    */
+    var $_headers;
+
+    /**
+    * Cookies set in response
+    * @var array
+    */
+    var $_cookies;
+
+    /**
+    * Response body
+    * @var string
+    */
+    var $_body = '';
+
+   /**
+    * Used by _readChunked(): remaining length of the current chunk
+    * @var string
+    */
+    var $_chunkLength = 0;
+
+   /**
+    * Attached listeners
+    * @var array
+    */
+    var $_listeners = array();
+
+   /**
+    * Bytes left to read from message-body
+    * @var null|int
+    */
+    var $_toRead;
+
+    /**
+    * Constructor
+    *
+    * @param  Net_Socket    socket to read the response from
+    * @param  array         listeners attached to request
+    */
+    function HTTP_Response(&$sock, &$listeners)
+    {
+        $this->_sock      =& $sock;
+        $this->_listeners =& $listeners;
+    }
+
+
+   /**
+    * Processes a HTTP response
+    *
+    * This extracts response code, headers, cookies and decodes body if it
+    * was encoded in some way
+    *
+    * @access public
+    * @param  bool      Whether to store response body in object property, set
+    *                   this to false if downloading a LARGE file and using a Listener.
+    *                   This is assumed to be true if body is gzip-encoded.
+    * @param  bool      Whether the response can actually have a message-body.
+    *                   Will be set to false for HEAD requests.
+    * @throws PEAR_Error
+    * @return mixed     true on success, PEAR_Error in case of malformed response
+    */
+    function process($saveBody = true, $canHaveBody = true)
+    {
+        do {
+            $line = $this->_sock->readLine();
+            if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) {
+                return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
+            } else {
+                $this->_protocol = $s[1];
+                $this->_code     = intval($s[2]);
+                $this->_reason   = empty($s[3])? null: $s[3];
+            }
+            while ('' !== ($header = $this->_sock->readLine())) {
+                $this->_processHeader($header);
+            }
+        } while (100 == $this->_code);
+
+        $this->_notify('gotHeaders', $this->_headers);
+
+        // RFC 2616, section 4.4:
+        // 1. Any response message which "MUST NOT" include a message-body ...
+        // is always terminated by the first empty line after the header fields
+        // 3. ... If a message is received with both a
+        // Transfer-Encoding header field and a Content-Length header field,
+        // the latter MUST be ignored.
+        $canHaveBody = $canHaveBody && $this->_code >= 200 &&
+                       $this->_code != 204 && $this->_code != 304;
+
+        // If response body is present, read it and decode
+        $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
+        $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
+        $hasBody = false;
+        if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
+                0 != $this->_headers['content-length']))
+        {
+            if ($chunked || !isset($this->_headers['content-length'])) {
+                $this->_toRead = null;
+            } else {
+                $this->_toRead = $this->_headers['content-length'];
+            }
+            while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
+                if ($chunked) {
+                    $data = $this->_readChunked();
+                } elseif (is_null($this->_toRead)) {
+                    $data = $this->_sock->read(4096);
+                } else {
+                    $data = $this->_sock->read(min(4096, $this->_toRead));
+                    $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+                }
+                if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
+                    break;
+                } else {
+                    $hasBody = true;
+                    if ($saveBody || $gzipped) {
+                        $this->_body .= $data;
+                    }
+                    $this->_notify($gzipped? 'gzTick': 'tick', $data);
+                }
+            }
+        }
+
+        if ($hasBody) {
+            // Uncompress the body if needed
+            if ($gzipped) {
+                $body = $this->_decodeGzip($this->_body);
+                if (PEAR::isError($body)) {
+                    return $body;
+                }
+                $this->_body = $body;
+                $this->_notify('gotBody', $this->_body);
+            } else {
+                $this->_notify('gotBody');
+            }
+        }
+        return true;
+    }
+
+
+   /**
+    * Processes the response header
+    *
+    * @access private
+    * @param  string    HTTP header
+    */
+    function _processHeader($header)
+    {
+        if (false === strpos($header, ':')) {
+            return;
+        }
+        list($headername, $headervalue) = explode(':', $header, 2);
+        $headername  = strtolower($headername);
+        $headervalue = ltrim($headervalue);
+
+        if ('set-cookie' != $headername) {
+            if (isset($this->_headers[$headername])) {
+                $this->_headers[$headername] .= ',' . $headervalue;
+            } else {
+                $this->_headers[$headername]  = $headervalue;
+            }
+        } else {
+            $this->_parseCookie($headervalue);
+        }
+    }
+
+
+   /**
+    * Parse a Set-Cookie header to fill $_cookies array
+    *
+    * @access private
+    * @param  string    value of Set-Cookie header
+    */
+    function _parseCookie($headervalue)
+    {
+        $cookie = array(
+            'expires' => null,
+            'domain'  => null,
+            'path'    => null,
+            'secure'  => false
+        );
+
+        // Only a name=value pair
+        if (!strpos($headervalue, ';')) {
+            $pos = strpos($headervalue, '=');
+            $cookie['name']  = trim(substr($headervalue, 0, $pos));
+            $cookie['value'] = trim(substr($headervalue, $pos + 1));
+
+        // Some optional parameters are supplied
+        } else {
+            $elements = explode(';', $headervalue);
+            $pos = strpos($elements[0], '=');
+            $cookie['name']  = trim(substr($elements[0], 0, $pos));
+            $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+            for ($i = 1; $i < count($elements); $i++) {
+                if (false === strpos($elements[$i], '=')) {
+                    $elName  = trim($elements[$i]);
+                    $elValue = null;
+                } else {
+                    list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+                }
+                $elName = strtolower($elName);
+                if ('secure' == $elName) {
+                    $cookie['secure'] = true;
+                } elseif ('expires' == $elName) {
+                    $cookie['expires'] = str_replace('"', '', $elValue);
+                } elseif ('path' == $elName || 'domain' == $elName) {
+                    $cookie[$elName] = urldecode($elValue);
+                } else {
+                    $cookie[$elName] = $elValue;
+                }
+            }
+        }
+        $this->_cookies[] = $cookie;
+    }
+
+
+   /**
+    * Read a part of response body encoded with chunked Transfer-Encoding
+    *
+    * @access private
+    * @return string
+    */
+    function _readChunked()
+    {
+        // at start of the next chunk?
+        if (0 == $this->_chunkLength) {
+            $line = $this->_sock->readLine();
+            if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+                $this->_chunkLength = hexdec($matches[1]);
+                // Chunk with zero length indicates the end
+                if (0 == $this->_chunkLength) {
+                    $this->_sock->readLine(); // make this an eof()
+                    return '';
+                }
+            } else {
+                return '';
+            }
+        }
+        $data = $this->_sock->read($this->_chunkLength);
+        $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+        if (0 == $this->_chunkLength) {
+            $this->_sock->readLine(); // Trailing CRLF
+        }
+        return $data;
+    }
+
+
+   /**
+    * Notifies all registered listeners of an event.
+    *
+    * @param    string  Event name
+    * @param    mixed   Additional data
+    * @access   private
+    * @see HTTP_Request::_notify()
+    */
+    function _notify($event, $data = null)
+    {
+        foreach (array_keys($this->_listeners) as $id) {
+            $this->_listeners[$id]->update($this, $event, $data);
+        }
+    }
+
+
+   /**
+    * Decodes the message-body encoded by gzip
+    *
+    * The real decoding work is done by gzinflate() built-in function, this
+    * method only parses the header and checks data for compliance with
+    * RFC 1952
+    *
+    * @access   private
+    * @param    string  gzip-encoded data
+    * @return   string  decoded data
+    */
+    function _decodeGzip($data)
+    {
+        if (HTTP_REQUEST_MBSTRING) {
+            $oldEncoding = mb_internal_encoding();
+            mb_internal_encoding('iso-8859-1');
+        }
+        $length = strlen($data);
+        // If it doesn't look like gzip-encoded data, don't bother
+        if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
+            return $data;
+        }
+        $method = ord(substr($data, 2, 1));
+        if (8 != $method) {
+            return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
+        }
+        $flags = ord(substr($data, 3, 1));
+        if ($flags & 224) {
+            return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
+        }
+
+        // header is 10 bytes minimum. may be longer, though.
+        $headerLength = 10;
+        // extra fields, need to skip 'em
+        if ($flags & 4) {
+            if ($length - $headerLength - 2 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $extraLength = unpack('v', substr($data, 10, 2));
+            if ($length - $headerLength - 2 - $extraLength[1] < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $headerLength += $extraLength[1] + 2;
+        }
+        // file name, need to skip that
+        if ($flags & 8) {
+            if ($length - $headerLength - 1 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $filenameLength = strpos(substr($data, $headerLength), chr(0));
+            if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $headerLength += $filenameLength + 1;
+        }
+        // comment, need to skip that also
+        if ($flags & 16) {
+            if ($length - $headerLength - 1 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $commentLength = strpos(substr($data, $headerLength), chr(0));
+            if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $headerLength += $commentLength + 1;
+        }
+        // have a CRC for header. let's check
+        if ($flags & 1) {
+            if ($length - $headerLength - 2 < 8) {
+                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+            }
+            $crcReal   = 0xffff & crc32(substr($data, 0, $headerLength));
+            $crcStored = unpack('v', substr($data, $headerLength, 2));
+            if ($crcReal != $crcStored[1]) {
+                return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+            }
+            $headerLength += 2;
+        }
+        // unpacked data CRC and size at the end of encoded data
+        $tmp = unpack('V2', substr($data, -8));
+        $dataCrc  = $tmp[1];
+        $dataSize = $tmp[2];
+
+        // finally, call the gzinflate() function
+        // don't pass $dataSize to gzinflate, see bugs #13135, #14370
+        $unpacked = gzinflate(substr($data, $headerLength, -8));
+        if (false === $unpacked) {
+            return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
+        } elseif ($dataSize != strlen($unpacked)) {
+            return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
+        } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
+            return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+        }
+        if (HTTP_REQUEST_MBSTRING) {
+            mb_internal_encoding($oldEncoding);
+        }
+        return $unpacked;
+    }
+} // End class HTTP_Response
+?>
+>>>>>>> .merge-right.r23124
Index: branches/version-2_13-dev/data/module/HTTP/Request2.php
===================================================================
--- branches/version-2_13-dev/data/module/HTTP/Request2.php	(revision 23125)
+++ 	(revision )
@@ -1,1050 +1,0 @@
-<?php
-/**
- * Class representing a HTTP request message
- *
- * PHP version 5
- *
- * LICENSE:
- *
- * Copyright (c) 2008-2012, Alexey Borzov <avb@php.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *    * Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *    * Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *    * The names of the authors may not be used to endorse or promote products
- *      derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @category HTTP
- * @package  HTTP_Request2
- * @author   Alexey Borzov <avb@php.net>
- * @license  http://opensource.org/licenses/bsd-license.php New BSD License
- * @version  SVN: $Id: Request2.php 324936 2012-04-07 07:49:03Z avb $
- * @link     http://pear.php.net/package/HTTP_Request2
- */
-
-/**
- * A class representing an URL as per RFC 3986.
- */
-require_once 'Net/URL2.php';
-
-/**
- * Exception class for HTTP_Request2 package
- */
-require_once 'HTTP/Request2/Exception.php';
-
-/**
- * Class representing a HTTP request message
- *
- * @category HTTP
- * @package  HTTP_Request2
- * @author   Alexey Borzov <avb@php.net>
- * @license  http://opensource.org/licenses/bsd-license.php New BSD License
- * @version  Release: 2.1.1
- * @link     http://pear.php.net/package/HTTP_Request2
- * @link     http://tools.ietf.org/html/rfc2616#section-5
- */
-class HTTP_Request2 implements SplSubject
-{
-    /**#@+
-     * Constants for HTTP request methods
-     *
-     * @link http://tools.ietf.org/html/rfc2616#section-5.1.1
-     */
-    const METHOD_OPTIONS = 'OPTIONS';
-    const METHOD_GET     = 'GET';
-    const METHOD_HEAD    = 'HEAD';
-    const METHOD_POST    = 'POST';
-    const METHOD_PUT     = 'PUT';
-    const METHOD_DELETE  = 'DELETE';
-    const METHOD_TRACE   = 'TRACE';
-    const METHOD_CONNECT = 'CONNECT';
-    /**#@-*/
-
-    /**#@+
-     * Constants for HTTP authentication schemes
-     *
-     * @link http://tools.ietf.org/html/rfc2617
-     */
-    const AUTH_BASIC  = 'basic';
-    const AUTH_DIGEST = 'digest';
-    /**#@-*/
-
-    /**
-     * Regular expression used to check for invalid symbols in RFC 2616 tokens
-     * @link http://pear.php.net/bugs/bug.php?id=15630
-     */
-    const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!';
-
-    /**
-     * Regular expression used to check for invalid symbols in cookie strings
-     * @link http://pear.php.net/bugs/bug.php?id=15630
-     * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
-     */
-    const REGEXP_INVALID_COOKIE = '/[\s,;]/';
-
-    /**
-     * Fileinfo magic database resource
-     * @var  resource
-     * @see  detectMimeType()
-     */
-    private static $_fileinfoDb;
-
-    /**
-     * Observers attached to the request (instances of SplObserver)
-     * @var  array
-     */
-    protected $observers = array();
-
-    /**
-     * Request URL
-     * @var  Net_URL2
-     */
-    protected $url;
-
-    /**
-     * Request method
-     * @var  string
-     */
-    protected $method = self::METHOD_GET;
-
-    /**
-     * Authentication data
-     * @var  array
-     * @see  getAuth()
-     */
-    protected $auth;
-
-    /**
-     * Request headers
-     * @var  array
-     */
-    protected $headers = array();
-
-    /**
-     * Configuration parameters
-     * @var  array
-     * @see  setConfig()
-     */
-    protected $config = array(
-        'adapter'           => 'HTTP_Request2_Adapter_Socket',
-        'connect_timeout'   => 10,
-        'timeout'           => 0,
-        'use_brackets'      => true,
-        'protocol_version'  => '1.1',
-        'buffer_size'       => 16384,
-        'store_body'        => true,
-
-        'proxy_host'        => '',
-        'proxy_port'        => '',
-        'proxy_user'        => '',
-        'proxy_password'    => '',
-        'proxy_auth_scheme' => self::AUTH_BASIC,
-        'proxy_type'        => 'http',
-
-        'ssl_verify_peer'   => true,
-        'ssl_verify_host'   => true,
-        'ssl_cafile'        => null,
-        'ssl_capath'        => null,
-        'ssl_local_cert'    => null,
-        'ssl_passphrase'    => null,
-
-        'digest_compat_ie'  => false,
-
-        'follow_redirects'  => false,
-        'max_redirects'     => 5,
-        'strict_redirects'  => false
-    );
-
-    /**
-     * Last event in request / response handling, intended for observers
-     * @var  array
-     * @see  getLastEvent()
-     */
-    protected $lastEvent = array(
-        'name' => 'start',
-        'data' => null
-    );
-
-    /**
-     * Request body
-     * @var  string|resource
-     * @see  setBody()
-     */
-    protected $body = '';
-
-    /**
-     * Array of POST parameters
-     * @var  array
-     */
-    protected $postParams = array();
-
-    /**
-     * Array of file uploads (for multipart/form-data POST requests)
-     * @var  array
-     */
-    protected $uploads = array();
-
-    /**
-     * Adapter used to perform actual HTTP request
-     * @var  HTTP_Request2_Adapter
-     */
-    protected $adapter;
-
-    /**
-     * Cookie jar to persist cookies between requests
-     * @var HTTP_Request2_CookieJar
-     */
-    protected $cookieJar = null;
-
-    /**
-     * Constructor. Can set request URL, method and configuration array.
-     *
-     * Also sets a default value for User-Agent header.
-     *
-     * @param string|Net_Url2 $url    Request URL
-     * @param string          $method Request method
-     * @param array           $config Configuration for this Request instance
-     */
-    public function __construct(
-        $url = null, $method = self::METHOD_GET, array $config = array()
-    ) {
-        $this->setConfig($config);
-        if (!empty($url)) {
-            $this->setUrl($url);
-        }
-        if (!empty($method)) {
-            $this->setMethod($method);
-        }
-        $this->setHeader(
-            'user-agent', 'HTTP_Request2/2.1.1 ' .
-            '(http://pear.php.net/package/http_request2) PHP/' . phpversion()
-        );
-    }
-
-    /**
-     * Sets the URL for this request
-     *
-     * If the URL has userinfo part (username & password) these will be removed
-     * and converted to auth data. If the URL does not have a path component,
-     * that will be set to '/'.
-     *
-     * @param string|Net_URL2 $url Request URL
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     */
-    public function setUrl($url)
-    {
-        if (is_string($url)) {
-            $url = new Net_URL2(
-                $url, array(Net_URL2::OPTION_USE_BRACKETS => $this->config['use_brackets'])
-            );
-        }
-        if (!$url instanceof Net_URL2) {
-            throw new HTTP_Request2_LogicException(
-                'Parameter is not a valid HTTP URL',
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        // URL contains username / password?
-        if ($url->getUserinfo()) {
-            $username = $url->getUser();
-            $password = $url->getPassword();
-            $this->setAuth(rawurldecode($username), $password? rawurldecode($password): '');
-            $url->setUserinfo('');
-        }
-        if ('' == $url->getPath()) {
-            $url->setPath('/');
-        }
-        $this->url = $url;
-
-        return $this;
-    }
-
-    /**
-     * Returns the request URL
-     *
-     * @return   Net_URL2
-     */
-    public function getUrl()
-    {
-        return $this->url;
-    }
-
-    /**
-     * Sets the request method
-     *
-     * @param string $method one of the methods defined in RFC 2616
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException if the method name is invalid
-     */
-    public function setMethod($method)
-    {
-        // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1
-        if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) {
-            throw new HTTP_Request2_LogicException(
-                "Invalid request method '{$method}'",
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        $this->method = $method;
-
-        return $this;
-    }
-
-    /**
-     * Returns the request method
-     *
-     * @return   string
-     */
-    public function getMethod()
-    {
-        return $this->method;
-    }
-
-    /**
-     * Sets the configuration parameter(s)
-     *
-     * The following parameters are available:
-     * <ul>
-     *   <li> 'adapter'           - adapter to use (string)</li>
-     *   <li> 'connect_timeout'   - Connection timeout in seconds (integer)</li>
-     *   <li> 'timeout'           - Total number of seconds a request can take.
-     *                              Use 0 for no limit, should be greater than
-     *                              'connect_timeout' if set (integer)</li>
-     *   <li> 'use_brackets'      - Whether to append [] to array variable names (bool)</li>
-     *   <li> 'protocol_version'  - HTTP Version to use, '1.0' or '1.1' (string)</li>
-     *   <li> 'buffer_size'       - Buffer size to use for reading and writing (int)</li>
-     *   <li> 'store_body'        - Whether to store response body in response object.
-     *                              Set to false if receiving a huge response and
-     *                              using an Observer to save it (boolean)</li>
-     *   <li> 'proxy_type'        - Proxy type, 'http' or 'socks5' (string)</li>
-     *   <li> 'proxy_host'        - Proxy server host (string)</li>
-     *   <li> 'proxy_port'        - Proxy server port (integer)</li>
-     *   <li> 'proxy_user'        - Proxy auth username (string)</li>
-     *   <li> 'proxy_password'    - Proxy auth password (string)</li>
-     *   <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li>
-     *   <li> 'proxy'             - Shorthand for proxy_* parameters, proxy given as URL,
-     *                              e.g. 'socks5://localhost:1080/' (string)</li>
-     *   <li> 'ssl_verify_peer'   - Whether to verify peer's SSL certificate (bool)</li>
-     *   <li> 'ssl_verify_host'   - Whether to check that Common Name in SSL
-     *                              certificate matches host name (bool)</li>
-     *   <li> 'ssl_cafile'        - Cerificate Authority file to verify the peer
-     *                              with (use with 'ssl_verify_peer') (string)</li>
-     *   <li> 'ssl_capath'        - Directory holding multiple Certificate
-     *                              Authority files (string)</li>
-     *   <li> 'ssl_local_cert'    - Name of a file containing local cerificate (string)</li>
-     *   <li> 'ssl_passphrase'    - Passphrase with which local certificate
-     *                              was encoded (string)</li>
-     *   <li> 'digest_compat_ie'  - Whether to imitate behaviour of MSIE 5 and 6
-     *                              in using URL without query string in digest
-     *                              authentication (boolean)</li>
-     *   <li> 'follow_redirects'  - Whether to automatically follow HTTP Redirects (boolean)</li>
-     *   <li> 'max_redirects'     - Maximum number of redirects to follow (integer)</li>
-     *   <li> 'strict_redirects'  - Whether to keep request method on redirects via status 301 and
-     *                              302 (true, needed for compatibility with RFC 2616)
-     *                              or switch to GET (false, needed for compatibility with most
-     *                              browsers) (boolean)</li>
-     * </ul>
-     *
-     * @param string|array $nameOrConfig configuration parameter name or array
-     *                                   ('parameter name' => 'parameter value')
-     * @param mixed        $value        parameter value if $nameOrConfig is not an array
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException If the parameter is unknown
-     */
-    public function setConfig($nameOrConfig, $value = null)
-    {
-        if (is_array($nameOrConfig)) {
-            foreach ($nameOrConfig as $name => $value) {
-                $this->setConfig($name, $value);
-            }
-
-        } elseif ('proxy' == $nameOrConfig) {
-            $url = new Net_URL2($value);
-            $this->setConfig(array(
-                'proxy_type'     => $url->getScheme(),
-                'proxy_host'     => $url->getHost(),
-                'proxy_port'     => $url->getPort(),
-                'proxy_user'     => rawurldecode($url->getUser()),
-                'proxy_password' => rawurldecode($url->getPassword())
-            ));
-
-        } else {
-            if (!array_key_exists($nameOrConfig, $this->config)) {
-                throw new HTTP_Request2_LogicException(
-                    "Unknown configuration parameter '{$nameOrConfig}'",
-                    HTTP_Request2_Exception::INVALID_ARGUMENT
-                );
-            }
-            $this->config[$nameOrConfig] = $value;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Returns the value(s) of the configuration parameter(s)
-     *
-     * @param string $name parameter name
-     *
-     * @return   mixed   value of $name parameter, array of all configuration
-     *                   parameters if $name is not given
-     * @throws   HTTP_Request2_LogicException If the parameter is unknown
-     */
-    public function getConfig($name = null)
-    {
-        if (null === $name) {
-            return $this->config;
-        } elseif (!array_key_exists($name, $this->config)) {
-            throw new HTTP_Request2_LogicException(
-                "Unknown configuration parameter '{$name}'",
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        return $this->config[$name];
-    }
-
-    /**
-     * Sets the autentification data
-     *
-     * @param string $user     user name
-     * @param string $password password
-     * @param string $scheme   authentication scheme
-     *
-     * @return   HTTP_Request2
-     */
-    public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC)
-    {
-        if (empty($user)) {
-            $this->auth = null;
-        } else {
-            $this->auth = array(
-                'user'     => (string)$user,
-                'password' => (string)$password,
-                'scheme'   => $scheme
-            );
-        }
-
-        return $this;
-    }
-
-    /**
-     * Returns the authentication data
-     *
-     * The array has the keys 'user', 'password' and 'scheme', where 'scheme'
-     * is one of the HTTP_Request2::AUTH_* constants.
-     *
-     * @return   array
-     */
-    public function getAuth()
-    {
-        return $this->auth;
-    }
-
-    /**
-     * Sets request header(s)
-     *
-     * The first parameter may be either a full header string 'header: value' or
-     * header name. In the former case $value parameter is ignored, in the latter
-     * the header's value will either be set to $value or the header will be
-     * removed if $value is null. The first parameter can also be an array of
-     * headers, in that case method will be called recursively.
-     *
-     * Note that headers are treated case insensitively as per RFC 2616.
-     *
-     * <code>
-     * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar'
-     * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz'
-     * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux'
-     * $req->setHeader('FOO'); // removes 'Foo' header from request
-     * </code>
-     *
-     * @param string|array      $name    header name, header string ('Header: value')
-     *                                   or an array of headers
-     * @param string|array|null $value   header value if $name is not an array,
-     *                                   header will be removed if value is null
-     * @param bool              $replace whether to replace previous header with the
-     *                                   same name or append to its value
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     */
-    public function setHeader($name, $value = null, $replace = true)
-    {
-        if (is_array($name)) {
-            foreach ($name as $k => $v) {
-                if (is_string($k)) {
-                    $this->setHeader($k, $v, $replace);
-                } else {
-                    $this->setHeader($v, null, $replace);
-                }
-            }
-        } else {
-            if (null === $value && strpos($name, ':')) {
-                list($name, $value) = array_map('trim', explode(':', $name, 2));
-            }
-            // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2
-            if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) {
-                throw new HTTP_Request2_LogicException(
-                    "Invalid header name '{$name}'",
-                    HTTP_Request2_Exception::INVALID_ARGUMENT
-                );
-            }
-            // Header names are case insensitive anyway
-            $name = strtolower($name);
-            if (null === $value) {
-                unset($this->headers[$name]);
-
-            } else {
-                if (is_array($value)) {
-                    $value = implode(', ', array_map('trim', $value));
-                } elseif (is_string($value)) {
-                    $value = trim($value);
-                }
-                if (!isset($this->headers[$name]) || $replace) {
-                    $this->headers[$name] = $value;
-                } else {
-                    $this->headers[$name] .= ', ' . $value;
-                }
-            }
-        }
-
-        return $this;
-    }
-
-    /**
-     * Returns the request headers
-     *
-     * The array is of the form ('header name' => 'header value'), header names
-     * are lowercased
-     *
-     * @return   array
-     */
-    public function getHeaders()
-    {
-        return $this->headers;
-    }
-
-    /**
-     * Adds a cookie to the request
-     *
-     * If the request does not have a CookieJar object set, this method simply
-     * appends a cookie to "Cookie:" header.
-     *
-     * If a CookieJar object is available, the cookie is stored in that object.
-     * Data from request URL will be used for setting its 'domain' and 'path'
-     * parameters, 'expires' and 'secure' will be set to null and false,
-     * respectively. If you need further control, use CookieJar's methods.
-     *
-     * @param string $name  cookie name
-     * @param string $value cookie value
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     * @see      setCookieJar()
-     */
-    public function addCookie($name, $value)
-    {
-        if (!empty($this->cookieJar)) {
-            $this->cookieJar->store(
-                array('name' => $name, 'value' => $value), $this->url
-            );
-
-        } else {
-            $cookie = $name . '=' . $value;
-            if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) {
-                throw new HTTP_Request2_LogicException(
-                    "Invalid cookie: '{$cookie}'",
-                    HTTP_Request2_Exception::INVALID_ARGUMENT
-                );
-            }
-            $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; ';
-            $this->setHeader('cookie', $cookies . $cookie);
-        }
-
-        return $this;
-    }
-
-    /**
-     * Sets the request body
-     *
-     * If you provide file pointer rather than file name, it should support
-     * fstat() and rewind() operations.
-     *
-     * @param string|resource|HTTP_Request2_MultipartBody $body       Either a
-     *               string with the body or filename containing body or
-     *               pointer to an open file or object with multipart body data
-     * @param bool                                        $isFilename Whether
-     *               first parameter is a filename
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     */
-    public function setBody($body, $isFilename = false)
-    {
-        if (!$isFilename && !is_resource($body)) {
-            if (!$body instanceof HTTP_Request2_MultipartBody) {
-                $this->body = (string)$body;
-            } else {
-                $this->body = $body;
-            }
-        } else {
-            $fileData = $this->fopenWrapper($body, empty($this->headers['content-type']));
-            $this->body = $fileData['fp'];
-            if (empty($this->headers['content-type'])) {
-                $this->setHeader('content-type', $fileData['type']);
-            }
-        }
-        $this->postParams = $this->uploads = array();
-
-        return $this;
-    }
-
-    /**
-     * Returns the request body
-     *
-     * @return   string|resource|HTTP_Request2_MultipartBody
-     */
-    public function getBody()
-    {
-        if (self::METHOD_POST == $this->method
-            && (!empty($this->postParams) || !empty($this->uploads))
-        ) {
-            if (0 === strpos($this->headers['content-type'], 'application/x-www-form-urlencoded')) {
-                $body = http_build_query($this->postParams, '', '&');
-                if (!$this->getConfig('use_brackets')) {
-                    $body = preg_replace('/%5B\d+%5D=/', '=', $body);
-                }
-                // support RFC 3986 by not encoding '~' symbol (request #15368)
-                return str_replace('%7E', '~', $body);
-
-            } elseif (0 === strpos($this->headers['content-type'], 'multipart/form-data')) {
-                require_once 'HTTP/Request2/MultipartBody.php';
-                return new HTTP_Request2_MultipartBody(
-                    $this->postParams, $this->uploads, $this->getConfig('use_brackets')
-                );
-            }
-        }
-        return $this->body;
-    }
-
-    /**
-     * Adds a file to form-based file upload
-     *
-     * Used to emulate file upload via a HTML form. The method also sets
-     * Content-Type of HTTP request to 'multipart/form-data'.
-     *
-     * If you just want to send the contents of a file as the body of HTTP
-     * request you should use setBody() method.
-     *
-     * If you provide file pointers rather than file names, they should support
-     * fstat() and rewind() operations.
-     *
-     * @param string                $fieldName    name of file-upload field
-     * @param string|resource|array $filename     full name of local file,
-     *               pointer to open file or an array of files
-     * @param string                $sendFilename filename to send in the request
-     * @param string                $contentType  content-type of file being uploaded
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     */
-    public function addUpload(
-        $fieldName, $filename, $sendFilename = null, $contentType = null
-    ) {
-        if (!is_array($filename)) {
-            $fileData = $this->fopenWrapper($filename, empty($contentType));
-            $this->uploads[$fieldName] = array(
-                'fp'        => $fileData['fp'],
-                'filename'  => !empty($sendFilename)? $sendFilename
-                                :(is_string($filename)? basename($filename): 'anonymous.blob') ,
-                'size'      => $fileData['size'],
-                'type'      => empty($contentType)? $fileData['type']: $contentType
-            );
-        } else {
-            $fps = $names = $sizes = $types = array();
-            foreach ($filename as $f) {
-                if (!is_array($f)) {
-                    $f = array($f);
-                }
-                $fileData = $this->fopenWrapper($f[0], empty($f[2]));
-                $fps[]   = $fileData['fp'];
-                $names[] = !empty($f[1])? $f[1]
-                            :(is_string($f[0])? basename($f[0]): 'anonymous.blob');
-                $sizes[] = $fileData['size'];
-                $types[] = empty($f[2])? $fileData['type']: $f[2];
-            }
-            $this->uploads[$fieldName] = array(
-                'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types
-            );
-        }
-        if (empty($this->headers['content-type'])
-            || 'application/x-www-form-urlencoded' == $this->headers['content-type']
-        ) {
-            $this->setHeader('content-type', 'multipart/form-data');
-        }
-
-        return $this;
-    }
-
-    /**
-     * Adds POST parameter(s) to the request.
-     *
-     * @param string|array $name  parameter name or array ('name' => 'value')
-     * @param mixed        $value parameter value (can be an array)
-     *
-     * @return   HTTP_Request2
-     */
-    public function addPostParameter($name, $value = null)
-    {
-        if (!is_array($name)) {
-            $this->postParams[$name] = $value;
-        } else {
-            foreach ($name as $k => $v) {
-                $this->addPostParameter($k, $v);
-            }
-        }
-        if (empty($this->headers['content-type'])) {
-            $this->setHeader('content-type', 'application/x-www-form-urlencoded');
-        }
-
-        return $this;
-    }
-
-    /**
-     * Attaches a new observer
-     *
-     * @param SplObserver $observer any object implementing SplObserver
-     */
-    public function attach(SplObserver $observer)
-    {
-        foreach ($this->observers as $attached) {
-            if ($attached === $observer) {
-                return;
-            }
-        }
-        $this->observers[] = $observer;
-    }
-
-    /**
-     * Detaches an existing observer
-     *
-     * @param SplObserver $observer any object implementing SplObserver
-     */
-    public function detach(SplObserver $observer)
-    {
-        foreach ($this->observers as $key => $attached) {
-            if ($attached === $observer) {
-                unset($this->observers[$key]);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Notifies all observers
-     */
-    public function notify()
-    {
-        foreach ($this->observers as $observer) {
-            $observer->update($this);
-        }
-    }
-
-    /**
-     * Sets the last event
-     *
-     * Adapters should use this method to set the current state of the request
-     * and notify the observers.
-     *
-     * @param string $name event name
-     * @param mixed  $data event data
-     */
-    public function setLastEvent($name, $data = null)
-    {
-        $this->lastEvent = array(
-            'name' => $name,
-            'data' => $data
-        );
-        $this->notify();
-    }
-
-    /**
-     * Returns the last event
-     *
-     * Observers should use this method to access the last change in request.
-     * The following event names are possible:
-     * <ul>
-     *   <li>'connect'                 - after connection to remote server,
-     *                                   data is the destination (string)</li>
-     *   <li>'disconnect'              - after disconnection from server</li>
-     *   <li>'sentHeaders'             - after sending the request headers,
-     *                                   data is the headers sent (string)</li>
-     *   <li>'sentBodyPart'            - after sending a part of the request body,
-     *                                   data is the length of that part (int)</li>
-     *   <li>'sentBody'                - after sending the whole request body,
-     *                                   data is request body length (int)</li>
-     *   <li>'receivedHeaders'         - after receiving the response headers,
-     *                                   data is HTTP_Request2_Response object</li>
-     *   <li>'receivedBodyPart'        - after receiving a part of the response
-     *                                   body, data is that part (string)</li>
-     *   <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still
-     *                                   encoded by Content-Encoding</li>
-     *   <li>'receivedBody'            - after receiving the complete response
-     *                                   body, data is HTTP_Request2_Response object</li>
-     * </ul>
-     * Different adapters may not send all the event types. Mock adapter does
-     * not send any events to the observers.
-     *
-     * @return   array   The array has two keys: 'name' and 'data'
-     */
-    public function getLastEvent()
-    {
-        return $this->lastEvent;
-    }
-
-    /**
-     * Sets the adapter used to actually perform the request
-     *
-     * You can pass either an instance of a class implementing HTTP_Request2_Adapter
-     * or a class name. The method will only try to include a file if the class
-     * name starts with HTTP_Request2_Adapter_, it will also try to prepend this
-     * prefix to the class name if it doesn't contain any underscores, so that
-     * <code>
-     * $request->setAdapter('curl');
-     * </code>
-     * will work.
-     *
-     * @param string|HTTP_Request2_Adapter $adapter Adapter to use
-     *
-     * @return   HTTP_Request2
-     * @throws   HTTP_Request2_LogicException
-     */
-    public function setAdapter($adapter)
-    {
-        if (is_string($adapter)) {
-            if (!class_exists($adapter, false)) {
-                if (false === strpos($adapter, '_')) {
-                    $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter);
-                }
-                if (!class_exists($adapter, false)
-                    && preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)
-                ) {
-                    include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php';
-                }
-                if (!class_exists($adapter, false)) {
-                    throw new HTTP_Request2_LogicException(
-                        "Class {$adapter} not found",
-                        HTTP_Request2_Exception::MISSING_VALUE
-                    );
-                }
-            }
-            $adapter = new $adapter;
-        }
-        if (!$adapter instanceof HTTP_Request2_Adapter) {
-            throw new HTTP_Request2_LogicException(
-                'Parameter is not a HTTP request adapter',
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        $this->adapter = $adapter;
-
-        return $this;
-    }
-
-    /**
-     * Sets the cookie jar
-     *
-     * A cookie jar is used to maintain cookies across HTTP requests and
-     * responses. Cookies from jar will be automatically added to the request
-     * headers based on request URL.
-     *
-     * @param HTTP_Request2_CookieJar|bool $jar Existing CookieJar object, true to
-     *                                          create a new one, false to remove
-     *
-     * @return HTTP_Request2
-     * @throws HTTP_Request2_LogicException
-     */
-    public function setCookieJar($jar = true)
-    {
-        if (!class_exists('HTTP_Request2_CookieJar', false)) {
-            require_once 'HTTP/Request2/CookieJar.php';
-        }
-
-        if ($jar instanceof HTTP_Request2_CookieJar) {
-            $this->cookieJar = $jar;
-        } elseif (true === $jar) {
-            $this->cookieJar = new HTTP_Request2_CookieJar();
-        } elseif (!$jar) {
-            $this->cookieJar = null;
-        } else {
-            throw new HTTP_Request2_LogicException(
-                'Invalid parameter passed to setCookieJar()',
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-
-        return $this;
-    }
-
-    /**
-     * Returns current CookieJar object or null if none
-     *
-     * @return HTTP_Request2_CookieJar|null
-     */
-    public function getCookieJar()
-    {
-        return $this->cookieJar;
-    }
-
-    /**
-     * Sends the request and returns the response
-     *
-     * @throws   HTTP_Request2_Exception
-     * @return   HTTP_Request2_Response
-     */
-    public function send()
-    {
-        // Sanity check for URL
-        if (!$this->url instanceof Net_URL2
-            || !$this->url->isAbsolute()
-            || !in_array(strtolower($this->url->getScheme()), array('https', 'http'))
-        ) {
-            throw new HTTP_Request2_LogicException(
-                'HTTP_Request2 needs an absolute HTTP(S) request URL, '
-                . ($this->url instanceof Net_URL2
-                   ? "'" . $this->url->__toString() . "'" : 'none')
-                . ' given',
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        if (empty($this->adapter)) {
-            $this->setAdapter($this->getConfig('adapter'));
-        }
-        // magic_quotes_runtime may break file uploads and chunked response
-        // processing; see bug #4543. Don't use ini_get() here; see bug #16440.
-        if ($magicQuotes = get_magic_quotes_runtime()) {
-            set_magic_quotes_runtime(false);
-        }
-        // force using single byte encoding if mbstring extension overloads
-        // strlen() and substr(); see bug #1781, bug #10605
-        if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
-            $oldEncoding = mb_internal_encoding();
-            mb_internal_encoding('8bit');
-        }
-
-        try {
-            $response = $this->adapter->sendRequest($this);
-        } catch (Exception $e) {
-        }
-        // cleanup in either case (poor man's "finally" clause)
-        if ($magicQuotes) {
-            set_magic_quotes_runtime(true);
-        }
-        if (!empty($oldEncoding)) {
-            mb_internal_encoding($oldEncoding);
-        }
-        // rethrow the exception
-        if (!empty($e)) {
-            throw $e;
-        }
-        return $response;
-    }
-
-    /**
-     * Wrapper around fopen()/fstat() used by setBody() and addUpload()
-     *
-     * @param string|resource $file       file name or pointer to open file
-     * @param bool            $detectType whether to try autodetecting MIME
-     *                        type of file, will only work if $file is a
-     *                        filename, not pointer
-     *
-     * @return array array('fp' => file pointer, 'size' => file size, 'type' => MIME type)
-     * @throws HTTP_Request2_LogicException
-     */
-    protected function fopenWrapper($file, $detectType = false)
-    {
-        if (!is_string($file) && !is_resource($file)) {
-            throw new HTTP_Request2_LogicException(
-                "Filename or file pointer resource expected",
-                HTTP_Request2_Exception::INVALID_ARGUMENT
-            );
-        }
-        $fileData = array(
-            'fp'   => is_string($file)? null: $file,
-            'type' => 'application/octet-stream',
-            'size' => 0
-        );
-        if (is_string($file)) {
-            if (!($fileData['fp'] = @fopen($file, 'rb'))) {
-                $error = error_get_last();
-                throw new HTTP_Request2_LogicException(
-                    $error['message'], HTTP_Request2_Exception::READ_ERROR
-                );
-            }
-            if ($detectType) {
-                $fileData['type'] = self::detectMimeType($file);
-            }
-        }
-        if (!($stat = fstat($fileData['fp']))) {
-            throw new HTTP_Request2_LogicException(
-                "fstat() call failed", HTTP_Request2_Exception::READ_ERROR
-            );
-        }
-        $fileData['size'] = $stat['size'];
-
-        return $fileData;
-    }
-
-    /**
-     * Tries to detect MIME type of a file
-     *
-     * The method will try to use fileinfo extension if it is available,
-     * deprecated mime_content_type() function in the other case. If neither
-     * works, default 'application/octet-stream' MIME type is returned
-     *
-     * @param string $filename file name
-     *
-     * @return   string  file MIME type
-     */
-    protected static function detectMimeType($filename)
-    {
-        // finfo extension from PECL available
-        if (function_exists('finfo_open')) {
-            if (!isset(self::$_fileinfoDb)) {
-                self::$_fileinfoDb = @finfo_open(FILEINFO_MIME);
-            }
-            if (self::$_fileinfoDb) {
-                $info = finfo_file(self::$_fileinfoDb, $filename);
-            }
-        }
-        // (deprecated) mime_content_type function available
-        if (empty($info) && function_exists('mime_content_type')) {
-            return mime_content_type($filename);
-        }
-        return empty($info)? 'application/octet-stream': $info;
-    }
-}
-?>
