Changeset 6912 for temp/trunk/data


Ignore:
Timestamp:
2006/10/26 18:39:36 (20 years ago)
Author:
kakinaka
Message:

* empty log message *

Location:
temp/trunk/data/module
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • temp/trunk/data/module/Net/Socket.php

    r6910 r6912  
    2020// $Id$ 
    2121 
    22 if(!defined('SOCKET_PHP_DIR')) { 
    23     $SOCKET_PHP_DIR = realpath(dirname( __FILE__)); 
    24     define("SOCKET_PHP_DIR", $SOCKET_PHP_DIR);   
    25 } 
    26 require_once SOCKET_PHP_DIR. '/../PEAR.php'; 
     22require_once '../PEAR.php'; 
    2723 
    2824define('NET_SOCKET_READ',  1); 
  • temp/trunk/data/module/Request.php

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