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

Documentation generated on Mon, 11 Mar 2019 14:47:58 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.