HTTP_Request
[ class tree: HTTP_Request ] [ index: HTTP_Request ] [ all elements ]

Source for file Request.php

Documentation is available at Request.php

  1. <?php
  2. /**
  3.  * Class for performing HTTP requests
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE:
  8.  *
  9.  * Copyright (c) 2002-2007, Richard Heyes
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or without
  13.  * modification, are permitted provided that the following conditions
  14.  * are met:
  15.  *
  16.  * o Redistributions of source code must retain the above copyright
  17.  *   notice, this list of conditions and the following disclaimer.
  18.  * o Redistributions in binary form must reproduce the above copyright
  19.  *   notice, this list of conditions and the following disclaimer in the
  20.  *   documentation and/or other materials provided with the distribution.
  21.  * o The names of the authors may not be used to endorse or promote
  22.  *   products derived from this software without specific prior written
  23.  *   permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36.  *
  37.  * @category    HTTP
  38.  * @package     HTTP_Request
  39.  * @author      Richard Heyes <richard@phpguru.org>
  40.  * @author      Alexey Borzov <avb@php.net>
  41.  * @copyright   2002-2007 Richard Heyes
  42.  * @license     http://opensource.org/licenses/bsd-license.php New BSD License
  43.  * @version     CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
  44.  * @link        http://pear.php.net/package/HTTP_Request/
  45.  */
  46.  
  47. /**
  48.  * PEAR and PEAR_Error classes (for error handling)
  49.  */
  50. require_once 'PEAR.php';
  51. /**
  52.  * Socket class
  53.  */
  54. require_once 'Net/Socket.php';
  55. /**
  56.  * URL handling class
  57.  */
  58. require_once 'Net/URL.php';
  59.  
  60. /**#@+
  61.  * Constants for HTTP request methods
  62.  */
  63. define('HTTP_REQUEST_METHOD_GET',     'GET',     true);
  64. define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true);
  65. define('HTTP_REQUEST_METHOD_POST',    'POST',    true);
  66. define('HTTP_REQUEST_METHOD_PUT',     'PUT',     true);
  67. define('HTTP_REQUEST_METHOD_DELETE',  'DELETE',  true);
  68. define('HTTP_REQUEST_METHOD_OPTIONS''OPTIONS'true);
  69. define('HTTP_REQUEST_METHOD_TRACE',   'TRACE',   true);
  70. /**#@-*/
  71.  
  72. /**#@+
  73.  * Constants for HTTP request error codes
  74.  */
  75. define('HTTP_REQUEST_ERROR_FILE',             1);
  76. define('HTTP_REQUEST_ERROR_URL',              2);
  77. define('HTTP_REQUEST_ERROR_PROXY',            4);
  78. define('HTTP_REQUEST_ERROR_REDIRECTS',        8);
  79. define('HTTP_REQUEST_ERROR_RESPONSE',        16);
  80. define('HTTP_REQUEST_ERROR_GZIP_METHOD',     32);
  81. define('HTTP_REQUEST_ERROR_GZIP_READ',       64);
  82. define('HTTP_REQUEST_ERROR_GZIP_DATA',      128);
  83. define('HTTP_REQUEST_ERROR_GZIP_CRC',       256);
  84. /**#@-*/
  85.  
  86. /**#@+
  87.  * Constants for HTTP protocol versions
  88.  */
  89. define('HTTP_REQUEST_HTTP_VER_1_0''1.0'true);
  90. define('HTTP_REQUEST_HTTP_VER_1_1''1.1'true);
  91. /**#@-*/
  92.  
  93. if (extension_loaded('mbstring'&& (ini_get('mbstring.func_overload'))) {
  94.    /**
  95.     * Whether string functions are overloaded by their mbstring equivalents
  96.     */
  97.     define('HTTP_REQUEST_MBSTRING'true);
  98. else {
  99.    /**
  100.     * @ignore
  101.     */
  102.     define('HTTP_REQUEST_MBSTRING'false);
  103. }
  104.  
  105. /**
  106.  * Class for performing HTTP requests
  107.  *
  108.  * Simple example (fetches yahoo.com and displays it):
  109.  * <code>
  110.  * $a = &new HTTP_Request('http://www.yahoo.com/');
  111.  * $a->sendRequest();
  112.  * echo $a->getResponseBody();
  113.  * </code>
  114.  *
  115.  * @category    HTTP
  116.  * @package     HTTP_Request
  117.  * @author      Richard Heyes <richard@phpguru.org>
  118.  * @author      Alexey Borzov <avb@php.net>
  119.  * @version     Release: 1.4.4
  120.  */
  121. {
  122.    /**#@+
  123.     * @access private
  124.     */
  125.     /**
  126.     * Instance of Net_URL
  127.     * @var Net_URL 
  128.     */
  129.     var $_url;
  130.  
  131.     /**
  132.     * Type of request
  133.     * @var string 
  134.     */
  135.     var $_method;
  136.  
  137.     /**
  138.     * HTTP Version
  139.     * @var string 
  140.     */
  141.     var $_http;
  142.  
  143.     /**
  144.     * Request headers
  145.     * @var array 
  146.     */
  147.     var $_requestHeaders;
  148.  
  149.     /**
  150.     * Basic Auth Username
  151.     * @var string 
  152.     */
  153.     var $_user;
  154.  
  155.     /**
  156.     * Basic Auth Password
  157.     * @var string 
  158.     */
  159.     var $_pass;
  160.  
  161.     /**
  162.     * Socket object
  163.     * @var Net_Socket 
  164.     */
  165.     var $_sock;
  166.  
  167.     /**
  168.     * Proxy server
  169.     * @var string 
  170.     */
  171.     var $_proxy_host;
  172.  
  173.     /**
  174.     * Proxy port
  175.     * @var integer 
  176.     */
  177.     var $_proxy_port;
  178.  
  179.     /**
  180.     * Proxy username
  181.     * @var string 
  182.     */
  183.     var $_proxy_user;
  184.  
  185.     /**
  186.     * Proxy password
  187.     * @var string 
  188.     */
  189.     var $_proxy_pass;
  190.  
  191.     /**
  192.     * Post data
  193.     * @var array 
  194.     */
  195.     var $_postData;
  196.  
  197.    /**
  198.     * Request body
  199.     * @var string 
  200.     */
  201.     var $_body;
  202.  
  203.    /**
  204.     * A list of methods that MUST NOT have a request body, per RFC 2616
  205.     * @var array 
  206.     */
  207.     var $_bodyDisallowed = array('TRACE');
  208.  
  209.    /**
  210.     * Methods having defined semantics for request body
  211.     *
  212.     * Content-Length header (indicating that the body follows, section 4.3 of
  213.     * RFC 2616) will be sent for these methods even if no body was added
  214.     *
  215.     * @var array 
  216.     */
  217.     var $_bodyRequired = array('POST''PUT');
  218.  
  219.    /**
  220.     * Files to post
  221.     * @var array 
  222.     */
  223.     var $_postFiles = array();
  224.  
  225.     /**
  226.     * Connection timeout.
  227.     * @var float 
  228.     */
  229.     var $_timeout;
  230.  
  231.     /**
  232.     * HTTP_Response object
  233.     * @var HTTP_Response 
  234.     */
  235.     var $_response;
  236.  
  237.     /**
  238.     * Whether to allow redirects
  239.     * @var boolean 
  240.     */
  241.     var $_allowRedirects;
  242.  
  243.     /**
  244.     * Maximum redirects allowed
  245.     * @var integer 
  246.     */
  247.     var $_maxRedirects;
  248.  
  249.     /**
  250.     * Current number of redirects
  251.     * @var integer 
  252.     */
  253.     var $_redirects;
  254.  
  255.    /**
  256.     * Whether to append brackets [] to array variables
  257.     * @var bool 
  258.     */
  259.     var $_useBrackets = true;
  260.  
  261.    /**
  262.     * Attached listeners
  263.     * @var array 
  264.     */
  265.     var $_listeners = array();
  266.  
  267.    /**
  268.     * Whether to save response body in response object property
  269.     * @var bool 
  270.     */
  271.     var $_saveBody = true;
  272.  
  273.    /**
  274.     * Timeout for reading from socket (array(seconds, microseconds))
  275.     * @var array 
  276.     */
  277.     var $_readTimeout = null;
  278.  
  279.    /**
  280.     * Options to pass to Net_Socket::connect. See stream_context_create
  281.     * @var array 
  282.     */
  283.     var $_socketOptions = null;
  284.    /**#@-*/
  285.  
  286.     /**
  287.     * Constructor
  288.     *
  289.     * Sets up the object
  290.     * @param    string  The url to fetch/access
  291.     * @param    array   Associative array of parameters which can have the following keys:
  292.     *  <ul>
  293.     *    <li>method         - Method to use, GET, POST etc (string)</li>
  294.     *    <li>http           - HTTP Version to use, 1.0 or 1.1 (string)</li>
  295.     *    <li>user           - Basic Auth username (string)</li>
  296.     *    <li>pass           - Basic Auth password (string)</li>
  297.     *    <li>proxy_host     - Proxy server host (string)</li>
  298.     *    <li>proxy_port     - Proxy server port (integer)</li>
  299.     *    <li>proxy_user     - Proxy auth username (string)</li>
  300.     *    <li>proxy_pass     - Proxy auth password (string)</li>
  301.     *    <li>timeout        - Connection timeout in seconds (float)</li>
  302.     *    <li>allowRedirects - Whether to follow redirects or not (bool)</li>
  303.     *    <li>maxRedirects   - Max number of redirects to follow (integer)</li>
  304.     *    <li>useBrackets    - Whether to append [] to array variable names (bool)</li>
  305.     *    <li>saveBody       - Whether to save response body in response object property (bool)</li>
  306.     *    <li>readTimeout    - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
  307.     *    <li>socketOptions  - Options to pass to Net_Socket object (array)</li>
  308.     *  </ul>
  309.     * @access public
  310.     */
  311.     function HTTP_Request($url ''$params = array())
  312.     {
  313.         $this->_method         =  HTTP_REQUEST_METHOD_GET;
  314.         $this->_http           =  HTTP_REQUEST_HTTP_VER_1_1;
  315.         $this->_requestHeaders = array();
  316.         $this->_postData       = array();
  317.         $this->_body           = null;
  318.  
  319.         $this->_user = null;
  320.         $this->_pass = null;
  321.  
  322.         $this->_proxy_host = null;
  323.         $this->_proxy_port = null;
  324.         $this->_proxy_user = null;
  325.         $this->_proxy_pass = null;
  326.  
  327.         $this->_allowRedirects = false;
  328.         $this->_maxRedirects   = 3;
  329.         $this->_redirects      = 0;
  330.  
  331.         $this->_timeout  = null;
  332.         $this->_response = null;
  333.  
  334.         foreach ($params as $key => $value{
  335.             $this->{'_' $key$value;
  336.         }
  337.  
  338.         if (!empty($url)) {
  339.             $this->setURL($url);
  340.         }
  341.  
  342.         // Default useragent
  343.         $this->addHeader('User-Agent''PEAR HTTP_Request class ( http://pear.php.net/ )');
  344.  
  345.         // We don't do keep-alives by default
  346.         $this->addHeader('Connection''close');
  347.  
  348.         // Basic authentication
  349.         if (!empty($this->_user)) {
  350.             $this->addHeader('Authorization''Basic ' base64_encode($this->_user . ':' $this->_pass));
  351.         }
  352.  
  353.         // Proxy authentication (see bug #5913)
  354.         if (!empty($this->_proxy_user)) {
  355.             $this->addHeader('Proxy-Authorization''Basic ' base64_encode($this->_proxy_user . ':' $this->_proxy_pass));
  356.         }
  357.  
  358.         // Use gzip encoding if possible
  359.         if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
  360.             $this->addHeader('Accept-Encoding''gzip');
  361.         }
  362.     }
  363.  
  364.     /**
  365.     * Generates a Host header for HTTP/1.1 requests
  366.     *
  367.     * @access private
  368.     * @return string 
  369.     */
  370.     function _generateHostHeader()
  371.     {
  372.         if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol'http'== 0{
  373.             $host $this->_url->host . ':' $this->_url->port;
  374.  
  375.         elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol'https'== 0{
  376.             $host $this->_url->host . ':' $this->_url->port;
  377.  
  378.         elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol'https'== 0 AND strpos($this->_url->url':443'!== false{
  379.             $host $this->_url->host . ':' $this->_url->port;
  380.  
  381.         else {
  382.             $host $this->_url->host;
  383.         }
  384.  
  385.         return $host;
  386.     }
  387.  
  388.     /**
  389.     * Resets the object to its initial state (DEPRECATED).
  390.     * Takes the same parameters as the constructor.
  391.     *
  392.     * @param  string $url    The url to be requested
  393.     * @param  array  $params Associative array of parameters
  394.     *                         (see constructor for details)
  395.     * @access public
  396.     * @deprecated deprecated since 1.2, call the constructor if this is necessary
  397.     */
  398.     function reset($url$params = array())
  399.     {
  400.         $this->HTTP_Request($url$params);
  401.     }
  402.  
  403.     /**
  404.     * Sets the URL to be requested
  405.     *
  406.     * @param  string The url to be requested
  407.     * @access public
  408.     */
  409.     function setURL($url)
  410.     {
  411.         $this->_url = &new Net_URL($url$this->_useBrackets);
  412.  
  413.         if (!empty($this->_url->user|| !empty($this->_url->pass)) {
  414.             $this->setBasicAuth($this->_url->user$this->_url->pass);
  415.         }
  416.  
  417.         if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http{
  418.             $this->addHeader('Host'$this->_generateHostHeader());
  419.         }
  420.  
  421.         // set '/' instead of empty path rather than check later (see bug #8662)
  422.         if (empty($this->_url->path)) {
  423.             $this->_url->path = '/';
  424.         }
  425.     }
  426.  
  427.    /**
  428.     * Returns the current request URL
  429.     *
  430.     * @return   string  Current request URL
  431.     * @access   public
  432.     */
  433.     function getUrl()
  434.     {
  435.         return empty($this->_url)''$this->_url->getUrl();
  436.     }
  437.  
  438.     /**
  439.     * Sets a proxy to be used
  440.     *
  441.     * @param string     Proxy host
  442.     * @param int        Proxy port
  443.     * @param string     Proxy username
  444.     * @param string     Proxy password
  445.     * @access public
  446.     */
  447.     function setProxy($host$port = 8080$user = null$pass = null)
  448.     {
  449.         $this->_proxy_host = $host;
  450.         $this->_proxy_port = $port;
  451.         $this->_proxy_user = $user;
  452.         $this->_proxy_pass = $pass;
  453.  
  454.         if (!empty($user)) {
  455.             $this->addHeader('Proxy-Authorization''Basic ' base64_encode($user ':' $pass));
  456.         }
  457.     }
  458.  
  459.     /**
  460.     * Sets basic authentication parameters
  461.     *
  462.     * @param string     Username
  463.     * @param string     Password
  464.     */
  465.     function setBasicAuth($user$pass)
  466.     {
  467.         $this->_user = $user;
  468.         $this->_pass = $pass;
  469.  
  470.         $this->addHeader('Authorization''Basic ' base64_encode($user ':' $pass));
  471.     }
  472.  
  473.     /**
  474.     * Sets the method to be used, GET, POST etc.
  475.     *
  476.     * @param string     Method to use. Use the defined constants for this
  477.     * @access public
  478.     */
  479.     function setMethod($method)
  480.     {
  481.         $this->_method = $method;
  482.     }
  483.  
  484.     /**
  485.     * Sets the HTTP version to use, 1.0 or 1.1
  486.     *
  487.     * @param string     Version to use. Use the defined constants for this
  488.     * @access public
  489.     */
  490.     function setHttpVer($http)
  491.     {
  492.         $this->_http = $http;
  493.     }
  494.  
  495.     /**
  496.     * Adds a request header
  497.     *
  498.     * @param string     Header name
  499.     * @param string     Header value
  500.     * @access public
  501.     */
  502.     function addHeader($name$value)
  503.     {
  504.         $this->_requestHeaders[strtolower($name)$value;
  505.     }
  506.  
  507.     /**
  508.     * Removes a request header
  509.     *
  510.     * @param string     Header name to remove
  511.     * @access public
  512.     */
  513.     function removeHeader($name)
  514.     {
  515.         if (isset($this->_requestHeaders[strtolower($name)])) {
  516.             unset($this->_requestHeaders[strtolower($name)]);
  517.         }
  518.     }
  519.  
  520.     /**
  521.     * Adds a querystring parameter
  522.     *
  523.     * @param string     Querystring parameter name
  524.     * @param string     Querystring parameter value
  525.     * @param bool       Whether the value is already urlencoded or not, default = not
  526.     * @access public
  527.     */
  528.     function addQueryString($name$value$preencoded = false)
  529.     {
  530.         $this->_url->addQueryString($name$value$preencoded);
  531.     }
  532.  
  533.     /**
  534.     * Sets the querystring to literally what you supply
  535.     *
  536.     * @param string     The querystring data. Should be of the format foo=bar&x=y etc
  537.     * @param bool       Whether data is already urlencoded or not, default = already encoded
  538.     * @access public
  539.     */
  540.     function addRawQueryString($querystring$preencoded = true)
  541.     {
  542.         $this->_url->addRawQueryString($querystring$preencoded);
  543.     }
  544.  
  545.     /**
  546.     * Adds postdata items
  547.     *
  548.     * @param string     Post data name
  549.     * @param string     Post data value
  550.     * @param bool       Whether data is already urlencoded or not, default = not
  551.     * @access public
  552.     */
  553.     function addPostData($name$value$preencoded = false)
  554.     {
  555.         if ($preencoded{
  556.             $this->_postData[$name$value;
  557.         else {
  558.             $this->_postData[$name$this->_arrayMapRecursive('urlencode'$value);
  559.         }
  560.     }
  561.  
  562.    /**
  563.     * Recursively applies the callback function to the value
  564.     *
  565.     * @param    mixed   Callback function
  566.     * @param    mixed   Value to process
  567.     * @access   private
  568.     * @return   mixed   Processed value
  569.     */
  570.     function _arrayMapRecursive($callback$value)
  571.     {
  572.         if (!is_array($value)) {
  573.             return call_user_func($callback$value);
  574.         else {
  575.             $map = array();
  576.             foreach ($value as $k => $v{
  577.                 $map[$k$this->_arrayMapRecursive($callback$v);
  578.             }
  579.             return $map;
  580.         }
  581.     }
  582.  
  583.    /**
  584.     * Adds a file to form-based file upload
  585.     *
  586.     * Used to emulate file upload via a HTML form. The method also sets
  587.     * Content-Type of HTTP request to 'multipart/form-data'.
  588.     *
  589.     * If you just want to send the contents of a file as the body of HTTP
  590.     * request you should use setBody() method.
  591.     *
  592.     * @access public
  593.     * @param  string    name of file-upload field
  594.     * @param  mixed     file name(s)
  595.     * @param  mixed     content-type(s) of file(s) being uploaded
  596.     * @return bool      true on success
  597.     * @throws PEAR_Error
  598.     */
  599.     function addFile($inputName$fileName$contentType 'application/octet-stream')
  600.     {
  601.         if (!is_array($fileName&& !is_readable($fileName)) {
  602.             return PEAR::raiseError("File '{$fileName}' is not readable"HTTP_REQUEST_ERROR_FILE);
  603.         elseif (is_array($fileName)) {
  604.             foreach ($fileName as $name{
  605.                 if (!is_readable($name)) {
  606.                     return PEAR::raiseError("File '{$name}' is not readable"HTTP_REQUEST_ERROR_FILE);
  607.                 }
  608.             }
  609.         }
  610.         $this->addHeader('Content-Type''multipart/form-data');
  611.         $this->_postFiles[$inputName= array(
  612.             'name' => $fileName,
  613.             'type' => $contentType
  614.         );
  615.         return true;
  616.     }
  617.  
  618.     /**
  619.     * Adds raw postdata (DEPRECATED)
  620.     *
  621.     * @param string     The data
  622.     * @param bool       Whether data is preencoded or not, default = already encoded
  623.     * @access public
  624.     * @deprecated       deprecated since 1.3.0, method setBody() should be used instead
  625.     */
  626.     function addRawPostData($postdata$preencoded = true)
  627.     {
  628.         $this->_body = $preencoded $postdata urlencode($postdata);
  629.     }
  630.  
  631.    /**
  632.     * Sets the request body (for POST, PUT and similar requests)
  633.     *
  634.     * @param    string  Request body
  635.     * @access   public
  636.     */
  637.     function setBody($body)
  638.     {
  639.         $this->_body = $body;
  640.     }
  641.  
  642.     /**
  643.     * Clears any postdata that has been added (DEPRECATED).
  644.     *
  645.     * Useful for multiple request scenarios.
  646.     *
  647.     * @access public
  648.     * @deprecated deprecated since 1.2
  649.     */
  650.     function clearPostData()
  651.     {
  652.         $this->_postData = null;
  653.     }
  654.  
  655.     /**
  656.     * Appends a cookie to "Cookie:" header
  657.     *
  658.     * @param string $name cookie name
  659.     * @param string $value cookie value
  660.     * @access public
  661.     */
  662.     function addCookie($name$value)
  663.     {
  664.         $cookies = isset($this->_requestHeaders['cookie']$this->_requestHeaders['cookie']'; ' '';
  665.         $this->addHeader('Cookie'$cookies $name '=' $value);
  666.     }
  667.  
  668.     /**
  669.     * Clears any cookies that have been added (DEPRECATED).
  670.     *
  671.     * Useful for multiple request scenarios
  672.     *
  673.     * @access public
  674.     * @deprecated deprecated since 1.2
  675.     */
  676.     function clearCookies()
  677.     {
  678.         $this->removeHeader('Cookie');
  679.     }
  680.  
  681.     /**
  682.     * Sends the request
  683.     *
  684.     * @access public
  685.     * @param  bool   Whether to store response body in Response object property,
  686.     *                 set this to false if downloading a LARGE file and using a Listener
  687.     * @return mixed  PEAR error on error, true otherwise
  688.     */
  689.     function sendRequest($saveBody = true)
  690.     {
  691.         if (!is_a($this->_url'Net_URL')) {
  692.             return PEAR::raiseError('No URL given'HTTP_REQUEST_ERROR_URL);
  693.         }
  694.  
  695.         $host = isset($this->_proxy_host$this->_proxy_host : $this->_url->host;
  696.         $port = isset($this->_proxy_port$this->_proxy_port : $this->_url->port;
  697.  
  698.         if (strcasecmp($this->_url->protocol'https'== 0{
  699.             // Bug #14127, don't try connecting to HTTPS sites without OpenSSL
  700.             if (version_compare(PHP_VERSION'4.3.0''<'|| !extension_loaded('openssl')) {
  701.                 return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
  702.                                         HTTP_REQUEST_ERROR_URL);
  703.             elseif (isset($this->_proxy_host)) {
  704.                 return PEAR::raiseError('HTTPS proxies are not supported'HTTP_REQUEST_ERROR_PROXY);
  705.             }
  706.             $host 'ssl://' $host;
  707.         }
  708.  
  709.         // magic quotes may fuck up file uploads and chunked response processing
  710.         $magicQuotes ini_get('magic_quotes_runtime');
  711.         ini_set('magic_quotes_runtime'false);
  712.  
  713.         // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
  714.         // connection token to a proxy server...
  715.         if (isset($this->_proxy_host&& !empty($this->_requestHeaders['connection']&&
  716.             'Keep-Alive' == $this->_requestHeaders['connection'])
  717.         {
  718.             $this->removeHeader('connection');
  719.         }
  720.  
  721.         $keepAlive (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
  722.                      (!empty($this->_requestHeaders['connection']&& 'Keep-Alive' == $this->_requestHeaders['connection']);
  723.         $sockets   &PEAR::getStaticProperty('HTTP_Request''sockets');
  724.         $sockKey   $host ':' $port;
  725.         unset($this->_sock);
  726.  
  727.         // There is a connected socket in the "static" property?
  728.         if ($keepAlive && !empty($sockets[$sockKey]&&
  729.             !empty($sockets[$sockKey]->fp))
  730.         {
  731.             $this->_sock =$sockets[$sockKey];
  732.             $err = null;
  733.         else {
  734.             $this->_notify('connect');
  735.             $this->_sock =new Net_Socket();
  736.             $err $this->_sock->connect($host$portnull$this->_timeout$this->_socketOptions);
  737.         }
  738.         PEAR::isError($error $err $this->_sock->write($this->_buildRequest());
  739.  
  740.         if (!PEAR::isError($err)) {
  741.             if (!empty($this->_readTimeout)) {
  742.                 $this->_sock->setTimeout($this->_readTimeout[0]$this->_readTimeout[1]);
  743.             }
  744.  
  745.             $this->_notify('sentRequest');
  746.  
  747.             // Read the response
  748.             $this->_response = &new HTTP_Response($this->_sock$this->_listeners);
  749.             $err $this->_response->process(
  750.                 $this->_saveBody && $saveBody,
  751.                 HTTP_REQUEST_METHOD_HEAD != $this->_method
  752.             );
  753.  
  754.             if ($keepAlive{
  755.                 $keepAlive (isset($this->_response->_headers['content-length'])
  756.                               || (isset($this->_response->_headers['transfer-encoding'])
  757.                                   && strtolower($this->_response->_headers['transfer-encoding']== 'chunked'));
  758.                 if ($keepAlive{
  759.                     if (isset($this->_response->_headers['connection'])) {
  760.                         $keepAlive strtolower($this->_response->_headers['connection']== 'keep-alive';
  761.                     else {
  762.                         $keepAlive 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
  763.                     }
  764.                 }
  765.             }
  766.         }
  767.  
  768.         ini_set('magic_quotes_runtime'$magicQuotes);
  769.  
  770.         if (PEAR::isError($err)) {
  771.             return $err;
  772.         }
  773.  
  774.         if (!$keepAlive{
  775.             $this->disconnect();
  776.         // Store the connected socket in "static" property
  777.         elseif (empty($sockets[$sockKey]|| empty($sockets[$sockKey]->fp)) {
  778.             $sockets[$sockKey=$this->_sock;
  779.         }
  780.  
  781.         // Check for redirection
  782.         if (    $this->_allowRedirects
  783.             AND $this->_redirects <= $this->_maxRedirects
  784.             AND $this->getResponseCode(> 300
  785.             AND $this->getResponseCode(< 399
  786.             AND !empty($this->_response->_headers['location'])) {
  787.  
  788.  
  789.             $redirect $this->_response->_headers['location'];
  790.  
  791.             // Absolute URL
  792.             if (preg_match('/^https?:\/\//i'$redirect)) {
  793.                 $this->_url = &new Net_URL($redirect);
  794.                 $this->addHeader('Host'$this->_generateHostHeader());
  795.             // Absolute path
  796.             elseif ($redirect{0== '/'{
  797.                 $this->_url->path = $redirect;
  798.  
  799.             // Relative path
  800.             elseif (substr($redirect03== '../' OR substr($redirect02== './'{
  801.                 if (substr($this->_url->path-1== '/'{
  802.                     $redirect $this->_url->path . $redirect;
  803.                 else {
  804.                     $redirect dirname($this->_url->path'/' $redirect;
  805.                 }
  806.                 $redirect = Net_URL::resolvePath($redirect);
  807.                 $this->_url->path = $redirect;
  808.  
  809.             // Filename, no path
  810.             else {
  811.                 if (substr($this->_url->path-1== '/'{
  812.                     $redirect $this->_url->path . $redirect;
  813.                 else {
  814.                     $redirect dirname($this->_url->path'/' $redirect;
  815.                 }
  816.                 $this->_url->path = $redirect;
  817.             }
  818.  
  819.             $this->_redirects++;
  820.             return $this->sendRequest($saveBody);
  821.  
  822.         // Too many redirects
  823.         elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects{
  824.             return PEAR::raiseError('Too many redirects'HTTP_REQUEST_ERROR_REDIRECTS);
  825.         }
  826.  
  827.         return true;
  828.     }
  829.  
  830.     /**
  831.      * Disconnect the socket, if connected. Only useful if using Keep-Alive.
  832.      *
  833.      * @access public
  834.      */
  835.     function disconnect()
  836.     {
  837.         if (!empty($this->_sock&& !empty($this->_sock->fp)) {
  838.             $this->_notify('disconnect');
  839.             $this->_sock->disconnect();
  840.         }
  841.     }
  842.  
  843.     /**
  844.     * Returns the response code
  845.     *
  846.     * @access public
  847.     * @return mixed     Response code, false if not set
  848.     */
  849.     function getResponseCode()
  850.     {
  851.         return isset($this->_response->_code$this->_response->_code : false;
  852.     }
  853.  
  854.     /**
  855.     * Returns the response reason phrase
  856.     *
  857.     * @access public
  858.     * @return mixed     Response reason phrase, false if not set
  859.     */
  860.     function getResponseReason()
  861.     {
  862.         return isset($this->_response->_reason$this->_response->_reason : false;
  863.     }
  864.  
  865.     /**
  866.     * Returns either the named header or all if no name given
  867.     *
  868.     * @access public
  869.     * @param string     The header name to return, do not set to get all headers
  870.     * @return mixed     either the value of $headername (false if header is not present)
  871.     *                    or an array of all headers
  872.     */
  873.     function getResponseHeader($headername = null)
  874.     {
  875.         if (!isset($headername)) {
  876.             return isset($this->_response->_headers)$this->_response->_headers: array();
  877.         else {
  878.             $headername strtolower($headername);
  879.             return isset($this->_response->_headers[$headername]$this->_response->_headers[$headername: false;
  880.         }
  881.     }
  882.  
  883.     /**
  884.     * Returns the body of the response
  885.     *
  886.     * @access public
  887.     * @return mixed     response body, false if not set
  888.     */
  889.     function getResponseBody()
  890.     {
  891.         return isset($this->_response->_body$this->_response->_body : false;
  892.     }
  893.  
  894.     /**
  895.     * Returns cookies set in response
  896.     *
  897.     * @access public
  898.     * @return mixed     array of response cookies, false if none are present
  899.     */
  900.     function getResponseCookies()
  901.     {
  902.         return isset($this->_response->_cookies$this->_response->_cookies : false;
  903.     }
  904.  
  905.     /**
  906.     * Builds the request string
  907.     *
  908.     * @access private
  909.     * @return string The request string
  910.     */
  911.     function _buildRequest()
  912.     {
  913.         $separator ini_get('arg_separator.output');
  914.         ini_set('arg_separator.output''&');
  915.         $querystring ($querystring $this->_url->getQueryString()) '?' $querystring '';
  916.         ini_set('arg_separator.output'$separator);
  917.  
  918.         $host = isset($this->_proxy_host$this->_url->protocol . '://' $this->_url->host : '';
  919.         $port (isset($this->_proxy_hostAND $this->_url->port != 80':' $this->_url->port : '';
  920.         $path $this->_url->path . $querystring;
  921.         $url  $host $port $path;
  922.  
  923.         if (!strlen($url)) {
  924.             $url '/';
  925.         }
  926.  
  927.         $request $this->_method . ' ' $url ' HTTP/' $this->_http . "\r\n";
  928.  
  929.         if (in_array($this->_method$this->_bodyDisallowed||
  930.             (0 == strlen($this->_body&& (HTTP_REQUEST_METHOD_POST != $this->_method ||
  931.              (empty($this->_postData&& empty($this->_postFiles)))))
  932.         {
  933.             $this->removeHeader('Content-Type');
  934.         else {
  935.             if (empty($this->_requestHeaders['content-type'])) {
  936.                 // Add default content-type
  937.                 $this->addHeader('Content-Type''application/x-www-form-urlencoded');
  938.             elseif ('multipart/form-data' == $this->_requestHeaders['content-type']{
  939.                 $boundary 'HTTP_Request_' md5(uniqid('request'microtime());
  940.                 $this->addHeader('Content-Type''multipart/form-data; boundary=' $boundary);
  941.             }
  942.         }
  943.  
  944.         // Request Headers
  945.         if (!empty($this->_requestHeaders)) {
  946.             foreach ($this->_requestHeaders as $name => $value{
  947.                 $canonicalName implode('-'array_map('ucfirst'explode('-'$name)));
  948.                 $request      .= $canonicalName ': ' $value "\r\n";
  949.             }
  950.         }
  951.  
  952.         // Method does not allow a body, simply add a final CRLF
  953.         if (in_array($this->_method$this->_bodyDisallowed)) {
  954.  
  955.             $request .= "\r\n";
  956.  
  957.         // Post data if it's an array
  958.         elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
  959.                   (!empty($this->_postData|| !empty($this->_postFiles))) {
  960.  
  961.             // "normal" POST request
  962.             if (!isset($boundary)) {
  963.                 $postdata implode('&'array_map(
  964.                     create_function('$a''return $a[0] . \'=\' . $a[1];'),
  965.                     $this->_flattenArray(''$this->_postData)
  966.                 ));
  967.  
  968.             // multipart request, probably with file uploads
  969.             else {
  970.                 $postdata '';
  971.                 if (!empty($this->_postData)) {
  972.                     $flatData $this->_flattenArray(''$this->_postData);
  973.                     foreach ($flatData as $item{
  974.                         $postdata .= '--' $boundary "\r\n";
  975.                         $postdata .= 'Content-Disposition: form-data; name="' $item[0'"';
  976.                         $postdata .= "\r\n\r\n" urldecode($item[1]"\r\n";
  977.                     }
  978.                 }
  979.                 foreach ($this->_postFiles as $name => $value{
  980.                     if (is_array($value['name'])) {
  981.                         $varname       $name ($this->_useBrackets? '[]''');
  982.                     else {
  983.                         $varname       $name;
  984.                         $value['name'= array($value['name']);
  985.                     }
  986.                     foreach ($value['name'as $key => $filename{
  987.                         $fp       fopen($filename'r');
  988.                         $basename basename($filename);
  989.                         $type     is_array($value['type'])@$value['type'][$key]$value['type'];
  990.  
  991.                         $postdata .= '--' $boundary "\r\n";
  992.                         $postdata .= 'Content-Disposition: form-data; name="' $varname '"; filename="' $basename '"';
  993.                         $postdata .= "\r\nContent-Type: " $type;
  994.                         $postdata .= "\r\n\r\n" fread($fpfilesize($filename)) "\r\n";
  995.                         fclose($fp);
  996.                     }
  997.                 }
  998.                 $postdata .= '--' $boundary "--\r\n";
  999.             }
  1000.             $request .= 'Content-Length: ' .
  1001.                         (HTTP_REQUEST_MBSTRINGmb_strlen($postdata'iso-8859-1')strlen($postdata)) .
  1002.                         "\r\n\r\n";
  1003.             $request .= $postdata;
  1004.  
  1005.         // Explicitly set request body
  1006.         elseif (0 < strlen($this->_body)) {
  1007.  
  1008.             $request .= 'Content-Length: ' .
  1009.                         (HTTP_REQUEST_MBSTRINGmb_strlen($this->_body'iso-8859-1')strlen($this->_body)) .
  1010.                         "\r\n\r\n";
  1011.             $request .= $this->_body;
  1012.  
  1013.         // No body: send a Content-Length header nonetheless (request #12900),
  1014.         // but do that only for methods that require a body (bug #14740)
  1015.         else {
  1016.  
  1017.             if (in_array($this->_method$this->_bodyRequired)) {
  1018.                 $request .= "Content-Length: 0\r\n";
  1019.             }
  1020.             $request .= "\r\n";
  1021.         }
  1022.  
  1023.         return $request;
  1024.     }
  1025.  
  1026.    /**
  1027.     * Helper function to change the (probably multidimensional) associative array
  1028.     * into the simple one.
  1029.     *
  1030.     * @param    string  name for item
  1031.     * @param    mixed   item's values
  1032.     * @return   array   array with the following items: array('item name', 'item value');
  1033.     * @access   private
  1034.     */
  1035.     function _flattenArray($name$values)
  1036.     {
  1037.         if (!is_array($values)) {
  1038.             return array(array($name$values));
  1039.         else {
  1040.             $ret = array();
  1041.             foreach ($values as $k => $v{
  1042.                 if (empty($name)) {
  1043.                     $newName $k;
  1044.                 elseif ($this->_useBrackets{
  1045.                     $newName $name '[' $k ']';
  1046.                 else {
  1047.                     $newName $name;
  1048.                 }
  1049.                 $ret array_merge($ret$this->_flattenArray($newName$v));
  1050.             }
  1051.             return $ret;
  1052.         }
  1053.     }
  1054.  
  1055.  
  1056.    /**
  1057.     * Adds a Listener to the list of listeners that are notified of
  1058.     * the object's events
  1059.     *
  1060.     * Events sent by HTTP_Request object
  1061.     * - 'connect': on connection to server
  1062.     * - 'sentRequest': after the request was sent
  1063.     * - 'disconnect': on disconnection from server
  1064.     *
  1065.     * Events sent by HTTP_Response object
  1066.     * - 'gotHeaders': after receiving response headers (headers are passed in $data)
  1067.     * - 'tick': on receiving a part of response body (the part is passed in $data)
  1068.     * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
  1069.     * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
  1070.     *
  1071.     * @param    HTTP_Request_Listener   listener to attach
  1072.     * @return   boolean                 whether the listener was successfully attached
  1073.     * @access   public
  1074.     */
  1075.     function attach(&$listener)
  1076.     {
  1077.         if (!is_a($listener'HTTP_Request_Listener')) {
  1078.             return false;
  1079.         }
  1080.         $this->_listeners[$listener->getId()=$listener;
  1081.         return true;
  1082.     }
  1083.  
  1084.  
  1085.    /**
  1086.     * Removes a Listener from the list of listeners
  1087.     *
  1088.     * @param    HTTP_Request_Listener   listener to detach
  1089.     * @return   boolean                 whether the listener was successfully detached
  1090.     * @access   public
  1091.     */
  1092.     function detach(&$listener)
  1093.     {
  1094.         if (!is_a($listener'HTTP_Request_Listener'||
  1095.             !isset($this->_listeners[$listener->getId()])) {
  1096.             return false;
  1097.         }
  1098.         unset($this->_listeners[$listener->getId()]);
  1099.         return true;
  1100.     }
  1101.  
  1102.  
  1103.    /**
  1104.     * Notifies all registered listeners of an event.
  1105.     *
  1106.     * @param    string  Event name
  1107.     * @param    mixed   Additional data
  1108.     * @access   private
  1109.     * @see      HTTP_Request::attach()
  1110.     */
  1111.     function _notify($event$data = null)
  1112.     {
  1113.         foreach (array_keys($this->_listenersas $id{
  1114.             $this->_listeners[$id]->update($this$event$data);
  1115.         }
  1116.     }
  1117. }
  1118.  
  1119.  
  1120. /**
  1121.  * Response class to complement the Request class
  1122.  *
  1123.  * @category    HTTP
  1124.  * @package     HTTP_Request
  1125.  * @author      Richard Heyes <richard@phpguru.org>
  1126.  * @author      Alexey Borzov <avb@php.net>
  1127.  * @version     Release: 1.4.4
  1128.  */
  1129. {
  1130.     /**
  1131.     * Socket object
  1132.     * @var Net_Socket 
  1133.     */
  1134.     var $_sock;
  1135.  
  1136.     /**
  1137.     * Protocol
  1138.     * @var string 
  1139.     */
  1140.     var $_protocol;
  1141.  
  1142.     /**
  1143.     * Return code
  1144.     * @var string 
  1145.     */
  1146.     var $_code;
  1147.  
  1148.     /**
  1149.     * Response reason phrase
  1150.     * @var string 
  1151.     */
  1152.     var $_reason;
  1153.  
  1154.     /**
  1155.     * Response headers
  1156.     * @var array 
  1157.     */
  1158.     var $_headers;
  1159.  
  1160.     /**
  1161.     * Cookies set in response
  1162.     * @var array 
  1163.     */
  1164.     var $_cookies;
  1165.  
  1166.     /**
  1167.     * Response body
  1168.     * @var string 
  1169.     */
  1170.     var $_body '';
  1171.  
  1172.    /**
  1173.     * Used by _readChunked(): remaining length of the current chunk
  1174.     * @var string 
  1175.     */
  1176.     var $_chunkLength = 0;
  1177.  
  1178.    /**
  1179.     * Attached listeners
  1180.     * @var array 
  1181.     */
  1182.     var $_listeners = array();
  1183.  
  1184.    /**
  1185.     * Bytes left to read from message-body
  1186.     * @var null|int
  1187.     */
  1188.     var $_toRead;
  1189.  
  1190.     /**
  1191.     * Constructor
  1192.     *
  1193.     * @param  Net_Socket    socket to read the response from
  1194.     * @param  array         listeners attached to request
  1195.     */
  1196.     function HTTP_Response(&$sock&$listeners)
  1197.     {
  1198.         $this->_sock      =$sock;
  1199.         $this->_listeners =$listeners;
  1200.     }
  1201.  
  1202.  
  1203.    /**
  1204.     * Processes a HTTP response
  1205.     *
  1206.     * This extracts response code, headers, cookies and decodes body if it
  1207.     * was encoded in some way
  1208.     *
  1209.     * @access public
  1210.     * @param  bool      Whether to store response body in object property, set
  1211.     *                    this to false if downloading a LARGE file and using a Listener.
  1212.     *                    This is assumed to be true if body is gzip-encoded.
  1213.     * @param  bool      Whether the response can actually have a message-body.
  1214.     *                    Will be set to false for HEAD requests.
  1215.     * @throws PEAR_Error
  1216.     * @return mixed     true on success, PEAR_Error in case of malformed response
  1217.     */
  1218.     function process($saveBody = true$canHaveBody = true)
  1219.     {
  1220.         do {
  1221.             $line $this->_sock->readLine();
  1222.             if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!'$line$s)) {
  1223.                 return PEAR::raiseError('Malformed response'HTTP_REQUEST_ERROR_RESPONSE);
  1224.             else {
  1225.                 $this->_protocol $s[1];
  1226.                 $this->_code     intval($s[2]);
  1227.                 $this->_reason   = empty($s[3])? null: $s[3];
  1228.             }
  1229.             while ('' !== ($header $this->_sock->readLine())) {
  1230.                 $this->_processHeader($header);
  1231.             }
  1232.         while (100 == $this->_code);
  1233.  
  1234.         $this->_notify('gotHeaders'$this->_headers);
  1235.  
  1236.         // RFC 2616, section 4.4:
  1237.         // 1. Any response message which "MUST NOT" include a message-body ...
  1238.         // is always terminated by the first empty line after the header fields
  1239.         // 3. ... If a message is received with both a
  1240.         // Transfer-Encoding header field and a Content-Length header field,
  1241.         // the latter MUST be ignored.
  1242.         $canHaveBody $canHaveBody && $this->_code >= 200 &&
  1243.                        $this->_code != 204 && $this->_code != 304;
  1244.  
  1245.         // If response body is present, read it and decode
  1246.         $chunked = isset($this->_headers['transfer-encoding']&& ('chunked' == $this->_headers['transfer-encoding']);
  1247.         $gzipped = isset($this->_headers['content-encoding']&& ('gzip' == $this->_headers['content-encoding']);
  1248.         $hasBody = false;
  1249.         if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']||
  1250.                 0 != $this->_headers['content-length']))
  1251.         {
  1252.             if ($chunked || !isset($this->_headers['content-length'])) {
  1253.                 $this->_toRead = null;
  1254.             else {
  1255.                 $this->_toRead $this->_headers['content-length'];
  1256.             }
  1257.             while (!$this->_sock->eof(&& (is_null($this->_toRead|| 0 < $this->_toRead)) {
  1258.                 if ($chunked{
  1259.                     $data $this->_readChunked();
  1260.                 elseif (is_null($this->_toRead)) {
  1261.                     $data $this->_sock->read(4096);
  1262.                 else {
  1263.                     $data $this->_sock->read(min(4096$this->_toRead));
  1264.                     $this->_toRead -= HTTP_REQUEST_MBSTRINGmb_strlen($data'iso-8859-1')strlen($data);
  1265.                 }
  1266.                 if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
  1267.                     break;
  1268.                 else {
  1269.                     $hasBody = true;
  1270.                     if ($saveBody || $gzipped{
  1271.                         $this->_body .= $data;
  1272.                     }
  1273.                     $this->_notify($gzipped'gzTick''tick'$data);
  1274.                 }
  1275.             }
  1276.         }
  1277.  
  1278.         if ($hasBody{
  1279.             // Uncompress the body if needed
  1280.             if ($gzipped{
  1281.                 $body $this->_decodeGzip($this->_body);
  1282.                 if (PEAR::isError($body)) {
  1283.                     return $body;
  1284.                 }
  1285.                 $this->_body $body;
  1286.                 $this->_notify('gotBody'$this->_body);
  1287.             else {
  1288.                 $this->_notify('gotBody');
  1289.             }
  1290.         }
  1291.         return true;
  1292.     }
  1293.  
  1294.  
  1295.    /**
  1296.     * Processes the response header
  1297.     *
  1298.     * @access private
  1299.     * @param  string    HTTP header
  1300.     */
  1301.     function _processHeader($header)
  1302.     {
  1303.         if (false === strpos($header':')) {
  1304.             return;
  1305.         }
  1306.         list($headername$headervalueexplode(':'$header2);
  1307.         $headername  strtolower($headername);
  1308.         $headervalue ltrim($headervalue);
  1309.  
  1310.         if ('set-cookie' != $headername{
  1311.             if (isset($this->_headers[$headername])) {
  1312.                 $this->_headers[$headername.= ',' $headervalue;
  1313.             else {
  1314.                 $this->_headers[$headername]  $headervalue;
  1315.             }
  1316.         else {
  1317.             $this->_parseCookie($headervalue);
  1318.         }
  1319.     }
  1320.  
  1321.  
  1322.    /**
  1323.     * Parse a Set-Cookie header to fill $_cookies array
  1324.     *
  1325.     * @access private
  1326.     * @param  string    value of Set-Cookie header
  1327.     */
  1328.     function _parseCookie($headervalue)
  1329.     {
  1330.         $cookie = array(
  1331.             'expires' => null,
  1332.             'domain'  => null,
  1333.             'path'    => null,
  1334.             'secure'  => false
  1335.         );
  1336.  
  1337.         // Only a name=value pair
  1338.         if (!strpos($headervalue';')) {
  1339.             $pos strpos($headervalue'=');
  1340.             $cookie['name']  trim(substr($headervalue0$pos));
  1341.             $cookie['value'trim(substr($headervalue$pos + 1));
  1342.  
  1343.         // Some optional parameters are supplied
  1344.         else {
  1345.             $elements explode(';'$headervalue);
  1346.             $pos strpos($elements[0]'=');
  1347.             $cookie['name']  trim(substr($elements[0]0$pos));
  1348.             $cookie['value'trim(substr($elements[0]$pos + 1));
  1349.  
  1350.             for ($i = 1; $i count($elements)$i++{
  1351.                 if (false === strpos($elements[$i]'=')) {
  1352.                     $elName  trim($elements[$i]);
  1353.                     $elValue = null;
  1354.                 else {
  1355.                     list ($elName$elValuearray_map('trim'explode('='$elements[$i]));
  1356.                 }
  1357.                 $elName strtolower($elName);
  1358.                 if ('secure' == $elName{
  1359.                     $cookie['secure'= true;
  1360.                 elseif ('expires' == $elName{
  1361.                     $cookie['expires'str_replace('"'''$elValue);
  1362.                 elseif ('path' == $elName || 'domain' == $elName{
  1363.                     $cookie[$elNameurldecode($elValue);
  1364.                 else {
  1365.                     $cookie[$elName$elValue;
  1366.                 }
  1367.             }
  1368.         }
  1369.         $this->_cookies[$cookie;
  1370.     }
  1371.  
  1372.  
  1373.    /**
  1374.     * Read a part of response body encoded with chunked Transfer-Encoding
  1375.     *
  1376.     * @access private
  1377.     * @return string 
  1378.     */
  1379.     function _readChunked()
  1380.     {
  1381.         // at start of the next chunk?
  1382.         if (0 == $this->_chunkLength{
  1383.             $line $this->_sock->readLine();
  1384.             if (preg_match('/^([0-9a-f]+)/i'$line$matches)) {
  1385.                 $this->_chunkLength hexdec($matches[1]);
  1386.                 // Chunk with zero length indicates the end
  1387.                 if (0 == $this->_chunkLength{
  1388.                     $this->_sock->readLine()// make this an eof()
  1389.                     return '';
  1390.                 }
  1391.             else {
  1392.                 return '';
  1393.             }
  1394.         }
  1395.         $data $this->_sock->read($this->_chunkLength);
  1396.         $this->_chunkLength -= HTTP_REQUEST_MBSTRINGmb_strlen($data'iso-8859-1')strlen($data);
  1397.         if (0 == $this->_chunkLength{
  1398.             $this->_sock->readLine()// Trailing CRLF
  1399.         }
  1400.         return $data;
  1401.     }
  1402.  
  1403.  
  1404.    /**
  1405.     * Notifies all registered listeners of an event.
  1406.     *
  1407.     * @param    string  Event name
  1408.     * @param    mixed   Additional data
  1409.     * @access   private
  1410.     * @see HTTP_Request::_notify()
  1411.     */
  1412.     function _notify($event$data = null)
  1413.     {
  1414.         foreach (array_keys($this->_listenersas $id{
  1415.             $this->_listeners[$id]->update($this$event$data);
  1416.         }
  1417.     }
  1418.  
  1419.  
  1420.    /**
  1421.     * Decodes the message-body encoded by gzip
  1422.     *
  1423.     * The real decoding work is done by gzinflate() built-in function, this
  1424.     * method only parses the header and checks data for compliance with
  1425.     * RFC 1952
  1426.     *
  1427.     * @access   private
  1428.     * @param    string  gzip-encoded data
  1429.     * @return   string  decoded data
  1430.     */
  1431.     function _decodeGzip($data)
  1432.     {
  1433.         if (HTTP_REQUEST_MBSTRING{
  1434.             $oldEncoding mb_internal_encoding();
  1435.             mb_internal_encoding('iso-8859-1');
  1436.         }
  1437.         $length strlen($data);
  1438.         // If it doesn't look like gzip-encoded data, don't bother
  1439.         if (18 > $length || strcmp(substr($data02)"\x1f\x8b")) {
  1440.             return $data;
  1441.         }
  1442.         $method ord(substr($data21));
  1443.         if (8 != $method{
  1444.             return PEAR::raiseError('_decodeGzip(): unknown compression method'HTTP_REQUEST_ERROR_GZIP_METHOD);
  1445.         }
  1446.         $flags ord(substr($data31));
  1447.         if ($flags 224{
  1448.             return PEAR::raiseError('_decodeGzip(): reserved bits are set'HTTP_REQUEST_ERROR_GZIP_DATA);
  1449.         }
  1450.  
  1451.         // header is 10 bytes minimum. may be longer, though.
  1452.         $headerLength = 10;
  1453.         // extra fields, need to skip 'em
  1454.         if ($flags 4{
  1455.             if ($length $headerLength - 2 < 8{
  1456.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1457.             }
  1458.             $extraLength unpack('v'substr($data102));
  1459.             if ($length $headerLength - 2 - $extraLength[1< 8{
  1460.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1461.             }
  1462.             $headerLength += $extraLength[1+ 2;
  1463.         }
  1464.         // file name, need to skip that
  1465.         if ($flags 8{
  1466.             if ($length $headerLength - 1 < 8{
  1467.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1468.             }
  1469.             $filenameLength strpos(substr($data$headerLength)chr(0));
  1470.             if (false === $filenameLength || $length $headerLength $filenameLength - 1 < 8{
  1471.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1472.             }
  1473.             $headerLength += $filenameLength + 1;
  1474.         }
  1475.         // comment, need to skip that also
  1476.         if ($flags 16{
  1477.             if ($length $headerLength - 1 < 8{
  1478.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1479.             }
  1480.             $commentLength strpos(substr($data$headerLength)chr(0));
  1481.             if (false === $commentLength || $length $headerLength $commentLength - 1 < 8{
  1482.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1483.             }
  1484.             $headerLength += $commentLength + 1;
  1485.         }
  1486.         // have a CRC for header. let's check
  1487.         if ($flags 1{
  1488.             if ($length $headerLength - 2 < 8{
  1489.                 return PEAR::raiseError('_decodeGzip(): data too short'HTTP_REQUEST_ERROR_GZIP_DATA);
  1490.             }
  1491.             $crcReal   = 0xffff crc32(substr($data0$headerLength));
  1492.             $crcStored unpack('v'substr($data$headerLength2));
  1493.             if ($crcReal != $crcStored[1]{
  1494.                 return PEAR::raiseError('_decodeGzip(): header CRC check failed'HTTP_REQUEST_ERROR_GZIP_CRC);
  1495.             }
  1496.             $headerLength += 2;
  1497.         }
  1498.         // unpacked data CRC and size at the end of encoded data
  1499.         $tmp unpack('V2'substr($data-8));
  1500.         $dataCrc  $tmp[1];
  1501.         $dataSize $tmp[2];
  1502.  
  1503.         // finally, call the gzinflate() function
  1504.         // don't pass $dataSize to gzinflate, see bugs #13135, #14370
  1505.         $unpacked gzinflate(substr($data$headerLength-8));
  1506.         if (false === $unpacked{
  1507.             return PEAR::raiseError('_decodeGzip(): gzinflate() call failed'HTTP_REQUEST_ERROR_GZIP_READ);
  1508.         elseif ($dataSize != strlen($unpacked)) {
  1509.             return PEAR::raiseError('_decodeGzip(): data size check failed'HTTP_REQUEST_ERROR_GZIP_READ);
  1510.         elseif ((0xffffffff $dataCrc!= (0xffffffff crc32($unpacked))) {
  1511.             return PEAR::raiseError('_decodeGzip(): data CRC check failed'HTTP_REQUEST_ERROR_GZIP_CRC);
  1512.         }
  1513.         if (HTTP_REQUEST_MBSTRING{
  1514.             mb_internal_encoding($oldEncoding);
  1515.         }
  1516.         return $unpacked;
  1517.     }
  1518. // End class HTTP_Response
  1519. ?>

Documentation generated on Mon, 17 Nov 2008 09:00:23 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.