forked from GNUsocial/gnu-social
		
	Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
This commit is contained in:
		@@ -47,18 +47,15 @@ class File_redirection extends Memcached_DataObject
 | 
			
		||||
    /* the code above is auto generated do not remove the tag below */
 | 
			
		||||
    ###END_AUTOCODE
 | 
			
		||||
 | 
			
		||||
    function _commonCurl($url, $redirs) {
 | 
			
		||||
        $curlh = curl_init();
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_URL, $url);
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_AUTOREFERER, true); // # setup referer header when folowing redirects
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 10); // # seconds to wait
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_MAXREDIRS, $redirs); // # max number of http redirections to follow
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_USERAGENT, USER_AGENT);
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_FILETIME, true);
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_HEADER, true); // Include header in output
 | 
			
		||||
        return $curlh;
 | 
			
		||||
    static function _commonHttp($url, $redirs) {
 | 
			
		||||
        $request = new HTTPClient($url);
 | 
			
		||||
        $request->setConfig(array(
 | 
			
		||||
            'connect_timeout' => 10, // # seconds to wait
 | 
			
		||||
            'max_redirs' => $redirs, // # max number of http redirections to follow
 | 
			
		||||
            'follow_redirects' => true, // Follow redirects
 | 
			
		||||
            'store_body' => false, // We won't need body content here.
 | 
			
		||||
        ));
 | 
			
		||||
        return $request;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _redirectWhere_imp($short_url, $redirs = 10, $protected = false) {
 | 
			
		||||
@@ -82,32 +79,39 @@ class File_redirection extends Memcached_DataObject
 | 
			
		||||
        if(strpos($short_url,'://') === false){
 | 
			
		||||
            return $short_url;
 | 
			
		||||
        }
 | 
			
		||||
        $curlh = File_redirection::_commonCurl($short_url, $redirs);
 | 
			
		||||
        try {
 | 
			
		||||
            $request = self::_commonHttp($short_url, $redirs);
 | 
			
		||||
            // Don't include body in output
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_NOBODY, true);
 | 
			
		||||
        curl_exec($curlh);
 | 
			
		||||
        $info = curl_getinfo($curlh);
 | 
			
		||||
        curl_close($curlh);
 | 
			
		||||
            $request->setMethod(HTTP_Request2::METHOD_HEAD);
 | 
			
		||||
            $response = $request->send();
 | 
			
		||||
 | 
			
		||||
        if (405 == $info['http_code']) {
 | 
			
		||||
            $curlh = File_redirection::_commonCurl($short_url, $redirs);
 | 
			
		||||
            curl_exec($curlh);
 | 
			
		||||
            $info = curl_getinfo($curlh);
 | 
			
		||||
            curl_close($curlh);
 | 
			
		||||
            if (405 == $response->getCode()) {
 | 
			
		||||
                // Server doesn't support HEAD method? Can this really happen?
 | 
			
		||||
                // We'll try again as a GET and ignore the response data.
 | 
			
		||||
                $request = self::_commonHttp($short_url, $redirs);
 | 
			
		||||
                $response = $request->send();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            // Invalid URL or failure to reach server
 | 
			
		||||
            return $short_url;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($info['redirect_count']) && File::isProtected($info['url'])) {
 | 
			
		||||
            return File_redirection::_redirectWhere_imp($short_url, $info['redirect_count'] - 1, true);
 | 
			
		||||
        if ($response->getRedirectCount() && File::isProtected($response->getUrl())) {
 | 
			
		||||
            // Bump back up the redirect chain until we find a non-protected URL
 | 
			
		||||
            return self::_redirectWhere_imp($short_url, $response->getRedirectCount() - 1, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ret = array('code' => $info['http_code']
 | 
			
		||||
                , 'redirects' => $info['redirect_count']
 | 
			
		||||
                , 'url' => $info['url']);
 | 
			
		||||
        $ret = array('code' => $response->getCode()
 | 
			
		||||
                , 'redirects' => $response->getRedirectCount()
 | 
			
		||||
                , 'url' => $response->getUrl());
 | 
			
		||||
 | 
			
		||||
        if (!empty($info['content_type'])) $ret['type'] = $info['content_type'];
 | 
			
		||||
        $type = $response->getHeader('Content-Type');
 | 
			
		||||
        if ($type) $ret['type'] = $type;
 | 
			
		||||
        if ($protected) $ret['protected'] = true;
 | 
			
		||||
        if (!empty($info['download_content_length'])) $ret['size'] = $info['download_content_length'];
 | 
			
		||||
        if (isset($info['filetime']) && ($info['filetime'] > 0)) $ret['time'] = $info['filetime'];
 | 
			
		||||
        $size = $request->getHeader('Content-Length'); // @fixme bytes?
 | 
			
		||||
        if ($size) $ret['size'] = $size;
 | 
			
		||||
        $time = $request->getHeader('Last-Modified');
 | 
			
		||||
        if ($time) $ret['time'] = strtotime($time);
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										844
									
								
								extlib/HTTP/Request2.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										844
									
								
								extlib/HTTP/Request2.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,844 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a HTTP request
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Request2.php 278226 2009-04-03 21:32:48Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A class representing an URL as per RFC 3986.
 | 
			
		||||
 */
 | 
			
		||||
require_once 'Net/URL2.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception class for HTTP_Request2 package
 | 
			
		||||
 */ 
 | 
			
		||||
require_once 'HTTP/Request2/Exception.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a HTTP request
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 * @link       http://tools.ietf.org/html/rfc2616#section-5
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2 implements SplSubject
 | 
			
		||||
{
 | 
			
		||||
   /**#@+
 | 
			
		||||
    * Constants for HTTP request methods
 | 
			
		||||
    *
 | 
			
		||||
    * @link http://tools.ietf.org/html/rfc2616#section-5.1.1
 | 
			
		||||
    */
 | 
			
		||||
    const METHOD_OPTIONS = 'OPTIONS';
 | 
			
		||||
    const METHOD_GET     = 'GET';
 | 
			
		||||
    const METHOD_HEAD    = 'HEAD';
 | 
			
		||||
    const METHOD_POST    = 'POST';
 | 
			
		||||
    const METHOD_PUT     = 'PUT';
 | 
			
		||||
    const METHOD_DELETE  = 'DELETE';
 | 
			
		||||
    const METHOD_TRACE   = 'TRACE';
 | 
			
		||||
    const METHOD_CONNECT = 'CONNECT';
 | 
			
		||||
   /**#@-*/
 | 
			
		||||
 | 
			
		||||
   /**#@+
 | 
			
		||||
    * Constants for HTTP authentication schemes 
 | 
			
		||||
    *
 | 
			
		||||
    * @link http://tools.ietf.org/html/rfc2617
 | 
			
		||||
    */
 | 
			
		||||
    const AUTH_BASIC  = 'basic';
 | 
			
		||||
    const AUTH_DIGEST = 'digest';
 | 
			
		||||
   /**#@-*/
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Regular expression used to check for invalid symbols in RFC 2616 tokens
 | 
			
		||||
    * @link http://pear.php.net/bugs/bug.php?id=15630
 | 
			
		||||
    */
 | 
			
		||||
    const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Regular expression used to check for invalid symbols in cookie strings
 | 
			
		||||
    * @link http://pear.php.net/bugs/bug.php?id=15630
 | 
			
		||||
    * @link http://cgi.netscape.com/newsref/std/cookie_spec.html
 | 
			
		||||
    */
 | 
			
		||||
    const REGEXP_INVALID_COOKIE = '/[\s,;]/';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Fileinfo magic database resource
 | 
			
		||||
    * @var  resource
 | 
			
		||||
    * @see  detectMimeType()
 | 
			
		||||
    */
 | 
			
		||||
    private static $_fileinfoDb;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Observers attached to the request (instances of SplObserver)
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $observers = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request URL
 | 
			
		||||
    * @var  Net_URL2
 | 
			
		||||
    */
 | 
			
		||||
    protected $url;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request method
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    protected $method = self::METHOD_GET;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Authentication data
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @see  getAuth()
 | 
			
		||||
    */
 | 
			
		||||
    protected $auth;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request headers
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $headers = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Configuration parameters
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @see  setConfig()
 | 
			
		||||
    */
 | 
			
		||||
    protected $config = array(
 | 
			
		||||
        'adapter'           => 'HTTP_Request2_Adapter_Socket',
 | 
			
		||||
        'connect_timeout'   => 10,
 | 
			
		||||
        'timeout'           => 0,
 | 
			
		||||
        'use_brackets'      => true,
 | 
			
		||||
        'protocol_version'  => '1.1',
 | 
			
		||||
        'buffer_size'       => 16384,
 | 
			
		||||
        'store_body'        => true,
 | 
			
		||||
 | 
			
		||||
        'proxy_host'        => '',
 | 
			
		||||
        'proxy_port'        => '',
 | 
			
		||||
        'proxy_user'        => '',
 | 
			
		||||
        'proxy_password'    => '',
 | 
			
		||||
        'proxy_auth_scheme' => self::AUTH_BASIC,
 | 
			
		||||
 | 
			
		||||
        'ssl_verify_peer'   => true,
 | 
			
		||||
        'ssl_verify_host'   => true,
 | 
			
		||||
        'ssl_cafile'        => null,
 | 
			
		||||
        'ssl_capath'        => null,
 | 
			
		||||
        'ssl_local_cert'    => null,
 | 
			
		||||
        'ssl_passphrase'    => null,
 | 
			
		||||
 | 
			
		||||
        'digest_compat_ie'  => false
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Last event in request / response handling, intended for observers
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @see  getLastEvent()
 | 
			
		||||
    */
 | 
			
		||||
    protected $lastEvent = array(
 | 
			
		||||
        'name' => 'start',
 | 
			
		||||
        'data' => null
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request body
 | 
			
		||||
    * @var  string|resource
 | 
			
		||||
    * @see  setBody()
 | 
			
		||||
    */
 | 
			
		||||
    protected $body = '';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Array of POST parameters
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $postParams = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Array of file uploads (for multipart/form-data POST requests) 
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $uploads = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adapter used to perform actual HTTP request
 | 
			
		||||
    * @var  HTTP_Request2_Adapter
 | 
			
		||||
    */
 | 
			
		||||
    protected $adapter;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Constructor. Can set request URL, method and configuration array.
 | 
			
		||||
    *
 | 
			
		||||
    * Also sets a default value for User-Agent header. 
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|Net_Url2     Request URL
 | 
			
		||||
    * @param    string              Request method
 | 
			
		||||
    * @param    array               Configuration for this Request instance
 | 
			
		||||
    */
 | 
			
		||||
    public function __construct($url = null, $method = self::METHOD_GET, array $config = array())
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($url)) {
 | 
			
		||||
            $this->setUrl($url);
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($method)) {
 | 
			
		||||
            $this->setMethod($method);
 | 
			
		||||
        }
 | 
			
		||||
        $this->setConfig($config);
 | 
			
		||||
        $this->setHeader('user-agent', 'HTTP_Request2/0.4.1 ' .
 | 
			
		||||
                         '(http://pear.php.net/package/http_request2) ' .
 | 
			
		||||
                         'PHP/' . phpversion());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the URL for this request
 | 
			
		||||
    *
 | 
			
		||||
    * If the URL has userinfo part (username & password) these will be removed
 | 
			
		||||
    * and converted to auth data. If the URL does not have a path component,
 | 
			
		||||
    * that will be set to '/'.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|Net_URL2 Request URL
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function setUrl($url)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($url)) {
 | 
			
		||||
            $url = new Net_URL2($url);
 | 
			
		||||
        }
 | 
			
		||||
        if (!$url instanceof Net_URL2) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Parameter is not a valid HTTP URL');
 | 
			
		||||
        }
 | 
			
		||||
        // URL contains username / password?
 | 
			
		||||
        if ($url->getUserinfo()) {
 | 
			
		||||
            $username = $url->getUser();
 | 
			
		||||
            $password = $url->getPassword();
 | 
			
		||||
            $this->setAuth(rawurldecode($username), $password? rawurldecode($password): '');
 | 
			
		||||
            $url->setUserinfo('');
 | 
			
		||||
        }
 | 
			
		||||
        if ('' == $url->getPath()) {
 | 
			
		||||
            $url->setPath('/');
 | 
			
		||||
        }
 | 
			
		||||
        $this->url = $url;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the request URL
 | 
			
		||||
    *
 | 
			
		||||
    * @return   Net_URL2
 | 
			
		||||
    */
 | 
			
		||||
    public function getUrl()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the request method
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception if the method name is invalid
 | 
			
		||||
    */
 | 
			
		||||
    public function setMethod($method)
 | 
			
		||||
    {
 | 
			
		||||
        // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1
 | 
			
		||||
        if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception("Invalid request method '{$method}'");
 | 
			
		||||
        }
 | 
			
		||||
        $this->method = $method;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the request method
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    */
 | 
			
		||||
    public function getMethod()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the configuration parameter(s)
 | 
			
		||||
    *
 | 
			
		||||
    * The following parameters are available:
 | 
			
		||||
    * <ul>
 | 
			
		||||
    *   <li> 'adapter'           - adapter to use (string)</li>
 | 
			
		||||
    *   <li> 'connect_timeout'   - Connection timeout in seconds (integer)</li>
 | 
			
		||||
    *   <li> 'timeout'           - Total number of seconds a request can take.
 | 
			
		||||
    *                              Use 0 for no limit, should be greater than 
 | 
			
		||||
    *                              'connect_timeout' if set (integer)</li>
 | 
			
		||||
    *   <li> 'use_brackets'      - Whether to append [] to array variable names (bool)</li>
 | 
			
		||||
    *   <li> 'protocol_version'  - HTTP Version to use, '1.0' or '1.1' (string)</li>
 | 
			
		||||
    *   <li> 'buffer_size'       - Buffer size to use for reading and writing (int)</li>
 | 
			
		||||
    *   <li> 'store_body'        - Whether to store response body in response object.
 | 
			
		||||
    *                              Set to false if receiving a huge response and
 | 
			
		||||
    *                              using an Observer to save it (boolean)</li>
 | 
			
		||||
    *   <li> 'proxy_host'        - Proxy server host (string)</li>
 | 
			
		||||
    *   <li> 'proxy_port'        - Proxy server port (integer)</li>
 | 
			
		||||
    *   <li> 'proxy_user'        - Proxy auth username (string)</li>
 | 
			
		||||
    *   <li> 'proxy_password'    - Proxy auth password (string)</li>
 | 
			
		||||
    *   <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li>
 | 
			
		||||
    *   <li> 'ssl_verify_peer'   - Whether to verify peer's SSL certificate (bool)</li>
 | 
			
		||||
    *   <li> 'ssl_verify_host'   - Whether to check that Common Name in SSL
 | 
			
		||||
    *                              certificate matches host name (bool)</li>
 | 
			
		||||
    *   <li> 'ssl_cafile'        - Cerificate Authority file to verify the peer
 | 
			
		||||
    *                              with (use with 'ssl_verify_peer') (string)</li>
 | 
			
		||||
    *   <li> 'ssl_capath'        - Directory holding multiple Certificate 
 | 
			
		||||
    *                              Authority files (string)</li>
 | 
			
		||||
    *   <li> 'ssl_local_cert'    - Name of a file containing local cerificate (string)</li>
 | 
			
		||||
    *   <li> 'ssl_passphrase'    - Passphrase with which local certificate
 | 
			
		||||
    *                              was encoded (string)</li>
 | 
			
		||||
    *   <li> 'digest_compat_ie'  - Whether to imitate behaviour of MSIE 5 and 6
 | 
			
		||||
    *                              in using URL without query string in digest
 | 
			
		||||
    *                              authentication (boolean)</li>
 | 
			
		||||
    * </ul>
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|array    configuration parameter name or array
 | 
			
		||||
    *                           ('parameter name' => 'parameter value')
 | 
			
		||||
    * @param    mixed           parameter value if $nameOrConfig is not an array
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception If the parameter is unknown
 | 
			
		||||
    */
 | 
			
		||||
    public function setConfig($nameOrConfig, $value = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array($nameOrConfig)) {
 | 
			
		||||
            foreach ($nameOrConfig as $name => $value) {
 | 
			
		||||
                $this->setConfig($name, $value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!array_key_exists($nameOrConfig, $this->config)) {
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    "Unknown configuration parameter '{$nameOrConfig}'"
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            $this->config[$nameOrConfig] = $value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the value(s) of the configuration parameter(s)
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  parameter name
 | 
			
		||||
    * @return   mixed   value of $name parameter, array of all configuration 
 | 
			
		||||
    *                   parameters if $name is not given
 | 
			
		||||
    * @throws   HTTP_Request2_Exception If the parameter is unknown
 | 
			
		||||
    */
 | 
			
		||||
    public function getConfig($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $name) {
 | 
			
		||||
            return $this->config;
 | 
			
		||||
        } elseif (!array_key_exists($name, $this->config)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                "Unknown configuration parameter '{$name}'"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        return $this->config[$name];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the autentification data
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  user name
 | 
			
		||||
    * @param    string  password
 | 
			
		||||
    * @param    string  authentication scheme
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    */ 
 | 
			
		||||
    public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC)
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($user)) {
 | 
			
		||||
            $this->auth = null;
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->auth = array(
 | 
			
		||||
                'user'     => (string)$user,
 | 
			
		||||
                'password' => (string)$password,
 | 
			
		||||
                'scheme'   => $scheme
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the authentication data
 | 
			
		||||
    *
 | 
			
		||||
    * The array has the keys 'user', 'password' and 'scheme', where 'scheme'
 | 
			
		||||
    * is one of the HTTP_Request2::AUTH_* constants.
 | 
			
		||||
    *
 | 
			
		||||
    * @return   array
 | 
			
		||||
    */
 | 
			
		||||
    public function getAuth()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->auth;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets request header(s)
 | 
			
		||||
    *
 | 
			
		||||
    * The first parameter may be either a full header string 'header: value' or
 | 
			
		||||
    * header name. In the former case $value parameter is ignored, in the latter 
 | 
			
		||||
    * the header's value will either be set to $value or the header will be
 | 
			
		||||
    * removed if $value is null. The first parameter can also be an array of
 | 
			
		||||
    * headers, in that case method will be called recursively.
 | 
			
		||||
    *
 | 
			
		||||
    * Note that headers are treated case insensitively as per RFC 2616.
 | 
			
		||||
    * 
 | 
			
		||||
    * <code>
 | 
			
		||||
    * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar'
 | 
			
		||||
    * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz'
 | 
			
		||||
    * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux'
 | 
			
		||||
    * $req->setHeader('FOO'); // removes 'Foo' header from request
 | 
			
		||||
    * </code>
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|array    header name, header string ('Header: value')
 | 
			
		||||
    *                           or an array of headers
 | 
			
		||||
    * @param    string|null     header value, header will be removed if null
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function setHeader($name, $value = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array($name)) {
 | 
			
		||||
            foreach ($name as $k => $v) {
 | 
			
		||||
                if (is_string($k)) {
 | 
			
		||||
                    $this->setHeader($k, $v);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $this->setHeader($v);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (null === $value && strpos($name, ':')) {
 | 
			
		||||
                list($name, $value) = array_map('trim', explode(':', $name, 2));
 | 
			
		||||
            }
 | 
			
		||||
            // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2
 | 
			
		||||
            if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) {
 | 
			
		||||
                throw new HTTP_Request2_Exception("Invalid header name '{$name}'");
 | 
			
		||||
            }
 | 
			
		||||
            // Header names are case insensitive anyway
 | 
			
		||||
            $name = strtolower($name);
 | 
			
		||||
            if (null === $value) {
 | 
			
		||||
                unset($this->headers[$name]);
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->headers[$name] = $value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the request headers
 | 
			
		||||
    *
 | 
			
		||||
    * The array is of the form ('header name' => 'header value'), header names
 | 
			
		||||
    * are lowercased
 | 
			
		||||
    *
 | 
			
		||||
    * @return   array
 | 
			
		||||
    */
 | 
			
		||||
    public function getHeaders()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->headers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Appends a cookie to "Cookie:" header
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  cookie name
 | 
			
		||||
    * @param    string  cookie value
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function addCookie($name, $value)
 | 
			
		||||
    {
 | 
			
		||||
        $cookie = $name . '=' . $value;
 | 
			
		||||
        if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception("Invalid cookie: '{$cookie}'");
 | 
			
		||||
        }
 | 
			
		||||
        $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; ';
 | 
			
		||||
        $this->setHeader('cookie', $cookies . $cookie);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the request body
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  Either a string with the body or filename containing body
 | 
			
		||||
    * @param    bool    Whether first parameter is a filename
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function setBody($body, $isFilename = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$isFilename) {
 | 
			
		||||
            $this->body = (string)$body;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!($fp = @fopen($body, 'rb'))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception("Cannot open file {$body}");
 | 
			
		||||
            }
 | 
			
		||||
            $this->body = $fp;
 | 
			
		||||
            if (empty($this->headers['content-type'])) {
 | 
			
		||||
                $this->setHeader('content-type', self::detectMimeType($body));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the request body
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string|resource|HTTP_Request2_MultipartBody
 | 
			
		||||
    */
 | 
			
		||||
    public function getBody()
 | 
			
		||||
    {
 | 
			
		||||
        if (self::METHOD_POST == $this->method && 
 | 
			
		||||
            (!empty($this->postParams) || !empty($this->uploads))
 | 
			
		||||
        ) {
 | 
			
		||||
            if ('application/x-www-form-urlencoded' == $this->headers['content-type']) {
 | 
			
		||||
                $body = http_build_query($this->postParams, '', '&');
 | 
			
		||||
                if (!$this->getConfig('use_brackets')) {
 | 
			
		||||
                    $body = preg_replace('/%5B\d+%5D=/', '=', $body);
 | 
			
		||||
                }
 | 
			
		||||
                // support RFC 3986 by not encoding '~' symbol (request #15368)
 | 
			
		||||
                return str_replace('%7E', '~', $body);
 | 
			
		||||
 | 
			
		||||
            } elseif ('multipart/form-data' == $this->headers['content-type']) {
 | 
			
		||||
                require_once 'HTTP/Request2/MultipartBody.php';
 | 
			
		||||
                return new HTTP_Request2_MultipartBody(
 | 
			
		||||
                    $this->postParams, $this->uploads, $this->getConfig('use_brackets')
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $this->body;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adds a file to form-based file upload
 | 
			
		||||
    *
 | 
			
		||||
    * Used to emulate file upload via a HTML form. The method also sets
 | 
			
		||||
    * Content-Type of HTTP request to 'multipart/form-data'.
 | 
			
		||||
    *
 | 
			
		||||
    * If you just want to send the contents of a file as the body of HTTP
 | 
			
		||||
    * request you should use setBody() method.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  name of file-upload field
 | 
			
		||||
    * @param    mixed   full name of local file
 | 
			
		||||
    * @param    string  filename to send in the request 
 | 
			
		||||
    * @param    string  content-type of file being uploaded
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function addUpload($fieldName, $filename, $sendFilename = null,
 | 
			
		||||
                              $contentType = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (!is_array($filename)) {
 | 
			
		||||
            if (!($fp = @fopen($filename, 'rb'))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception("Cannot open file {$filename}");
 | 
			
		||||
            }
 | 
			
		||||
            $this->uploads[$fieldName] = array(
 | 
			
		||||
                'fp'        => $fp,
 | 
			
		||||
                'filename'  => empty($sendFilename)? basename($filename): $sendFilename,
 | 
			
		||||
                'size'      => filesize($filename),
 | 
			
		||||
                'type'      => empty($contentType)? self::detectMimeType($filename): $contentType
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            $fps = $names = $sizes = $types = array();
 | 
			
		||||
            foreach ($filename as $f) {
 | 
			
		||||
                if (!is_array($f)) {
 | 
			
		||||
                    $f = array($f);
 | 
			
		||||
                }
 | 
			
		||||
                if (!($fp = @fopen($f[0], 'rb'))) {
 | 
			
		||||
                    throw new HTTP_Request2_Exception("Cannot open file {$f[0]}");
 | 
			
		||||
                }
 | 
			
		||||
                $fps[]   = $fp;
 | 
			
		||||
                $names[] = empty($f[1])? basename($f[0]): $f[1];
 | 
			
		||||
                $sizes[] = filesize($f[0]);
 | 
			
		||||
                $types[] = empty($f[2])? self::detectMimeType($f[0]): $f[2];
 | 
			
		||||
            }
 | 
			
		||||
            $this->uploads[$fieldName] = array(
 | 
			
		||||
                'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($this->headers['content-type']) ||
 | 
			
		||||
            'application/x-www-form-urlencoded' == $this->headers['content-type']
 | 
			
		||||
        ) {
 | 
			
		||||
            $this->setHeader('content-type', 'multipart/form-data');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adds POST parameter(s) to the request.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|array    parameter name or array ('name' => 'value')
 | 
			
		||||
    * @param    mixed           parameter value (can be an array)
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    */
 | 
			
		||||
    public function addPostParameter($name, $value = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (!is_array($name)) {
 | 
			
		||||
            $this->postParams[$name] = $value;
 | 
			
		||||
        } else {
 | 
			
		||||
            foreach ($name as $k => $v) {
 | 
			
		||||
                $this->addPostParameter($k, $v);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($this->headers['content-type'])) {
 | 
			
		||||
            $this->setHeader('content-type', 'application/x-www-form-urlencoded');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Attaches a new observer
 | 
			
		||||
    *
 | 
			
		||||
    * @param    SplObserver
 | 
			
		||||
    */
 | 
			
		||||
    public function attach(SplObserver $observer)
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->observers as $attached) {
 | 
			
		||||
            if ($attached === $observer) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $this->observers[] = $observer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Detaches an existing observer
 | 
			
		||||
    *
 | 
			
		||||
    * @param    SplObserver
 | 
			
		||||
    */
 | 
			
		||||
    public function detach(SplObserver $observer)
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->observers as $key => $attached) {
 | 
			
		||||
            if ($attached === $observer) {
 | 
			
		||||
                unset($this->observers[$key]);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Notifies all observers
 | 
			
		||||
    */
 | 
			
		||||
    public function notify()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->observers as $observer) {
 | 
			
		||||
            $observer->update($this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the last event
 | 
			
		||||
    *
 | 
			
		||||
    * Adapters should use this method to set the current state of the request
 | 
			
		||||
    * and notify the observers.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  event name
 | 
			
		||||
    * @param    mixed   event data
 | 
			
		||||
    */
 | 
			
		||||
    public function setLastEvent($name, $data = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->lastEvent = array(
 | 
			
		||||
            'name' => $name,
 | 
			
		||||
            'data' => $data
 | 
			
		||||
        );
 | 
			
		||||
        $this->notify();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the last event
 | 
			
		||||
    *
 | 
			
		||||
    * Observers should use this method to access the last change in request.
 | 
			
		||||
    * The following event names are possible:
 | 
			
		||||
    * <ul>
 | 
			
		||||
    *   <li>'connect'                 - after connection to remote server,
 | 
			
		||||
    *                                   data is the destination (string)</li>
 | 
			
		||||
    *   <li>'disconnect'              - after disconnection from server</li>
 | 
			
		||||
    *   <li>'sentHeaders'             - after sending the request headers,
 | 
			
		||||
    *                                   data is the headers sent (string)</li>
 | 
			
		||||
    *   <li>'sentBodyPart'            - after sending a part of the request body, 
 | 
			
		||||
    *                                   data is the length of that part (int)</li>
 | 
			
		||||
    *   <li>'receivedHeaders'         - after receiving the response headers,
 | 
			
		||||
    *                                   data is HTTP_Request2_Response object</li>
 | 
			
		||||
    *   <li>'receivedBodyPart'        - after receiving a part of the response
 | 
			
		||||
    *                                   body, data is that part (string)</li>
 | 
			
		||||
    *   <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still
 | 
			
		||||
    *                                   encoded by Content-Encoding</li>
 | 
			
		||||
    *   <li>'receivedBody'            - after receiving the complete response
 | 
			
		||||
    *                                   body, data is HTTP_Request2_Response object</li>
 | 
			
		||||
    * </ul>
 | 
			
		||||
    * Different adapters may not send all the event types. Mock adapter does
 | 
			
		||||
    * not send any events to the observers.
 | 
			
		||||
    *
 | 
			
		||||
    * @return   array   The array has two keys: 'name' and 'data'
 | 
			
		||||
    */
 | 
			
		||||
    public function getLastEvent()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->lastEvent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the adapter used to actually perform the request
 | 
			
		||||
    *
 | 
			
		||||
    * You can pass either an instance of a class implementing HTTP_Request2_Adapter
 | 
			
		||||
    * or a class name. The method will only try to include a file if the class
 | 
			
		||||
    * name starts with HTTP_Request2_Adapter_, it will also try to prepend this
 | 
			
		||||
    * prefix to the class name if it doesn't contain any underscores, so that
 | 
			
		||||
    * <code>
 | 
			
		||||
    * $request->setAdapter('curl');
 | 
			
		||||
    * </code>
 | 
			
		||||
    * will work.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string|HTTP_Request2_Adapter
 | 
			
		||||
    * @return   HTTP_Request2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function setAdapter($adapter)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($adapter)) {
 | 
			
		||||
            if (!class_exists($adapter, false)) {
 | 
			
		||||
                if (false === strpos($adapter, '_')) {
 | 
			
		||||
                    $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter);
 | 
			
		||||
                }
 | 
			
		||||
                if (preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)) {
 | 
			
		||||
                    include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php';
 | 
			
		||||
                }
 | 
			
		||||
                if (!class_exists($adapter, false)) {
 | 
			
		||||
                    throw new HTTP_Request2_Exception("Class {$adapter} not found");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $adapter = new $adapter;
 | 
			
		||||
        }
 | 
			
		||||
        if (!$adapter instanceof HTTP_Request2_Adapter) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Parameter is not a HTTP request adapter');
 | 
			
		||||
        }
 | 
			
		||||
        $this->adapter = $adapter;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sends the request and returns the response
 | 
			
		||||
    *
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    */
 | 
			
		||||
    public function send()
 | 
			
		||||
    {
 | 
			
		||||
        // Sanity check for URL
 | 
			
		||||
        if (!$this->url instanceof Net_URL2) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('No URL given');
 | 
			
		||||
        } elseif (!$this->url->isAbsolute()) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Absolute URL required');
 | 
			
		||||
        } elseif (!in_array(strtolower($this->url->getScheme()), array('https', 'http'))) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Not a HTTP URL');
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($this->adapter)) {
 | 
			
		||||
            $this->setAdapter($this->getConfig('adapter'));
 | 
			
		||||
        }
 | 
			
		||||
        // magic_quotes_runtime may break file uploads and chunked response
 | 
			
		||||
        // processing; see bug #4543
 | 
			
		||||
        if ($magicQuotes = ini_get('magic_quotes_runtime')) {
 | 
			
		||||
            ini_set('magic_quotes_runtime', false);
 | 
			
		||||
        }
 | 
			
		||||
        // force using single byte encoding if mbstring extension overloads
 | 
			
		||||
        // strlen() and substr(); see bug #1781, bug #10605
 | 
			
		||||
        if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
 | 
			
		||||
            $oldEncoding = mb_internal_encoding();
 | 
			
		||||
            mb_internal_encoding('iso-8859-1');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->adapter->sendRequest($this);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
        }
 | 
			
		||||
        // cleanup in either case (poor man's "finally" clause)
 | 
			
		||||
        if ($magicQuotes) {
 | 
			
		||||
            ini_set('magic_quotes_runtime', true);
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($oldEncoding)) {
 | 
			
		||||
            mb_internal_encoding($oldEncoding);
 | 
			
		||||
        }
 | 
			
		||||
        // rethrow the exception
 | 
			
		||||
        if (!empty($e)) {
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Tries to detect MIME type of a file
 | 
			
		||||
    *
 | 
			
		||||
    * The method will try to use fileinfo extension if it is available,
 | 
			
		||||
    * deprecated mime_content_type() function in the other case. If neither
 | 
			
		||||
    * works, default 'application/octet-stream' MIME type is returned
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  filename
 | 
			
		||||
    * @return   string  file MIME type
 | 
			
		||||
    */
 | 
			
		||||
    protected static function detectMimeType($filename)
 | 
			
		||||
    {
 | 
			
		||||
        // finfo extension from PECL available 
 | 
			
		||||
        if (function_exists('finfo_open')) {
 | 
			
		||||
            if (!isset(self::$_fileinfoDb)) {
 | 
			
		||||
                self::$_fileinfoDb = @finfo_open(FILEINFO_MIME);
 | 
			
		||||
            }
 | 
			
		||||
            if (self::$_fileinfoDb) { 
 | 
			
		||||
                $info = finfo_file(self::$_fileinfoDb, $filename);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // (deprecated) mime_content_type function available
 | 
			
		||||
        if (empty($info) && function_exists('mime_content_type')) {
 | 
			
		||||
            return mime_content_type($filename);
 | 
			
		||||
        }
 | 
			
		||||
        return empty($info)? 'application/octet-stream': $info;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										152
									
								
								extlib/HTTP/Request2/Adapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								extlib/HTTP/Request2/Adapter.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for HTTP_Request2 adapters
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Adapter.php 274684 2009-01-26 23:07:27Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a HTTP response
 | 
			
		||||
 */
 | 
			
		||||
require_once 'HTTP/Request2/Response.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for HTTP_Request2 adapters
 | 
			
		||||
 *
 | 
			
		||||
 * HTTP_Request2 class itself only defines methods for aggregating the request
 | 
			
		||||
 * data, all actual work of sending the request to the remote server and 
 | 
			
		||||
 * receiving its response is performed by adapters.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 */
 | 
			
		||||
abstract class HTTP_Request2_Adapter
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * A list of methods that MUST NOT have a request body, per RFC 2616
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected static $bodyDisallowed = array('TRACE');
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Methods having defined semantics for request body
 | 
			
		||||
    *
 | 
			
		||||
    * Content-Length header (indicating that the body follows, section 4.3 of
 | 
			
		||||
    * RFC 2616) will be sent for these methods even if no body was added
 | 
			
		||||
    *
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @link http://pear.php.net/bugs/bug.php?id=12900
 | 
			
		||||
    * @link http://pear.php.net/bugs/bug.php?id=14740
 | 
			
		||||
    */
 | 
			
		||||
    protected static $bodyRequired = array('POST', 'PUT');
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request being sent
 | 
			
		||||
    * @var  HTTP_Request2
 | 
			
		||||
    */
 | 
			
		||||
    protected $request;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Request body
 | 
			
		||||
    * @var  string|resource|HTTP_Request2_MultipartBody
 | 
			
		||||
    * @see  HTTP_Request2::getBody()
 | 
			
		||||
    */
 | 
			
		||||
    protected $requestBody;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Length of the request body
 | 
			
		||||
    * @var  integer
 | 
			
		||||
    */
 | 
			
		||||
    protected $contentLength;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sends request to the remote server and returns its response
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    abstract public function sendRequest(HTTP_Request2 $request);
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Calculates length of the request body, adds proper headers
 | 
			
		||||
    *
 | 
			
		||||
    * @param    array   associative array of request headers, this method will 
 | 
			
		||||
    *                   add proper 'Content-Length' and 'Content-Type' headers 
 | 
			
		||||
    *                   to this array (or remove them if not needed)
 | 
			
		||||
    */
 | 
			
		||||
    protected function calculateRequestLength(&$headers)
 | 
			
		||||
    {
 | 
			
		||||
        $this->requestBody = $this->request->getBody();
 | 
			
		||||
 | 
			
		||||
        if (is_string($this->requestBody)) {
 | 
			
		||||
            $this->contentLength = strlen($this->requestBody);
 | 
			
		||||
        } elseif (is_resource($this->requestBody)) {
 | 
			
		||||
            $stat = fstat($this->requestBody);
 | 
			
		||||
            $this->contentLength = $stat['size'];
 | 
			
		||||
            rewind($this->requestBody);
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->contentLength = $this->requestBody->getLength();
 | 
			
		||||
            $headers['content-type'] = 'multipart/form-data; boundary=' .
 | 
			
		||||
                                       $this->requestBody->getBoundary();
 | 
			
		||||
            $this->requestBody->rewind();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
 | 
			
		||||
            0 == $this->contentLength
 | 
			
		||||
        ) {
 | 
			
		||||
            unset($headers['content-type']);
 | 
			
		||||
            // No body: send a Content-Length header nonetheless (request #12900),
 | 
			
		||||
            // but do that only for methods that require a body (bug #14740)
 | 
			
		||||
            if (in_array($this->request->getMethod(), self::$bodyRequired)) {
 | 
			
		||||
                $headers['content-length'] = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                unset($headers['content-length']);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (empty($headers['content-type'])) {
 | 
			
		||||
                $headers['content-type'] = 'application/x-www-form-urlencoded';
 | 
			
		||||
            }
 | 
			
		||||
            $headers['content-length'] = $this->contentLength;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										383
									
								
								extlib/HTTP/Request2/Adapter/Curl.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								extlib/HTTP/Request2/Adapter/Curl.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,383 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Adapter for HTTP_Request2 wrapping around cURL extension
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Curl.php 278226 2009-04-03 21:32:48Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for HTTP_Request2 adapters
 | 
			
		||||
 */
 | 
			
		||||
require_once 'HTTP/Request2/Adapter.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Adapter for HTTP_Request2 wrapping around cURL extension
 | 
			
		||||
 *
 | 
			
		||||
 * @category    HTTP
 | 
			
		||||
 * @package     HTTP_Request2
 | 
			
		||||
 * @author      Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version     Release: 0.4.1
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * Mapping of header names to cURL options
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected static $headerMap = array(
 | 
			
		||||
        'accept-encoding' => CURLOPT_ENCODING,
 | 
			
		||||
        'cookie'          => CURLOPT_COOKIE,
 | 
			
		||||
        'referer'         => CURLOPT_REFERER,
 | 
			
		||||
        'user-agent'      => CURLOPT_USERAGENT
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Mapping of SSL context options to cURL options
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected static $sslContextMap = array(
 | 
			
		||||
        'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
 | 
			
		||||
        'ssl_cafile'      => CURLOPT_CAINFO,
 | 
			
		||||
        'ssl_capath'      => CURLOPT_CAPATH,
 | 
			
		||||
        'ssl_local_cert'  => CURLOPT_SSLCERT,
 | 
			
		||||
        'ssl_passphrase'  => CURLOPT_SSLCERTPASSWD
 | 
			
		||||
   );
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Response being received
 | 
			
		||||
    * @var  HTTP_Request2_Response
 | 
			
		||||
    */
 | 
			
		||||
    protected $response;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Whether 'sentHeaders' event was sent to observers
 | 
			
		||||
    * @var  boolean
 | 
			
		||||
    */
 | 
			
		||||
    protected $eventSentHeaders = false;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Whether 'receivedHeaders' event was sent to observers
 | 
			
		||||
    * @var boolean
 | 
			
		||||
    */
 | 
			
		||||
    protected $eventReceivedHeaders = false;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Position within request body
 | 
			
		||||
    * @var  integer
 | 
			
		||||
    * @see  callbackReadBody()
 | 
			
		||||
    */
 | 
			
		||||
    protected $position = 0;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Information about last transfer, as returned by curl_getinfo()
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $lastInfo;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sends request to the remote server and returns its response
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function sendRequest(HTTP_Request2 $request)
 | 
			
		||||
    {
 | 
			
		||||
        if (!extension_loaded('curl')) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('cURL extension not available');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->request              = $request;
 | 
			
		||||
        $this->response             = null;
 | 
			
		||||
        $this->position             = 0;
 | 
			
		||||
        $this->eventSentHeaders     = false;
 | 
			
		||||
        $this->eventReceivedHeaders = false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (false === curl_exec($ch = $this->createCurlHandle())) {
 | 
			
		||||
                $errorMessage = 'Error sending request: #' . curl_errno($ch) .
 | 
			
		||||
                                                       ' ' . curl_error($ch);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
        }
 | 
			
		||||
        $this->lastInfo = curl_getinfo($ch);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        if (!empty($e)) {
 | 
			
		||||
            throw $e;
 | 
			
		||||
        } elseif (!empty($errorMessage)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception($errorMessage);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (0 < $this->lastInfo['size_download']) {
 | 
			
		||||
            $this->request->setLastEvent('receivedBody', $this->response);
 | 
			
		||||
        }
 | 
			
		||||
        return $this->response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns information about last transfer
 | 
			
		||||
    *
 | 
			
		||||
    * @return   array   associative array as returned by curl_getinfo()
 | 
			
		||||
    */
 | 
			
		||||
    public function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->lastInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Creates a new cURL handle and populates it with data from the request
 | 
			
		||||
    *
 | 
			
		||||
    * @return   resource    a cURL handle, as created by curl_init()
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function createCurlHandle()
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init();
 | 
			
		||||
 | 
			
		||||
        curl_setopt_array($ch, array(
 | 
			
		||||
            // setup callbacks
 | 
			
		||||
            CURLOPT_READFUNCTION   => array($this, 'callbackReadBody'),
 | 
			
		||||
            CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),
 | 
			
		||||
            CURLOPT_WRITEFUNCTION  => array($this, 'callbackWriteBody'),
 | 
			
		||||
            // disallow redirects
 | 
			
		||||
            CURLOPT_FOLLOWLOCATION => false,
 | 
			
		||||
            // buffer size
 | 
			
		||||
            CURLOPT_BUFFERSIZE     => $this->request->getConfig('buffer_size'),
 | 
			
		||||
            // connection timeout
 | 
			
		||||
            CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),
 | 
			
		||||
            // save full outgoing headers, in case someone is interested
 | 
			
		||||
            CURLINFO_HEADER_OUT    => true,
 | 
			
		||||
            // request url
 | 
			
		||||
            CURLOPT_URL            => $this->request->getUrl()->getUrl()
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // request timeout
 | 
			
		||||
        if ($timeout = $this->request->getConfig('timeout')) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set HTTP version
 | 
			
		||||
        switch ($this->request->getConfig('protocol_version')) {
 | 
			
		||||
            case '1.0':
 | 
			
		||||
                curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
 | 
			
		||||
                break;
 | 
			
		||||
            case '1.1':
 | 
			
		||||
                curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set request method
 | 
			
		||||
        switch ($this->request->getMethod()) {
 | 
			
		||||
            case HTTP_Request2::METHOD_GET:
 | 
			
		||||
                curl_setopt($ch, CURLOPT_HTTPGET, true);
 | 
			
		||||
                break;
 | 
			
		||||
            case HTTP_Request2::METHOD_POST:
 | 
			
		||||
                curl_setopt($ch, CURLOPT_POST, true);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set proxy, if needed
 | 
			
		||||
        if ($host = $this->request->getConfig('proxy_host')) {
 | 
			
		||||
            if (!($port = $this->request->getConfig('proxy_port'))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Proxy port not provided');
 | 
			
		||||
            }
 | 
			
		||||
            curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);
 | 
			
		||||
            if ($user = $this->request->getConfig('proxy_user')) {
 | 
			
		||||
                curl_setopt($ch, CURLOPT_PROXYUSERPWD, $user . ':' .
 | 
			
		||||
                            $this->request->getConfig('proxy_password'));
 | 
			
		||||
                switch ($this->request->getConfig('proxy_auth_scheme')) {
 | 
			
		||||
                    case HTTP_Request2::AUTH_BASIC:
 | 
			
		||||
                        curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case HTTP_Request2::AUTH_DIGEST:
 | 
			
		||||
                        curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set authentication data
 | 
			
		||||
        if ($auth = $this->request->getAuth()) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
 | 
			
		||||
            switch ($auth['scheme']) {
 | 
			
		||||
                case HTTP_Request2::AUTH_BASIC:
 | 
			
		||||
                    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 | 
			
		||||
                    break;
 | 
			
		||||
                case HTTP_Request2::AUTH_DIGEST:
 | 
			
		||||
                    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set SSL options
 | 
			
		||||
        if (0 == strcasecmp($this->request->getUrl()->getScheme(), 'https')) {
 | 
			
		||||
            foreach ($this->request->getConfig() as $name => $value) {
 | 
			
		||||
                if ('ssl_verify_host' == $name && null !== $value) {
 | 
			
		||||
                    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);
 | 
			
		||||
                } elseif (isset(self::$sslContextMap[$name]) && null !== $value) {
 | 
			
		||||
                    curl_setopt($ch, self::$sslContextMap[$name], $value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $headers = $this->request->getHeaders();
 | 
			
		||||
        // make cURL automagically send proper header
 | 
			
		||||
        if (!isset($headers['accept-encoding'])) {
 | 
			
		||||
            $headers['accept-encoding'] = '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // set headers having special cURL keys
 | 
			
		||||
        foreach (self::$headerMap as $name => $option) {
 | 
			
		||||
            if (isset($headers[$name])) {
 | 
			
		||||
                curl_setopt($ch, $option, $headers[$name]);
 | 
			
		||||
                unset($headers[$name]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->calculateRequestLength($headers);
 | 
			
		||||
 | 
			
		||||
        // set headers not having special keys
 | 
			
		||||
        $headersFmt = array();
 | 
			
		||||
        foreach ($headers as $name => $value) {
 | 
			
		||||
            $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
 | 
			
		||||
            $headersFmt[]  = $canonicalName . ': ' . $value;
 | 
			
		||||
        }
 | 
			
		||||
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);
 | 
			
		||||
 | 
			
		||||
        return $ch;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Callback function called by cURL for reading the request body
 | 
			
		||||
    *
 | 
			
		||||
    * @param    resource    cURL handle
 | 
			
		||||
    * @param    resource    file descriptor (not used)
 | 
			
		||||
    * @param    integer     maximum length of data to return
 | 
			
		||||
    * @return   string      part of the request body, up to $length bytes 
 | 
			
		||||
    */
 | 
			
		||||
    protected function callbackReadBody($ch, $fd, $length)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->eventSentHeaders) {
 | 
			
		||||
            $this->request->setLastEvent(
 | 
			
		||||
                'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
 | 
			
		||||
            );
 | 
			
		||||
            $this->eventSentHeaders = true;
 | 
			
		||||
        }
 | 
			
		||||
        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
 | 
			
		||||
            0 == $this->contentLength || $this->position >= $this->contentLength
 | 
			
		||||
        ) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
        if (is_string($this->requestBody)) {
 | 
			
		||||
            $string = substr($this->requestBody, $this->position, $length);
 | 
			
		||||
        } elseif (is_resource($this->requestBody)) {
 | 
			
		||||
            $string = fread($this->requestBody, $length);
 | 
			
		||||
        } else {
 | 
			
		||||
            $string = $this->requestBody->read($length);
 | 
			
		||||
        }
 | 
			
		||||
        $this->request->setLastEvent('sentBodyPart', strlen($string));
 | 
			
		||||
        $this->position += strlen($string);
 | 
			
		||||
        return $string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Callback function called by cURL for saving the response headers
 | 
			
		||||
    *
 | 
			
		||||
    * @param    resource    cURL handle
 | 
			
		||||
    * @param    string      response header (with trailing CRLF)
 | 
			
		||||
    * @return   integer     number of bytes saved
 | 
			
		||||
    * @see      HTTP_Request2_Response::parseHeaderLine()
 | 
			
		||||
    */
 | 
			
		||||
    protected function callbackWriteHeader($ch, $string)
 | 
			
		||||
    {
 | 
			
		||||
        // we may receive a second set of headers if doing e.g. digest auth
 | 
			
		||||
        if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {
 | 
			
		||||
            // don't bother with 100-Continue responses (bug #15785)
 | 
			
		||||
            if (!$this->eventSentHeaders ||
 | 
			
		||||
                $this->response->getStatus() >= 200
 | 
			
		||||
            ) {
 | 
			
		||||
                $this->request->setLastEvent(
 | 
			
		||||
                    'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            $this->eventSentHeaders = true;
 | 
			
		||||
            // we'll need a new response object
 | 
			
		||||
            if ($this->eventReceivedHeaders) {
 | 
			
		||||
                $this->eventReceivedHeaders = false;
 | 
			
		||||
                $this->response             = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($this->response)) {
 | 
			
		||||
            $this->response = new HTTP_Request2_Response($string, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->response->parseHeaderLine($string);
 | 
			
		||||
            if ('' == trim($string)) {
 | 
			
		||||
                // don't bother with 100-Continue responses (bug #15785)
 | 
			
		||||
                if (200 <= $this->response->getStatus()) {
 | 
			
		||||
                    $this->request->setLastEvent('receivedHeaders', $this->response);
 | 
			
		||||
                }
 | 
			
		||||
                $this->eventReceivedHeaders = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return strlen($string);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Callback function called by cURL for saving the response body
 | 
			
		||||
    *
 | 
			
		||||
    * @param    resource    cURL handle (not used)
 | 
			
		||||
    * @param    string      part of the response body
 | 
			
		||||
    * @return   integer     number of bytes saved
 | 
			
		||||
    * @see      HTTP_Request2_Response::appendBody()
 | 
			
		||||
    */
 | 
			
		||||
    protected function callbackWriteBody($ch, $string)
 | 
			
		||||
    {
 | 
			
		||||
        // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if 
 | 
			
		||||
        // response doesn't start with proper HTTP status line (see bug #15716)
 | 
			
		||||
        if (empty($this->response)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception("Malformed response: {$string}");
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->request->getConfig('store_body')) {
 | 
			
		||||
            $this->response->appendBody($string);
 | 
			
		||||
        }
 | 
			
		||||
        $this->request->setLastEvent('receivedBodyPart', $string);
 | 
			
		||||
        return strlen($string);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										171
									
								
								extlib/HTTP/Request2/Adapter/Mock.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								extlib/HTTP/Request2/Adapter/Mock.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Mock adapter intended for testing
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Mock.php 274406 2009-01-23 18:01:57Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for HTTP_Request2 adapters
 | 
			
		||||
 */
 | 
			
		||||
require_once 'HTTP/Request2/Adapter.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Mock adapter intended for testing
 | 
			
		||||
 *
 | 
			
		||||
 * Can be used to test applications depending on HTTP_Request2 package without
 | 
			
		||||
 * actually performing any HTTP requests. This adapter will return responses
 | 
			
		||||
 * previously added via addResponse()
 | 
			
		||||
 * <code>
 | 
			
		||||
 * $mock = new HTTP_Request2_Adapter_Mock();
 | 
			
		||||
 * $mock->addResponse("HTTP/1.1 ... ");
 | 
			
		||||
 * 
 | 
			
		||||
 * $request = new HTTP_Request2();
 | 
			
		||||
 * $request->setAdapter($mock);
 | 
			
		||||
 * 
 | 
			
		||||
 * // This will return the response set above
 | 
			
		||||
 * $response = $req->send();
 | 
			
		||||
 * </code> 
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * A queue of responses to be returned by sendRequest()
 | 
			
		||||
    * @var  array 
 | 
			
		||||
    */
 | 
			
		||||
    protected $responses = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the next response from the queue built by addResponse()
 | 
			
		||||
    *
 | 
			
		||||
    * If the queue is empty will return default empty response with status 400,
 | 
			
		||||
    * if an Exception object was added to the queue it will be thrown.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function sendRequest(HTTP_Request2 $request)
 | 
			
		||||
    {
 | 
			
		||||
        if (count($this->responses) > 0) {
 | 
			
		||||
            $response = array_shift($this->responses);
 | 
			
		||||
            if ($response instanceof HTTP_Request2_Response) {
 | 
			
		||||
                return $response;
 | 
			
		||||
            } else {
 | 
			
		||||
                // rethrow the exception,
 | 
			
		||||
                $class   = get_class($response);
 | 
			
		||||
                $message = $response->getMessage();
 | 
			
		||||
                $code    = $response->getCode();
 | 
			
		||||
                throw new $class($message, $code);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adds response to the queue
 | 
			
		||||
    *
 | 
			
		||||
    * @param    mixed   either a string, a pointer to an open file,
 | 
			
		||||
    *                   a HTTP_Request2_Response or Exception object
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function addResponse($response)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($response)) {
 | 
			
		||||
            $response = self::createResponseFromString($response);
 | 
			
		||||
        } elseif (is_resource($response)) {
 | 
			
		||||
            $response = self::createResponseFromFile($response);
 | 
			
		||||
        } elseif (!$response instanceof HTTP_Request2_Response &&
 | 
			
		||||
                  !$response instanceof Exception
 | 
			
		||||
        ) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Parameter is not a valid response');
 | 
			
		||||
        }
 | 
			
		||||
        $this->responses[] = $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Creates a new HTTP_Request2_Response object from a string
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public static function createResponseFromString($str)
 | 
			
		||||
    {
 | 
			
		||||
        $parts       = preg_split('!(\r?\n){2}!m', $str, 2);
 | 
			
		||||
        $headerLines = explode("\n", $parts[0]); 
 | 
			
		||||
        $response    = new HTTP_Request2_Response(array_shift($headerLines));
 | 
			
		||||
        foreach ($headerLines as $headerLine) {
 | 
			
		||||
            $response->parseHeaderLine($headerLine);
 | 
			
		||||
        }
 | 
			
		||||
        $response->parseHeaderLine('');
 | 
			
		||||
        if (isset($parts[1])) {
 | 
			
		||||
            $response->appendBody($parts[1]);
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Creates a new HTTP_Request2_Response object from a file
 | 
			
		||||
    *
 | 
			
		||||
    * @param    resource    file pointer returned by fopen()
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public static function createResponseFromFile($fp)
 | 
			
		||||
    {
 | 
			
		||||
        $response = new HTTP_Request2_Response(fgets($fp));
 | 
			
		||||
        do {
 | 
			
		||||
            $headerLine = fgets($fp);
 | 
			
		||||
            $response->parseHeaderLine($headerLine);
 | 
			
		||||
        } while ('' != trim($headerLine));
 | 
			
		||||
 | 
			
		||||
        while (!feof($fp)) {
 | 
			
		||||
            $response->appendBody(fread($fp, 8192));
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										971
									
								
								extlib/HTTP/Request2/Adapter/Socket.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										971
									
								
								extlib/HTTP/Request2/Adapter/Socket.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,971 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Socket-based adapter for HTTP_Request2
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Socket.php 279760 2009-05-03 10:46:42Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for HTTP_Request2 adapters
 | 
			
		||||
 */
 | 
			
		||||
require_once 'HTTP/Request2/Adapter.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Socket-based adapter for HTTP_Request2
 | 
			
		||||
 *
 | 
			
		||||
 * This adapter uses only PHP sockets and will work on almost any PHP
 | 
			
		||||
 * environment. Code is based on original HTTP_Request PEAR package.
 | 
			
		||||
 *
 | 
			
		||||
 * @category    HTTP
 | 
			
		||||
 * @package     HTTP_Request2
 | 
			
		||||
 * @author      Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version     Release: 0.4.1
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * Regular expression for 'token' rule from RFC 2616
 | 
			
		||||
    */ 
 | 
			
		||||
    const REGEXP_TOKEN = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Regular expression for 'quoted-string' rule from RFC 2616
 | 
			
		||||
    */
 | 
			
		||||
    const REGEXP_QUOTED_STRING = '"(?:\\\\.|[^\\\\"])*"';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Connected sockets, needed for Keep-Alive support
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @see  connect()
 | 
			
		||||
    */
 | 
			
		||||
    protected static $sockets = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Data for digest authentication scheme
 | 
			
		||||
    *
 | 
			
		||||
    * The keys for the array are URL prefixes. 
 | 
			
		||||
    *
 | 
			
		||||
    * The values are associative arrays with data (realm, nonce, nonce-count, 
 | 
			
		||||
    * opaque...) needed for digest authentication. Stored here to prevent making 
 | 
			
		||||
    * duplicate requests to digest-protected resources after we have already 
 | 
			
		||||
    * received the challenge.
 | 
			
		||||
    *
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected static $challenges = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Connected socket
 | 
			
		||||
    * @var  resource
 | 
			
		||||
    * @see  connect()
 | 
			
		||||
    */
 | 
			
		||||
    protected $socket;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Challenge used for server digest authentication
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $serverChallenge;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Challenge used for proxy digest authentication
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $proxyChallenge;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Global timeout, exception will be raised if request continues past this time
 | 
			
		||||
    * @var  integer
 | 
			
		||||
    */
 | 
			
		||||
    protected $timeout = null;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Remaining length of the current chunk, when reading chunked response
 | 
			
		||||
    * @var  integer
 | 
			
		||||
    * @see  readChunked()
 | 
			
		||||
    */ 
 | 
			
		||||
    protected $chunkLength = 0;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sends request to the remote server and returns its response
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public function sendRequest(HTTP_Request2 $request)
 | 
			
		||||
    {
 | 
			
		||||
        $this->request = $request;
 | 
			
		||||
        $keepAlive     = $this->connect();
 | 
			
		||||
        $headers       = $this->prepareHeaders();
 | 
			
		||||
 | 
			
		||||
        // Use global request timeout if given, see feature requests #5735, #8964 
 | 
			
		||||
        if ($timeout = $request->getConfig('timeout')) {
 | 
			
		||||
            $this->timeout = time() + $timeout;
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->timeout = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (false === @fwrite($this->socket, $headers, strlen($headers))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error writing request');
 | 
			
		||||
            }
 | 
			
		||||
            // provide request headers to the observer, see request #7633
 | 
			
		||||
            $this->request->setLastEvent('sentHeaders', $headers);
 | 
			
		||||
            $this->writeBody();
 | 
			
		||||
 | 
			
		||||
            if ($this->timeout && time() > $this->timeout) {
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    'Request timed out after ' . 
 | 
			
		||||
                    $request->getConfig('timeout') . ' second(s)'
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $response = $this->readResponse();
 | 
			
		||||
 | 
			
		||||
            if (!$this->canKeepAlive($keepAlive, $response)) {
 | 
			
		||||
                $this->disconnect();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->shouldUseProxyDigestAuth($response)) {
 | 
			
		||||
                return $this->sendRequest($request);
 | 
			
		||||
            }
 | 
			
		||||
            if ($this->shouldUseServerDigestAuth($response)) {
 | 
			
		||||
                return $this->sendRequest($request);
 | 
			
		||||
            }
 | 
			
		||||
            if ($authInfo = $response->getHeader('authentication-info')) {
 | 
			
		||||
                $this->updateChallenge($this->serverChallenge, $authInfo);
 | 
			
		||||
            }
 | 
			
		||||
            if ($proxyInfo = $response->getHeader('proxy-authentication-info')) {
 | 
			
		||||
                $this->updateChallenge($this->proxyChallenge, $proxyInfo);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            $this->disconnect();
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Connects to the remote server
 | 
			
		||||
    *
 | 
			
		||||
    * @return   bool    whether the connection can be persistent
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function connect()
 | 
			
		||||
    {
 | 
			
		||||
        $secure  = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https');
 | 
			
		||||
        $tunnel  = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
 | 
			
		||||
        $headers = $this->request->getHeaders();
 | 
			
		||||
        $reqHost = $this->request->getUrl()->getHost();
 | 
			
		||||
        if (!($reqPort = $this->request->getUrl()->getPort())) {
 | 
			
		||||
            $reqPort = $secure? 443: 80;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($host = $this->request->getConfig('proxy_host')) {
 | 
			
		||||
            if (!($port = $this->request->getConfig('proxy_port'))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Proxy port not provided');
 | 
			
		||||
            }
 | 
			
		||||
            $proxy = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            $host  = $reqHost;
 | 
			
		||||
            $port  = $reqPort;
 | 
			
		||||
            $proxy = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($tunnel && !$proxy) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                "Trying to perform CONNECT request without proxy"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        if ($secure && !in_array('ssl', stream_get_transports())) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                'Need OpenSSL support for https:// requests'
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
 | 
			
		||||
        // connection token to a proxy server...
 | 
			
		||||
        if ($proxy && !$secure && 
 | 
			
		||||
            !empty($headers['connection']) && 'Keep-Alive' == $headers['connection']
 | 
			
		||||
        ) {
 | 
			
		||||
            $this->request->setHeader('connection');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $keepAlive = ('1.1' == $this->request->getConfig('protocol_version') && 
 | 
			
		||||
                      empty($headers['connection'])) ||
 | 
			
		||||
                     (!empty($headers['connection']) &&
 | 
			
		||||
                      'Keep-Alive' == $headers['connection']);
 | 
			
		||||
        $host = ((!$secure || $proxy)? 'tcp://': 'ssl://') . $host;
 | 
			
		||||
 | 
			
		||||
        $options = array();
 | 
			
		||||
        if ($secure || $tunnel) {
 | 
			
		||||
            foreach ($this->request->getConfig() as $name => $value) {
 | 
			
		||||
                if ('ssl_' == substr($name, 0, 4) && null !== $value) {
 | 
			
		||||
                    if ('ssl_verify_host' == $name) {
 | 
			
		||||
                        if ($value) {
 | 
			
		||||
                            $options['CN_match'] = $reqHost;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $options[substr($name, 4)] = $value;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ksort($options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Changing SSL context options after connection is established does *not*
 | 
			
		||||
        // work, we need a new connection if options change
 | 
			
		||||
        $remote    = $host . ':' . $port;
 | 
			
		||||
        $socketKey = $remote . (($secure && $proxy)? "->{$reqHost}:{$reqPort}": '') .
 | 
			
		||||
                     (empty($options)? '': ':' . serialize($options));
 | 
			
		||||
        unset($this->socket);
 | 
			
		||||
 | 
			
		||||
        // We use persistent connections and have a connected socket?
 | 
			
		||||
        // Ensure that the socket is still connected, see bug #16149
 | 
			
		||||
        if ($keepAlive && !empty(self::$sockets[$socketKey]) &&
 | 
			
		||||
            !feof(self::$sockets[$socketKey])
 | 
			
		||||
        ) {
 | 
			
		||||
            $this->socket =& self::$sockets[$socketKey];
 | 
			
		||||
 | 
			
		||||
        } elseif ($secure && $proxy && !$tunnel) {
 | 
			
		||||
            $this->establishTunnel();
 | 
			
		||||
            $this->request->setLastEvent(
 | 
			
		||||
                'connect', "ssl://{$reqHost}:{$reqPort} via {$host}:{$port}"
 | 
			
		||||
            );
 | 
			
		||||
            self::$sockets[$socketKey] =& $this->socket;
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            // Set SSL context options if doing HTTPS request or creating a tunnel
 | 
			
		||||
            $context = stream_context_create();
 | 
			
		||||
            foreach ($options as $name => $value) {
 | 
			
		||||
                if (!stream_context_set_option($context, 'ssl', $name, $value)) {
 | 
			
		||||
                    throw new HTTP_Request2_Exception(
 | 
			
		||||
                        "Error setting SSL context option '{$name}'"
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $this->socket = @stream_socket_client(
 | 
			
		||||
                $remote, $errno, $errstr,
 | 
			
		||||
                $this->request->getConfig('connect_timeout'),
 | 
			
		||||
                STREAM_CLIENT_CONNECT, $context
 | 
			
		||||
            );
 | 
			
		||||
            if (!$this->socket) {
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    "Unable to connect to {$remote}. Error #{$errno}: {$errstr}"
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            $this->request->setLastEvent('connect', $remote);
 | 
			
		||||
            self::$sockets[$socketKey] =& $this->socket;
 | 
			
		||||
        }
 | 
			
		||||
        return $keepAlive;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Establishes a tunnel to a secure remote server via HTTP CONNECT request
 | 
			
		||||
    *
 | 
			
		||||
    * This method will fail if 'ssl_verify_peer' is enabled. Probably because PHP
 | 
			
		||||
    * sees that we are connected to a proxy server (duh!) rather than the server
 | 
			
		||||
    * that presents its certificate.
 | 
			
		||||
    *
 | 
			
		||||
    * @link     http://tools.ietf.org/html/rfc2817#section-5.2
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function establishTunnel()
 | 
			
		||||
    {
 | 
			
		||||
        $donor   = new self;
 | 
			
		||||
        $connect = new HTTP_Request2(
 | 
			
		||||
            $this->request->getUrl(), HTTP_Request2::METHOD_CONNECT,
 | 
			
		||||
            array_merge($this->request->getConfig(),
 | 
			
		||||
                        array('adapter' => $donor))
 | 
			
		||||
        );
 | 
			
		||||
        $response = $connect->send();
 | 
			
		||||
        // Need any successful (2XX) response
 | 
			
		||||
        if (200 > $response->getStatus() || 300 <= $response->getStatus()) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                'Failed to connect via HTTPS proxy. Proxy response: ' .
 | 
			
		||||
                $response->getStatus() . ' ' . $response->getReasonPhrase()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        $this->socket = $donor->socket;
 | 
			
		||||
 | 
			
		||||
        $modes = array(
 | 
			
		||||
            STREAM_CRYPTO_METHOD_TLS_CLIENT, 
 | 
			
		||||
            STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
 | 
			
		||||
            STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
 | 
			
		||||
            STREAM_CRYPTO_METHOD_SSLv2_CLIENT 
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        foreach ($modes as $mode) {
 | 
			
		||||
            if (stream_socket_enable_crypto($this->socket, true, $mode)) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new HTTP_Request2_Exception(
 | 
			
		||||
            'Failed to enable secure connection when connecting through proxy'
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Checks whether current connection may be reused or should be closed
 | 
			
		||||
    *
 | 
			
		||||
    * @param    boolean                 whether connection could be persistent 
 | 
			
		||||
    *                                   in the first place
 | 
			
		||||
    * @param    HTTP_Request2_Response  response object to check
 | 
			
		||||
    * @return   boolean
 | 
			
		||||
    */
 | 
			
		||||
    protected function canKeepAlive($requestKeepAlive, HTTP_Request2_Response $response)
 | 
			
		||||
    {
 | 
			
		||||
        // Do not close socket on successful CONNECT request
 | 
			
		||||
        if (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() &&
 | 
			
		||||
            200 <= $response->getStatus() && 300 > $response->getStatus()
 | 
			
		||||
        ) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $lengthKnown = 'chunked' == strtolower($response->getHeader('transfer-encoding')) ||
 | 
			
		||||
                       null !== $response->getHeader('content-length');
 | 
			
		||||
        $persistent  = 'keep-alive' == strtolower($response->getHeader('connection')) ||
 | 
			
		||||
                       (null === $response->getHeader('connection') &&
 | 
			
		||||
                        '1.1' == $response->getVersion());
 | 
			
		||||
        return $requestKeepAlive && $lengthKnown && $persistent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Disconnects from the remote server
 | 
			
		||||
    */
 | 
			
		||||
    protected function disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        if (is_resource($this->socket)) {
 | 
			
		||||
            fclose($this->socket);
 | 
			
		||||
            $this->socket = null;
 | 
			
		||||
            $this->request->setLastEvent('disconnect');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Checks whether another request should be performed with server digest auth
 | 
			
		||||
    *
 | 
			
		||||
    * Several conditions should be satisfied for it to return true:
 | 
			
		||||
    *   - response status should be 401
 | 
			
		||||
    *   - auth credentials should be set in the request object
 | 
			
		||||
    *   - response should contain WWW-Authenticate header with digest challenge
 | 
			
		||||
    *   - there is either no challenge stored for this URL or new challenge
 | 
			
		||||
    *     contains stale=true parameter (in other case we probably just failed 
 | 
			
		||||
    *     due to invalid username / password)
 | 
			
		||||
    *
 | 
			
		||||
    * The method stores challenge values in $challenges static property
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2_Response  response to check
 | 
			
		||||
    * @return   boolean whether another request should be performed
 | 
			
		||||
    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
 | 
			
		||||
    */
 | 
			
		||||
    protected function shouldUseServerDigestAuth(HTTP_Request2_Response $response)
 | 
			
		||||
    {
 | 
			
		||||
        // no sense repeating a request if we don't have credentials
 | 
			
		||||
        if (401 != $response->getStatus() || !$this->request->getAuth()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (!$challenge = $this->parseDigestChallenge($response->getHeader('www-authenticate'))) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $url    = $this->request->getUrl();
 | 
			
		||||
        $scheme = $url->getScheme();
 | 
			
		||||
        $host   = $scheme . '://' . $url->getHost();
 | 
			
		||||
        if ($port = $url->getPort()) {
 | 
			
		||||
            if ((0 == strcasecmp($scheme, 'http') && 80 != $port) ||
 | 
			
		||||
                (0 == strcasecmp($scheme, 'https') && 443 != $port)
 | 
			
		||||
            ) {
 | 
			
		||||
                $host .= ':' . $port;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($challenge['domain'])) {
 | 
			
		||||
            $prefixes = array();
 | 
			
		||||
            foreach (preg_split('/\\s+/', $challenge['domain']) as $prefix) {
 | 
			
		||||
                // don't bother with different servers
 | 
			
		||||
                if ('/' == substr($prefix, 0, 1)) {
 | 
			
		||||
                    $prefixes[] = $host . $prefix;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($prefixes)) {
 | 
			
		||||
            $prefixes = array($host . '/');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ret = true;
 | 
			
		||||
        foreach ($prefixes as $prefix) {
 | 
			
		||||
            if (!empty(self::$challenges[$prefix]) &&
 | 
			
		||||
                (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
 | 
			
		||||
            ) {
 | 
			
		||||
                // probably credentials are invalid
 | 
			
		||||
                $ret = false;
 | 
			
		||||
            }
 | 
			
		||||
            self::$challenges[$prefix] =& $challenge;
 | 
			
		||||
        }
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Checks whether another request should be performed with proxy digest auth
 | 
			
		||||
    *
 | 
			
		||||
    * Several conditions should be satisfied for it to return true:
 | 
			
		||||
    *   - response status should be 407
 | 
			
		||||
    *   - proxy auth credentials should be set in the request object
 | 
			
		||||
    *   - response should contain Proxy-Authenticate header with digest challenge
 | 
			
		||||
    *   - there is either no challenge stored for this proxy or new challenge
 | 
			
		||||
    *     contains stale=true parameter (in other case we probably just failed 
 | 
			
		||||
    *     due to invalid username / password)
 | 
			
		||||
    *
 | 
			
		||||
    * The method stores challenge values in $challenges static property
 | 
			
		||||
    *
 | 
			
		||||
    * @param    HTTP_Request2_Response  response to check
 | 
			
		||||
    * @return   boolean whether another request should be performed
 | 
			
		||||
    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
 | 
			
		||||
    */
 | 
			
		||||
    protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response)
 | 
			
		||||
    {
 | 
			
		||||
        if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $key = 'proxy://' . $this->request->getConfig('proxy_host') .
 | 
			
		||||
               ':' . $this->request->getConfig('proxy_port');
 | 
			
		||||
 | 
			
		||||
        if (!empty(self::$challenges[$key]) &&
 | 
			
		||||
            (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
 | 
			
		||||
        ) {
 | 
			
		||||
            $ret = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            $ret = true;
 | 
			
		||||
        }
 | 
			
		||||
        self::$challenges[$key] = $challenge;
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Extracts digest method challenge from (WWW|Proxy)-Authenticate header value
 | 
			
		||||
    *
 | 
			
		||||
    * There is a problem with implementation of RFC 2617: several of the parameters
 | 
			
		||||
    * here are defined as quoted-string and thus may contain backslash escaped
 | 
			
		||||
    * double quotes (RFC 2616, section 2.2). However, RFC 2617 defines unq(X) as
 | 
			
		||||
    * just value of quoted-string X without surrounding quotes, it doesn't speak
 | 
			
		||||
    * about removing backslash escaping.
 | 
			
		||||
    *
 | 
			
		||||
    * Now realm parameter is user-defined and human-readable, strange things
 | 
			
		||||
    * happen when it contains quotes:
 | 
			
		||||
    *   - Apache allows quotes in realm, but apparently uses realm value without
 | 
			
		||||
    *     backslashes for digest computation
 | 
			
		||||
    *   - Squid allows (manually escaped) quotes there, but it is impossible to
 | 
			
		||||
    *     authorize with either escaped or unescaped quotes used in digest,
 | 
			
		||||
    *     probably it can't parse the response (?)
 | 
			
		||||
    *   - Both IE and Firefox display realm value with backslashes in 
 | 
			
		||||
    *     the password popup and apparently use the same value for digest
 | 
			
		||||
    *
 | 
			
		||||
    * HTTP_Request2 follows IE and Firefox (and hopefully RFC 2617) in
 | 
			
		||||
    * quoted-string handling, unfortunately that means failure to authorize 
 | 
			
		||||
    * sometimes
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  value of WWW-Authenticate or Proxy-Authenticate header
 | 
			
		||||
    * @return   mixed   associative array with challenge parameters, false if
 | 
			
		||||
    *                   no challenge is present in header value
 | 
			
		||||
    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
 | 
			
		||||
    */
 | 
			
		||||
    protected function parseDigestChallenge($headerValue)
 | 
			
		||||
    {
 | 
			
		||||
        $authParam   = '(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
 | 
			
		||||
                       self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')';
 | 
			
		||||
        $challenge   = "!(?<=^|\\s|,)Digest ({$authParam}\\s*(,\\s*|$))+!";
 | 
			
		||||
        if (!preg_match($challenge, $headerValue, $matches)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        preg_match_all('!' . $authParam . '!', $matches[0], $params);
 | 
			
		||||
        $paramsAry   = array();
 | 
			
		||||
        $knownParams = array('realm', 'domain', 'nonce', 'opaque', 'stale',
 | 
			
		||||
                             'algorithm', 'qop');
 | 
			
		||||
        for ($i = 0; $i < count($params[0]); $i++) {
 | 
			
		||||
            // section 3.2.1: Any unrecognized directive MUST be ignored.
 | 
			
		||||
            if (in_array($params[1][$i], $knownParams)) {
 | 
			
		||||
                if ('"' == substr($params[2][$i], 0, 1)) {
 | 
			
		||||
                    $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $paramsAry[$params[1][$i]] = $params[2][$i];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // we only support qop=auth
 | 
			
		||||
        if (!empty($paramsAry['qop']) && 
 | 
			
		||||
            !in_array('auth', array_map('trim', explode(',', $paramsAry['qop'])))
 | 
			
		||||
        ) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                "Only 'auth' qop is currently supported in digest authentication, " .
 | 
			
		||||
                "server requested '{$paramsAry['qop']}'"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        // we only support algorithm=MD5
 | 
			
		||||
        if (!empty($paramsAry['algorithm']) && 'MD5' != $paramsAry['algorithm']) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                "Only 'MD5' algorithm is currently supported in digest authentication, " .
 | 
			
		||||
                "server requested '{$paramsAry['algorithm']}'"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $paramsAry; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Parses [Proxy-]Authentication-Info header value and updates challenge
 | 
			
		||||
    *
 | 
			
		||||
    * @param    array   challenge to update
 | 
			
		||||
    * @param    string  value of [Proxy-]Authentication-Info header
 | 
			
		||||
    * @todo     validate server rspauth response
 | 
			
		||||
    */ 
 | 
			
		||||
    protected function updateChallenge(&$challenge, $headerValue)
 | 
			
		||||
    {
 | 
			
		||||
        $authParam   = '!(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
 | 
			
		||||
                       self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')!';
 | 
			
		||||
        $paramsAry   = array();
 | 
			
		||||
 | 
			
		||||
        preg_match_all($authParam, $headerValue, $params);
 | 
			
		||||
        for ($i = 0; $i < count($params[0]); $i++) {
 | 
			
		||||
            if ('"' == substr($params[2][$i], 0, 1)) {
 | 
			
		||||
                $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
 | 
			
		||||
            } else {
 | 
			
		||||
                $paramsAry[$params[1][$i]] = $params[2][$i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // for now, just update the nonce value
 | 
			
		||||
        if (!empty($paramsAry['nextnonce'])) {
 | 
			
		||||
            $challenge['nonce'] = $paramsAry['nextnonce'];
 | 
			
		||||
            $challenge['nc']    = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Creates a value for [Proxy-]Authorization header when using digest authentication
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  user name
 | 
			
		||||
    * @param    string  password
 | 
			
		||||
    * @param    string  request URL
 | 
			
		||||
    * @param    array   digest challenge parameters
 | 
			
		||||
    * @return   string  value of [Proxy-]Authorization request header
 | 
			
		||||
    * @link     http://tools.ietf.org/html/rfc2617#section-3.2.2
 | 
			
		||||
    */ 
 | 
			
		||||
    protected function createDigestResponse($user, $password, $url, &$challenge)
 | 
			
		||||
    {
 | 
			
		||||
        if (false !== ($q = strpos($url, '?')) && 
 | 
			
		||||
            $this->request->getConfig('digest_compat_ie')
 | 
			
		||||
        ) {
 | 
			
		||||
            $url = substr($url, 0, $q);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $a1 = md5($user . ':' . $challenge['realm'] . ':' . $password);
 | 
			
		||||
        $a2 = md5($this->request->getMethod() . ':' . $url);
 | 
			
		||||
 | 
			
		||||
        if (empty($challenge['qop'])) {
 | 
			
		||||
            $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $a2);
 | 
			
		||||
        } else {
 | 
			
		||||
            $challenge['cnonce'] = 'Req2.' . rand();
 | 
			
		||||
            if (empty($challenge['nc'])) {
 | 
			
		||||
                $challenge['nc'] = 1;
 | 
			
		||||
            }
 | 
			
		||||
            $nc     = sprintf('%08x', $challenge['nc']++);
 | 
			
		||||
            $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' .
 | 
			
		||||
                          $challenge['cnonce'] . ':auth:' . $a2);
 | 
			
		||||
        }
 | 
			
		||||
        return 'Digest username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $user) . '", ' .
 | 
			
		||||
               'realm="' . $challenge['realm'] . '", ' .
 | 
			
		||||
               'nonce="' . $challenge['nonce'] . '", ' .
 | 
			
		||||
               'uri="' . $url . '", ' .
 | 
			
		||||
               'response="' . $digest . '"' .
 | 
			
		||||
               (!empty($challenge['opaque'])? 
 | 
			
		||||
                ', opaque="' . $challenge['opaque'] . '"':
 | 
			
		||||
                '') .
 | 
			
		||||
               (!empty($challenge['qop'])?
 | 
			
		||||
                ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"':
 | 
			
		||||
                '');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adds 'Authorization' header (if needed) to request headers array
 | 
			
		||||
    *
 | 
			
		||||
    * @param    array   request headers
 | 
			
		||||
    * @param    string  request host (needed for digest authentication)
 | 
			
		||||
    * @param    string  request URL (needed for digest authentication)
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function addAuthorizationHeader(&$headers, $requestHost, $requestUrl)
 | 
			
		||||
    {
 | 
			
		||||
        if (!($auth = $this->request->getAuth())) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        switch ($auth['scheme']) {
 | 
			
		||||
            case HTTP_Request2::AUTH_BASIC:
 | 
			
		||||
                $headers['authorization'] = 
 | 
			
		||||
                    'Basic ' . base64_encode($auth['user'] . ':' . $auth['password']);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case HTTP_Request2::AUTH_DIGEST:
 | 
			
		||||
                unset($this->serverChallenge);
 | 
			
		||||
                $fullUrl = ('/' == $requestUrl[0])?
 | 
			
		||||
                           $this->request->getUrl()->getScheme() . '://' .
 | 
			
		||||
                            $requestHost . $requestUrl:
 | 
			
		||||
                           $requestUrl;
 | 
			
		||||
                foreach (array_keys(self::$challenges) as $key) {
 | 
			
		||||
                    if ($key == substr($fullUrl, 0, strlen($key))) {
 | 
			
		||||
                        $headers['authorization'] = $this->createDigestResponse(
 | 
			
		||||
                            $auth['user'], $auth['password'], 
 | 
			
		||||
                            $requestUrl, self::$challenges[$key]
 | 
			
		||||
                        );
 | 
			
		||||
                        $this->serverChallenge =& self::$challenges[$key];
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    "Unknown HTTP authentication scheme '{$auth['scheme']}'"
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Adds 'Proxy-Authorization' header (if needed) to request headers array
 | 
			
		||||
    *
 | 
			
		||||
    * @param    array   request headers
 | 
			
		||||
    * @param    string  request URL (needed for digest authentication)
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function addProxyAuthorizationHeader(&$headers, $requestUrl)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->request->getConfig('proxy_host') ||
 | 
			
		||||
            !($user = $this->request->getConfig('proxy_user')) ||
 | 
			
		||||
            (0 == strcasecmp('https', $this->request->getUrl()->getScheme()) &&
 | 
			
		||||
             HTTP_Request2::METHOD_CONNECT != $this->request->getMethod())
 | 
			
		||||
        ) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $password = $this->request->getConfig('proxy_password');
 | 
			
		||||
        switch ($this->request->getConfig('proxy_auth_scheme')) {
 | 
			
		||||
            case HTTP_Request2::AUTH_BASIC:
 | 
			
		||||
                $headers['proxy-authorization'] =
 | 
			
		||||
                    'Basic ' . base64_encode($user . ':' . $password);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case HTTP_Request2::AUTH_DIGEST:
 | 
			
		||||
                unset($this->proxyChallenge);
 | 
			
		||||
                $proxyUrl = 'proxy://' . $this->request->getConfig('proxy_host') .
 | 
			
		||||
                            ':' . $this->request->getConfig('proxy_port');
 | 
			
		||||
                if (!empty(self::$challenges[$proxyUrl])) {
 | 
			
		||||
                    $headers['proxy-authorization'] = $this->createDigestResponse(
 | 
			
		||||
                        $user, $password,
 | 
			
		||||
                        $requestUrl, self::$challenges[$proxyUrl]
 | 
			
		||||
                    );
 | 
			
		||||
                    $this->proxyChallenge =& self::$challenges[$proxyUrl];
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    "Unknown HTTP authentication scheme '" .
 | 
			
		||||
                    $this->request->getConfig('proxy_auth_scheme') . "'"
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Creates the string with the Request-Line and request headers
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function prepareHeaders()
 | 
			
		||||
    {
 | 
			
		||||
        $headers = $this->request->getHeaders();
 | 
			
		||||
        $url     = $this->request->getUrl();
 | 
			
		||||
        $connect = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
 | 
			
		||||
        $host    = $url->getHost();
 | 
			
		||||
 | 
			
		||||
        $defaultPort = 0 == strcasecmp($url->getScheme(), 'https')? 443: 80;
 | 
			
		||||
        if (($port = $url->getPort()) && $port != $defaultPort || $connect) {
 | 
			
		||||
            $host .= ':' . (empty($port)? $defaultPort: $port);
 | 
			
		||||
        }
 | 
			
		||||
        // Do not overwrite explicitly set 'Host' header, see bug #16146
 | 
			
		||||
        if (!isset($headers['host'])) {
 | 
			
		||||
            $headers['host'] = $host;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($connect) {
 | 
			
		||||
            $requestUrl = $host;
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!$this->request->getConfig('proxy_host') ||
 | 
			
		||||
                0 == strcasecmp($url->getScheme(), 'https')
 | 
			
		||||
            ) {
 | 
			
		||||
                $requestUrl = '';
 | 
			
		||||
            } else {
 | 
			
		||||
                $requestUrl = $url->getScheme() . '://' . $host;
 | 
			
		||||
            }
 | 
			
		||||
            $path        = $url->getPath();
 | 
			
		||||
            $query       = $url->getQuery();
 | 
			
		||||
            $requestUrl .= (empty($path)? '/': $path) . (empty($query)? '': '?' . $query);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ('1.1' == $this->request->getConfig('protocol_version') &&
 | 
			
		||||
            extension_loaded('zlib') && !isset($headers['accept-encoding'])
 | 
			
		||||
        ) {
 | 
			
		||||
            $headers['accept-encoding'] = 'gzip, deflate';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->addAuthorizationHeader($headers, $host, $requestUrl);
 | 
			
		||||
        $this->addProxyAuthorizationHeader($headers, $requestUrl);
 | 
			
		||||
        $this->calculateRequestLength($headers);
 | 
			
		||||
 | 
			
		||||
        $headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' .
 | 
			
		||||
                      $this->request->getConfig('protocol_version') . "\r\n";
 | 
			
		||||
        foreach ($headers as $name => $value) {
 | 
			
		||||
            $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
 | 
			
		||||
            $headersStr   .= $canonicalName . ': ' . $value . "\r\n";
 | 
			
		||||
        }
 | 
			
		||||
        return $headersStr . "\r\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sends the request body
 | 
			
		||||
    *
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function writeBody()
 | 
			
		||||
    {
 | 
			
		||||
        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
 | 
			
		||||
            0 == $this->contentLength
 | 
			
		||||
        ) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $position   = 0;
 | 
			
		||||
        $bufferSize = $this->request->getConfig('buffer_size');
 | 
			
		||||
        while ($position < $this->contentLength) {
 | 
			
		||||
            if (is_string($this->requestBody)) {
 | 
			
		||||
                $str = substr($this->requestBody, $position, $bufferSize);
 | 
			
		||||
            } elseif (is_resource($this->requestBody)) {
 | 
			
		||||
                $str = fread($this->requestBody, $bufferSize);
 | 
			
		||||
            } else {
 | 
			
		||||
                $str = $this->requestBody->read($bufferSize);
 | 
			
		||||
            }
 | 
			
		||||
            if (false === @fwrite($this->socket, $str, strlen($str))) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error writing request');
 | 
			
		||||
            }
 | 
			
		||||
            // Provide the length of written string to the observer, request #7630
 | 
			
		||||
            $this->request->setLastEvent('sentBodyPart', strlen($str));
 | 
			
		||||
            $position += strlen($str); 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Reads the remote server's response
 | 
			
		||||
    *
 | 
			
		||||
    * @return   HTTP_Request2_Response
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function readResponse()
 | 
			
		||||
    {
 | 
			
		||||
        $bufferSize = $this->request->getConfig('buffer_size');
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
            $response = new HTTP_Request2_Response($this->readLine($bufferSize), true);
 | 
			
		||||
            do {
 | 
			
		||||
                $headerLine = $this->readLine($bufferSize);
 | 
			
		||||
                $response->parseHeaderLine($headerLine);
 | 
			
		||||
            } while ('' != $headerLine);
 | 
			
		||||
        } while (in_array($response->getStatus(), array(100, 101)));
 | 
			
		||||
 | 
			
		||||
        $this->request->setLastEvent('receivedHeaders', $response);
 | 
			
		||||
 | 
			
		||||
        // No body possible in such responses
 | 
			
		||||
        if (HTTP_Request2::METHOD_HEAD == $this->request->getMethod() ||
 | 
			
		||||
            (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() &&
 | 
			
		||||
             200 <= $response->getStatus() && 300 > $response->getStatus()) ||
 | 
			
		||||
            in_array($response->getStatus(), array(204, 304))
 | 
			
		||||
        ) {
 | 
			
		||||
            return $response;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $chunked = 'chunked' == $response->getHeader('transfer-encoding');
 | 
			
		||||
        $length  = $response->getHeader('content-length');
 | 
			
		||||
        $hasBody = false;
 | 
			
		||||
        if ($chunked || null === $length || 0 < intval($length)) {
 | 
			
		||||
            // RFC 2616, section 4.4:
 | 
			
		||||
            // 3. ... If a message is received with both a
 | 
			
		||||
            // Transfer-Encoding header field and a Content-Length header field,
 | 
			
		||||
            // the latter MUST be ignored.
 | 
			
		||||
            $toRead = ($chunked || null === $length)? null: $length;
 | 
			
		||||
            $this->chunkLength = 0;
 | 
			
		||||
 | 
			
		||||
            while (!feof($this->socket) && (is_null($toRead) || 0 < $toRead)) {
 | 
			
		||||
                if ($chunked) {
 | 
			
		||||
                    $data = $this->readChunked($bufferSize);
 | 
			
		||||
                } elseif (is_null($toRead)) {
 | 
			
		||||
                    $data = $this->fread($bufferSize);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $data    = $this->fread(min($toRead, $bufferSize));
 | 
			
		||||
                    $toRead -= strlen($data);
 | 
			
		||||
                }
 | 
			
		||||
                if ('' == $data && (!$this->chunkLength || feof($this->socket))) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $hasBody = true;
 | 
			
		||||
                if ($this->request->getConfig('store_body')) {
 | 
			
		||||
                    $response->appendBody($data);
 | 
			
		||||
                }
 | 
			
		||||
                if (!in_array($response->getHeader('content-encoding'), array('identity', null))) {
 | 
			
		||||
                    $this->request->setLastEvent('receivedEncodedBodyPart', $data);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $this->request->setLastEvent('receivedBodyPart', $data);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($hasBody) {
 | 
			
		||||
            $this->request->setLastEvent('receivedBody', $response);
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Reads until either the end of the socket or a newline, whichever comes first 
 | 
			
		||||
    *
 | 
			
		||||
    * Strips the trailing newline from the returned data, handles global 
 | 
			
		||||
    * request timeout. Method idea borrowed from Net_Socket PEAR package. 
 | 
			
		||||
    *
 | 
			
		||||
    * @param    int     buffer size to use for reading
 | 
			
		||||
    * @return   Available data up to the newline (not including newline)
 | 
			
		||||
    * @throws   HTTP_Request2_Exception     In case of timeout
 | 
			
		||||
    */
 | 
			
		||||
    protected function readLine($bufferSize)
 | 
			
		||||
    {
 | 
			
		||||
        $line = '';
 | 
			
		||||
        while (!feof($this->socket)) {
 | 
			
		||||
            if ($this->timeout) {
 | 
			
		||||
                stream_set_timeout($this->socket, max($this->timeout - time(), 1));
 | 
			
		||||
            }
 | 
			
		||||
            $line .= @fgets($this->socket, $bufferSize);
 | 
			
		||||
            $info  = stream_get_meta_data($this->socket);
 | 
			
		||||
            if ($info['timed_out'] || $this->timeout && time() > $this->timeout) {
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    'Request timed out after ' . 
 | 
			
		||||
                    $this->request->getConfig('timeout') . ' second(s)'
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            if (substr($line, -1) == "\n") {
 | 
			
		||||
                return rtrim($line, "\r\n");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $line;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Wrapper around fread(), handles global request timeout
 | 
			
		||||
    *
 | 
			
		||||
    * @param    int     Reads up to this number of bytes
 | 
			
		||||
    * @return   Data read from socket
 | 
			
		||||
    * @throws   HTTP_Request2_Exception     In case of timeout
 | 
			
		||||
    */
 | 
			
		||||
    protected function fread($length)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->timeout) {
 | 
			
		||||
            stream_set_timeout($this->socket, max($this->timeout - time(), 1));
 | 
			
		||||
        }
 | 
			
		||||
        $data = fread($this->socket, $length);
 | 
			
		||||
        $info = stream_get_meta_data($this->socket);
 | 
			
		||||
        if ($info['timed_out'] || $this->timeout && time() > $this->timeout) {
 | 
			
		||||
            throw new HTTP_Request2_Exception(
 | 
			
		||||
                'Request timed out after ' . 
 | 
			
		||||
                $this->request->getConfig('timeout') . ' second(s)'
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        return $data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Reads a part of response body encoded with chunked Transfer-Encoding
 | 
			
		||||
    *
 | 
			
		||||
    * @param    int     buffer size to use for reading
 | 
			
		||||
    * @return   string
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    protected function readChunked($bufferSize)
 | 
			
		||||
    {
 | 
			
		||||
        // at start of the next chunk?
 | 
			
		||||
        if (0 == $this->chunkLength) {
 | 
			
		||||
            $line = $this->readLine($bufferSize);
 | 
			
		||||
            if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
 | 
			
		||||
                throw new HTTP_Request2_Exception(
 | 
			
		||||
                    "Cannot decode chunked response, invalid chunk length '{$line}'"
 | 
			
		||||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->chunkLength = hexdec($matches[1]);
 | 
			
		||||
                // Chunk with zero length indicates the end
 | 
			
		||||
                if (0 == $this->chunkLength) {
 | 
			
		||||
                    $this->readLine($bufferSize);
 | 
			
		||||
                    return '';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $data = $this->fread(min($this->chunkLength, $bufferSize));
 | 
			
		||||
        $this->chunkLength -= strlen($data);
 | 
			
		||||
        if (0 == $this->chunkLength) {
 | 
			
		||||
            $this->readLine($bufferSize); // Trailing CRLF
 | 
			
		||||
        }
 | 
			
		||||
        return $data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										62
									
								
								extlib/HTTP/Request2/Exception.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								extlib/HTTP/Request2/Exception.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Exception class for HTTP_Request2 package
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Exception.php 273003 2009-01-07 19:28:22Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base class for exceptions in PEAR
 | 
			
		||||
 */
 | 
			
		||||
require_once 'PEAR/Exception.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception class for HTTP_Request2 package
 | 
			
		||||
 *
 | 
			
		||||
 * Such a class is required by the Exception RFC:
 | 
			
		||||
 * http://pear.php.net/pepr/pepr-proposal-show.php?id=132
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Exception extends PEAR_Exception
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										274
									
								
								extlib/HTTP/Request2/MultipartBody.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								extlib/HTTP/Request2/MultipartBody.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,274 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Helper class for building multipart/form-data request body
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: MultipartBody.php 287306 2009-08-14 15:22:52Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class for building multipart/form-data request body
 | 
			
		||||
 *
 | 
			
		||||
 * The class helps to reduce memory consumption by streaming large file uploads
 | 
			
		||||
 * from disk, it also allows monitoring of upload progress (see request #7630)
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 * @link       http://tools.ietf.org/html/rfc1867
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_MultipartBody
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * MIME boundary
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    private $_boundary;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Form parameters added via {@link HTTP_Request2::addPostParameter()}
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    private $_params = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * File uploads added via {@link HTTP_Request2::addUpload()}
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    private $_uploads = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Header for parts with parameters
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    private $_headerParam = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n";
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Header for parts with uploads
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    private $_headerUpload = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n";
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Current position in parameter and upload arrays
 | 
			
		||||
    *
 | 
			
		||||
    * First number is index of "current" part, second number is position within
 | 
			
		||||
    * "current" part
 | 
			
		||||
    *
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    private $_pos = array(0, 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Constructor. Sets the arrays with POST data.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    array   values of form fields set via {@link HTTP_Request2::addPostParameter()}
 | 
			
		||||
    * @param    array   file uploads set via {@link HTTP_Request2::addUpload()}
 | 
			
		||||
    * @param    bool    whether to append brackets to array variable names
 | 
			
		||||
    */
 | 
			
		||||
    public function __construct(array $params, array $uploads, $useBrackets = true)
 | 
			
		||||
    {
 | 
			
		||||
        $this->_params = self::_flattenArray('', $params, $useBrackets);
 | 
			
		||||
        foreach ($uploads as $fieldName => $f) {
 | 
			
		||||
            if (!is_array($f['fp'])) {
 | 
			
		||||
                $this->_uploads[] = $f + array('name' => $fieldName);
 | 
			
		||||
            } else {
 | 
			
		||||
                for ($i = 0; $i < count($f['fp']); $i++) {
 | 
			
		||||
                    $upload = array(
 | 
			
		||||
                        'name' => ($useBrackets? $fieldName . '[' . $i . ']': $fieldName)
 | 
			
		||||
                    );
 | 
			
		||||
                    foreach (array('fp', 'filename', 'size', 'type') as $key) {
 | 
			
		||||
                        $upload[$key] = $f[$key][$i];
 | 
			
		||||
                    }
 | 
			
		||||
                    $this->_uploads[] = $upload;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the length of the body to use in Content-Length header
 | 
			
		||||
    *
 | 
			
		||||
    * @return   integer
 | 
			
		||||
    */
 | 
			
		||||
    public function getLength()
 | 
			
		||||
    {
 | 
			
		||||
        $boundaryLength     = strlen($this->getBoundary());
 | 
			
		||||
        $headerParamLength  = strlen($this->_headerParam) - 4 + $boundaryLength;
 | 
			
		||||
        $headerUploadLength = strlen($this->_headerUpload) - 8 + $boundaryLength;
 | 
			
		||||
        $length             = $boundaryLength + 6;
 | 
			
		||||
        foreach ($this->_params as $p) {
 | 
			
		||||
            $length += $headerParamLength + strlen($p[0]) + strlen($p[1]) + 2;
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($this->_uploads as $u) {
 | 
			
		||||
            $length += $headerUploadLength + strlen($u['name']) + strlen($u['type']) +
 | 
			
		||||
                       strlen($u['filename']) + $u['size'] + 2;
 | 
			
		||||
        }
 | 
			
		||||
        return $length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the boundary to use in Content-Type header
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    */
 | 
			
		||||
    public function getBoundary()
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($this->_boundary)) {
 | 
			
		||||
            $this->_boundary = '--' . md5('PEAR-HTTP_Request2-' . microtime());
 | 
			
		||||
        }
 | 
			
		||||
        return $this->_boundary;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns next chunk of request body
 | 
			
		||||
    *
 | 
			
		||||
    * @param    integer Amount of bytes to read
 | 
			
		||||
    * @return   string  Up to $length bytes of data, empty string if at end
 | 
			
		||||
    */
 | 
			
		||||
    public function read($length)
 | 
			
		||||
    {
 | 
			
		||||
        $ret         = '';
 | 
			
		||||
        $boundary    = $this->getBoundary();
 | 
			
		||||
        $paramCount  = count($this->_params);
 | 
			
		||||
        $uploadCount = count($this->_uploads);
 | 
			
		||||
        while ($length > 0 && $this->_pos[0] <= $paramCount + $uploadCount) {
 | 
			
		||||
            $oldLength = $length;
 | 
			
		||||
            if ($this->_pos[0] < $paramCount) {
 | 
			
		||||
                $param = sprintf($this->_headerParam, $boundary, 
 | 
			
		||||
                                 $this->_params[$this->_pos[0]][0]) .
 | 
			
		||||
                         $this->_params[$this->_pos[0]][1] . "\r\n";
 | 
			
		||||
                $ret    .= substr($param, $this->_pos[1], $length);
 | 
			
		||||
                $length -= min(strlen($param) - $this->_pos[1], $length);
 | 
			
		||||
 | 
			
		||||
            } elseif ($this->_pos[0] < $paramCount + $uploadCount) {
 | 
			
		||||
                $pos    = $this->_pos[0] - $paramCount;
 | 
			
		||||
                $header = sprintf($this->_headerUpload, $boundary,
 | 
			
		||||
                                  $this->_uploads[$pos]['name'],
 | 
			
		||||
                                  $this->_uploads[$pos]['filename'],
 | 
			
		||||
                                  $this->_uploads[$pos]['type']);
 | 
			
		||||
                if ($this->_pos[1] < strlen($header)) {
 | 
			
		||||
                    $ret    .= substr($header, $this->_pos[1], $length);
 | 
			
		||||
                    $length -= min(strlen($header) - $this->_pos[1], $length);
 | 
			
		||||
                }
 | 
			
		||||
                $filePos  = max(0, $this->_pos[1] - strlen($header));
 | 
			
		||||
                if ($length > 0 && $filePos < $this->_uploads[$pos]['size']) {
 | 
			
		||||
                    $ret     .= fread($this->_uploads[$pos]['fp'], $length);
 | 
			
		||||
                    $length  -= min($length, $this->_uploads[$pos]['size'] - $filePos);
 | 
			
		||||
                }
 | 
			
		||||
                if ($length > 0) {
 | 
			
		||||
                    $start   = $this->_pos[1] + ($oldLength - $length) -
 | 
			
		||||
                               strlen($header) - $this->_uploads[$pos]['size'];
 | 
			
		||||
                    $ret    .= substr("\r\n", $start, $length);
 | 
			
		||||
                    $length -= min(2 - $start, $length);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                $closing  = '--' . $boundary . "--\r\n";
 | 
			
		||||
                $ret     .= substr($closing, $this->_pos[1], $length);
 | 
			
		||||
                $length  -= min(strlen($closing) - $this->_pos[1], $length);
 | 
			
		||||
            }
 | 
			
		||||
            if ($length > 0) {
 | 
			
		||||
                $this->_pos     = array($this->_pos[0] + 1, 0);
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->_pos[1] += $oldLength;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Sets the current position to the start of the body
 | 
			
		||||
    *
 | 
			
		||||
    * This allows reusing the same body in another request
 | 
			
		||||
    */
 | 
			
		||||
    public function rewind()
 | 
			
		||||
    {
 | 
			
		||||
        $this->_pos = array(0, 0);
 | 
			
		||||
        foreach ($this->_uploads as $u) {
 | 
			
		||||
            rewind($u['fp']);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the body as string
 | 
			
		||||
    *
 | 
			
		||||
    * Note that it reads all file uploads into memory so it is a good idea not
 | 
			
		||||
    * to use this method with large file uploads and rely on read() instead.
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    */
 | 
			
		||||
    public function __toString()
 | 
			
		||||
    {
 | 
			
		||||
        $this->rewind();
 | 
			
		||||
        return $this->read($this->getLength());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Helper function to change the (probably multidimensional) associative array
 | 
			
		||||
    * into the simple one.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  name for item
 | 
			
		||||
    * @param    mixed   item's values
 | 
			
		||||
    * @param    bool    whether to append [] to array variables' names
 | 
			
		||||
    * @return   array   array with the following items: array('item name', 'item value');
 | 
			
		||||
    */
 | 
			
		||||
    private static function _flattenArray($name, $values, $useBrackets)
 | 
			
		||||
    {
 | 
			
		||||
        if (!is_array($values)) {
 | 
			
		||||
            return array(array($name, $values));
 | 
			
		||||
        } else {
 | 
			
		||||
            $ret = array();
 | 
			
		||||
            foreach ($values as $k => $v) {
 | 
			
		||||
                if (empty($name)) {
 | 
			
		||||
                    $newName = $k;
 | 
			
		||||
                } elseif ($useBrackets) {
 | 
			
		||||
                    $newName = $name . '[' . $k . ']';
 | 
			
		||||
                } else {
 | 
			
		||||
                    $newName = $name;
 | 
			
		||||
                }
 | 
			
		||||
                $ret = array_merge($ret, self::_flattenArray($newName, $v, $useBrackets));
 | 
			
		||||
            }
 | 
			
		||||
            return $ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										215
									
								
								extlib/HTTP/Request2/Observer/Log.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								extlib/HTTP/Request2/Observer/Log.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,215 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * An observer useful for debugging / testing.
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category HTTP
 | 
			
		||||
 * @package  HTTP_Request2
 | 
			
		||||
 * @author   David Jean Louis <izi@php.net>
 | 
			
		||||
 * @author   Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license  http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version  CVS: $Id: Log.php 272593 2009-01-02 16:27:14Z avb $
 | 
			
		||||
 * @link     http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception class for HTTP_Request2 package
 | 
			
		||||
 */ 
 | 
			
		||||
require_once 'HTTP/Request2/Exception.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A debug observer useful for debugging / testing.
 | 
			
		||||
 *
 | 
			
		||||
 * This observer logs to a log target data corresponding to the various request 
 | 
			
		||||
 * and response events, it logs by default to php://output but can be configured
 | 
			
		||||
 * to log to a file or via the PEAR Log package.
 | 
			
		||||
 *
 | 
			
		||||
 * A simple example:
 | 
			
		||||
 * <code>
 | 
			
		||||
 * require_once 'HTTP/Request2.php';
 | 
			
		||||
 * require_once 'HTTP/Request2/Observer/Log.php';
 | 
			
		||||
 *
 | 
			
		||||
 * $request  = new HTTP_Request2('http://www.example.com');
 | 
			
		||||
 * $observer = new HTTP_Request2_Observer_Log();
 | 
			
		||||
 * $request->attach($observer);
 | 
			
		||||
 * $request->send();
 | 
			
		||||
 * </code>
 | 
			
		||||
 *
 | 
			
		||||
 * A more complex example with PEAR Log:
 | 
			
		||||
 * <code>
 | 
			
		||||
 * require_once 'HTTP/Request2.php';
 | 
			
		||||
 * require_once 'HTTP/Request2/Observer/Log.php';
 | 
			
		||||
 * require_once 'Log.php';
 | 
			
		||||
 *
 | 
			
		||||
 * $request  = new HTTP_Request2('http://www.example.com');
 | 
			
		||||
 * // we want to log with PEAR log
 | 
			
		||||
 * $observer = new HTTP_Request2_Observer_Log(Log::factory('console'));
 | 
			
		||||
 *
 | 
			
		||||
 * // we only want to log received headers
 | 
			
		||||
 * $observer->events = array('receivedHeaders');
 | 
			
		||||
 *
 | 
			
		||||
 * $request->attach($observer);
 | 
			
		||||
 * $request->send();
 | 
			
		||||
 * </code>
 | 
			
		||||
 *
 | 
			
		||||
 * @category HTTP
 | 
			
		||||
 * @package  HTTP_Request2
 | 
			
		||||
 * @author   David Jean Louis <izi@php.net>
 | 
			
		||||
 * @author   Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license  http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version  Release: 0.4.1
 | 
			
		||||
 * @link     http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Observer_Log implements SplObserver
 | 
			
		||||
{
 | 
			
		||||
    // properties {{{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The log target, it can be a a resource or a PEAR Log instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @var resource|Log $target
 | 
			
		||||
     */
 | 
			
		||||
    protected $target = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The events to log.
 | 
			
		||||
     *
 | 
			
		||||
     * @var array $events
 | 
			
		||||
     */
 | 
			
		||||
    public $events = array(
 | 
			
		||||
        'connect',
 | 
			
		||||
        'sentHeaders',
 | 
			
		||||
        'sentBodyPart',
 | 
			
		||||
        'receivedHeaders',
 | 
			
		||||
        'receivedBody',
 | 
			
		||||
        'disconnect',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // }}}
 | 
			
		||||
    // __construct() {{{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $target Can be a file path (default: php://output), a resource,
 | 
			
		||||
     *                      or an instance of the PEAR Log class.
 | 
			
		||||
     * @param array $events Array of events to listen to (default: all events)
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($target = 'php://output', array $events = array())
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($events)) {
 | 
			
		||||
            $this->events = $events;
 | 
			
		||||
        }
 | 
			
		||||
        if (is_resource($target) || $target instanceof Log) {
 | 
			
		||||
            $this->target = $target;
 | 
			
		||||
        } elseif (false === ($this->target = @fopen($target, 'w'))) {
 | 
			
		||||
            throw new HTTP_Request2_Exception("Unable to open '{$target}'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // }}}
 | 
			
		||||
    // update() {{{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the request notify us of an event.
 | 
			
		||||
     *
 | 
			
		||||
     * @param HTTP_Request2 $subject The HTTP_Request2 instance
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function update(SplSubject $subject)
 | 
			
		||||
    {
 | 
			
		||||
        $event = $subject->getLastEvent();
 | 
			
		||||
        if (!in_array($event['name'], $this->events)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch ($event['name']) {
 | 
			
		||||
        case 'connect':
 | 
			
		||||
            $this->log('* Connected to ' . $event['data']);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'sentHeaders':
 | 
			
		||||
            $headers = explode("\r\n", $event['data']);
 | 
			
		||||
            array_pop($headers);
 | 
			
		||||
            foreach ($headers as $header) {
 | 
			
		||||
                $this->log('> ' . $header);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'sentBodyPart':
 | 
			
		||||
            $this->log('> ' . $event['data']);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'receivedHeaders':
 | 
			
		||||
            $this->log(sprintf('< HTTP/%s %s %s',
 | 
			
		||||
                $event['data']->getVersion(),
 | 
			
		||||
                $event['data']->getStatus(),
 | 
			
		||||
                $event['data']->getReasonPhrase()));
 | 
			
		||||
            $headers = $event['data']->getHeader();
 | 
			
		||||
            foreach ($headers as $key => $val) {
 | 
			
		||||
                $this->log('< ' . $key . ': ' . $val);
 | 
			
		||||
            }
 | 
			
		||||
            $this->log('< ');
 | 
			
		||||
            break;
 | 
			
		||||
        case 'receivedBody':
 | 
			
		||||
            $this->log($event['data']->getBody());
 | 
			
		||||
            break;
 | 
			
		||||
        case 'disconnect':
 | 
			
		||||
            $this->log('* Disconnected');
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // }}}
 | 
			
		||||
    // log() {{{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log the given message to the configured target.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $message Message to display
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    protected function log($message)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->target instanceof Log) {
 | 
			
		||||
            $this->target->debug($message);
 | 
			
		||||
        } elseif (is_resource($this->target)) {
 | 
			
		||||
            fwrite($this->target, $message . "\r\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // }}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										549
									
								
								extlib/HTTP/Request2/Response.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										549
									
								
								extlib/HTTP/Request2/Response.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,549 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a HTTP response
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *    * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *    * The names of the authors may not be used to endorse or promote products
 | 
			
		||||
 *      derived from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version    CVS: $Id: Response.php 287948 2009-09-01 17:12:18Z avb $
 | 
			
		||||
 * @link       http://pear.php.net/package/HTTP_Request2
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception class for HTTP_Request2 package
 | 
			
		||||
 */ 
 | 
			
		||||
require_once 'HTTP/Request2/Exception.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class representing a HTTP response
 | 
			
		||||
 *
 | 
			
		||||
 * The class is designed to be used in "streaming" scenario, building the
 | 
			
		||||
 * response as it is being received:
 | 
			
		||||
 * <code>
 | 
			
		||||
 * $statusLine = read_status_line();
 | 
			
		||||
 * $response = new HTTP_Request2_Response($statusLine);
 | 
			
		||||
 * do {
 | 
			
		||||
 *     $headerLine = read_header_line();
 | 
			
		||||
 *     $response->parseHeaderLine($headerLine);
 | 
			
		||||
 * } while ($headerLine != '');
 | 
			
		||||
 * 
 | 
			
		||||
 * while ($chunk = read_body()) {
 | 
			
		||||
 *     $response->appendBody($chunk);
 | 
			
		||||
 * }
 | 
			
		||||
 * 
 | 
			
		||||
 * var_dump($response->getHeader(), $response->getCookies(), $response->getBody());
 | 
			
		||||
 * </code>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * @category   HTTP
 | 
			
		||||
 * @package    HTTP_Request2
 | 
			
		||||
 * @author     Alexey Borzov <avb@php.net>
 | 
			
		||||
 * @version    Release: 0.4.1
 | 
			
		||||
 * @link       http://tools.ietf.org/html/rfc2616#section-6
 | 
			
		||||
 */
 | 
			
		||||
class HTTP_Request2_Response
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * HTTP protocol version (e.g. 1.0, 1.1)
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    protected $version;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Status code
 | 
			
		||||
    * @var  integer
 | 
			
		||||
    * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
 | 
			
		||||
    */
 | 
			
		||||
    protected $code;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Reason phrase
 | 
			
		||||
    * @var  string
 | 
			
		||||
    * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
 | 
			
		||||
    */
 | 
			
		||||
    protected $reasonPhrase;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Associative array of response headers
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $headers = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Cookies set in the response
 | 
			
		||||
    * @var  array
 | 
			
		||||
    */
 | 
			
		||||
    protected $cookies = array();
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Name of last header processed by parseHederLine()
 | 
			
		||||
    *
 | 
			
		||||
    * Used to handle the headers that span multiple lines
 | 
			
		||||
    *
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    protected $lastHeader = null;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Response body
 | 
			
		||||
    * @var  string
 | 
			
		||||
    */
 | 
			
		||||
    protected $body = '';
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Whether the body is still encoded by Content-Encoding
 | 
			
		||||
    *
 | 
			
		||||
    * cURL provides the decoded body to the callback; if we are reading from
 | 
			
		||||
    * socket the body is still gzipped / deflated
 | 
			
		||||
    *
 | 
			
		||||
    * @var  bool
 | 
			
		||||
    */
 | 
			
		||||
    protected $bodyEncoded;
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Associative array of HTTP status code / reason phrase.
 | 
			
		||||
    *
 | 
			
		||||
    * @var  array
 | 
			
		||||
    * @link http://tools.ietf.org/html/rfc2616#section-10
 | 
			
		||||
    */
 | 
			
		||||
    protected static $phrases = array(
 | 
			
		||||
 | 
			
		||||
        // 1xx: Informational - Request received, continuing process
 | 
			
		||||
        100 => 'Continue',
 | 
			
		||||
        101 => 'Switching Protocols',
 | 
			
		||||
 | 
			
		||||
        // 2xx: Success - The action was successfully received, understood and
 | 
			
		||||
        // accepted
 | 
			
		||||
        200 => 'OK',
 | 
			
		||||
        201 => 'Created',
 | 
			
		||||
        202 => 'Accepted',
 | 
			
		||||
        203 => 'Non-Authoritative Information',
 | 
			
		||||
        204 => 'No Content',
 | 
			
		||||
        205 => 'Reset Content',
 | 
			
		||||
        206 => 'Partial Content',
 | 
			
		||||
 | 
			
		||||
        // 3xx: Redirection - Further action must be taken in order to complete
 | 
			
		||||
        // the request
 | 
			
		||||
        300 => 'Multiple Choices',
 | 
			
		||||
        301 => 'Moved Permanently',
 | 
			
		||||
        302 => 'Found',  // 1.1
 | 
			
		||||
        303 => 'See Other',
 | 
			
		||||
        304 => 'Not Modified',
 | 
			
		||||
        305 => 'Use Proxy',
 | 
			
		||||
        307 => 'Temporary Redirect',
 | 
			
		||||
 | 
			
		||||
        // 4xx: Client Error - The request contains bad syntax or cannot be 
 | 
			
		||||
        // fulfilled
 | 
			
		||||
        400 => 'Bad Request',
 | 
			
		||||
        401 => 'Unauthorized',
 | 
			
		||||
        402 => 'Payment Required',
 | 
			
		||||
        403 => 'Forbidden',
 | 
			
		||||
        404 => 'Not Found',
 | 
			
		||||
        405 => 'Method Not Allowed',
 | 
			
		||||
        406 => 'Not Acceptable',
 | 
			
		||||
        407 => 'Proxy Authentication Required',
 | 
			
		||||
        408 => 'Request Timeout',
 | 
			
		||||
        409 => 'Conflict',
 | 
			
		||||
        410 => 'Gone',
 | 
			
		||||
        411 => 'Length Required',
 | 
			
		||||
        412 => 'Precondition Failed',
 | 
			
		||||
        413 => 'Request Entity Too Large',
 | 
			
		||||
        414 => 'Request-URI Too Long',
 | 
			
		||||
        415 => 'Unsupported Media Type',
 | 
			
		||||
        416 => 'Requested Range Not Satisfiable',
 | 
			
		||||
        417 => 'Expectation Failed',
 | 
			
		||||
 | 
			
		||||
        // 5xx: Server Error - The server failed to fulfill an apparently
 | 
			
		||||
        // valid request
 | 
			
		||||
        500 => 'Internal Server Error',
 | 
			
		||||
        501 => 'Not Implemented',
 | 
			
		||||
        502 => 'Bad Gateway',
 | 
			
		||||
        503 => 'Service Unavailable',
 | 
			
		||||
        504 => 'Gateway Timeout',
 | 
			
		||||
        505 => 'HTTP Version Not Supported',
 | 
			
		||||
        509 => 'Bandwidth Limit Exceeded',
 | 
			
		||||
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Constructor, parses the response status line
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  Response status line (e.g. "HTTP/1.1 200 OK")
 | 
			
		||||
    * @param    bool    Whether body is still encoded by Content-Encoding
 | 
			
		||||
    * @throws   HTTP_Request2_Exception if status line is invalid according to spec
 | 
			
		||||
    */
 | 
			
		||||
    public function __construct($statusLine, $bodyEncoded = true)
 | 
			
		||||
    {
 | 
			
		||||
        if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception("Malformed response: {$statusLine}");
 | 
			
		||||
        }
 | 
			
		||||
        $this->version = $m[1];
 | 
			
		||||
        $this->code    = intval($m[2]);
 | 
			
		||||
        if (!empty($m[3])) {
 | 
			
		||||
            $this->reasonPhrase = trim($m[3]);
 | 
			
		||||
        } elseif (!empty(self::$phrases[$this->code])) {
 | 
			
		||||
            $this->reasonPhrase = self::$phrases[$this->code];
 | 
			
		||||
        }
 | 
			
		||||
        $this->bodyEncoded = (bool)$bodyEncoded;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Parses the line from HTTP response filling $headers array
 | 
			
		||||
    *
 | 
			
		||||
    * The method should be called after reading the line from socket or receiving 
 | 
			
		||||
    * it into cURL callback. Passing an empty string here indicates the end of
 | 
			
		||||
    * response headers and triggers additional processing, so be sure to pass an
 | 
			
		||||
    * empty string in the end.
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  Line from HTTP response
 | 
			
		||||
    */
 | 
			
		||||
    public function parseHeaderLine($headerLine)
 | 
			
		||||
    {
 | 
			
		||||
        $headerLine = trim($headerLine, "\r\n");
 | 
			
		||||
 | 
			
		||||
        // empty string signals the end of headers, process the received ones
 | 
			
		||||
        if ('' == $headerLine) {
 | 
			
		||||
            if (!empty($this->headers['set-cookie'])) {
 | 
			
		||||
                $cookies = is_array($this->headers['set-cookie'])?
 | 
			
		||||
                           $this->headers['set-cookie']:
 | 
			
		||||
                           array($this->headers['set-cookie']);
 | 
			
		||||
                foreach ($cookies as $cookieString) {
 | 
			
		||||
                    $this->parseCookie($cookieString);
 | 
			
		||||
                }
 | 
			
		||||
                unset($this->headers['set-cookie']);
 | 
			
		||||
            }
 | 
			
		||||
            foreach (array_keys($this->headers) as $k) {
 | 
			
		||||
                if (is_array($this->headers[$k])) {
 | 
			
		||||
                    $this->headers[$k] = implode(', ', $this->headers[$k]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        // string of the form header-name: header value
 | 
			
		||||
        } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) {
 | 
			
		||||
            $name  = strtolower($m[1]);
 | 
			
		||||
            $value = trim($m[2]);
 | 
			
		||||
            if (empty($this->headers[$name])) {
 | 
			
		||||
                $this->headers[$name] = $value;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!is_array($this->headers[$name])) {
 | 
			
		||||
                    $this->headers[$name] = array($this->headers[$name]);
 | 
			
		||||
                }
 | 
			
		||||
                $this->headers[$name][] = $value;
 | 
			
		||||
            }
 | 
			
		||||
            $this->lastHeader = $name;
 | 
			
		||||
 | 
			
		||||
        // string 
 | 
			
		||||
        } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) {
 | 
			
		||||
            if (!is_array($this->headers[$this->lastHeader])) {
 | 
			
		||||
                $this->headers[$this->lastHeader] .= ' ' . trim($m[1]);
 | 
			
		||||
            } else {
 | 
			
		||||
                $key = count($this->headers[$this->lastHeader]) - 1;
 | 
			
		||||
                $this->headers[$this->lastHeader][$key] .= ' ' . trim($m[1]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } 
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Parses a Set-Cookie header to fill $cookies array
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string    value of Set-Cookie header
 | 
			
		||||
    * @link     http://cgi.netscape.com/newsref/std/cookie_spec.html
 | 
			
		||||
    */
 | 
			
		||||
    protected function parseCookie($cookieString)
 | 
			
		||||
    {
 | 
			
		||||
        $cookie = array(
 | 
			
		||||
            'expires' => null,
 | 
			
		||||
            'domain'  => null,
 | 
			
		||||
            'path'    => null,
 | 
			
		||||
            'secure'  => false
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Only a name=value pair
 | 
			
		||||
        if (!strpos($cookieString, ';')) {
 | 
			
		||||
            $pos = strpos($cookieString, '=');
 | 
			
		||||
            $cookie['name']  = trim(substr($cookieString, 0, $pos));
 | 
			
		||||
            $cookie['value'] = trim(substr($cookieString, $pos + 1));
 | 
			
		||||
 | 
			
		||||
        // Some optional parameters are supplied
 | 
			
		||||
        } else {
 | 
			
		||||
            $elements = explode(';', $cookieString);
 | 
			
		||||
            $pos = strpos($elements[0], '=');
 | 
			
		||||
            $cookie['name']  = trim(substr($elements[0], 0, $pos));
 | 
			
		||||
            $cookie['value'] = trim(substr($elements[0], $pos + 1));
 | 
			
		||||
 | 
			
		||||
            for ($i = 1; $i < count($elements); $i++) {
 | 
			
		||||
                if (false === strpos($elements[$i], '=')) {
 | 
			
		||||
                    $elName  = trim($elements[$i]);
 | 
			
		||||
                    $elValue = null;
 | 
			
		||||
                } else {
 | 
			
		||||
                    list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
 | 
			
		||||
                }
 | 
			
		||||
                $elName = strtolower($elName);
 | 
			
		||||
                if ('secure' == $elName) {
 | 
			
		||||
                    $cookie['secure'] = true;
 | 
			
		||||
                } elseif ('expires' == $elName) {
 | 
			
		||||
                    $cookie['expires'] = str_replace('"', '', $elValue);
 | 
			
		||||
                } elseif ('path' == $elName || 'domain' == $elName) {
 | 
			
		||||
                    $cookie[$elName] = urldecode($elValue);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $cookie[$elName] = $elValue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $this->cookies[] = $cookie;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Appends a string to the response body
 | 
			
		||||
    * @param    string
 | 
			
		||||
    */
 | 
			
		||||
    public function appendBody($bodyChunk)
 | 
			
		||||
    {
 | 
			
		||||
        $this->body .= $bodyChunk;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the status code
 | 
			
		||||
    * @return   integer 
 | 
			
		||||
    */
 | 
			
		||||
    public function getStatus()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the reason phrase
 | 
			
		||||
    * @return   string
 | 
			
		||||
    */
 | 
			
		||||
    public function getReasonPhrase()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->reasonPhrase;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns either the named header or all response headers
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string          Name of header to return
 | 
			
		||||
    * @return   string|array    Value of $headerName header (null if header is
 | 
			
		||||
    *                           not present), array of all response headers if
 | 
			
		||||
    *                           $headerName is null
 | 
			
		||||
    */
 | 
			
		||||
    public function getHeader($headerName = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $headerName) {
 | 
			
		||||
            return $this->headers;
 | 
			
		||||
        } else {
 | 
			
		||||
            $headerName = strtolower($headerName);
 | 
			
		||||
            return isset($this->headers[$headerName])? $this->headers[$headerName]: null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns cookies set in response
 | 
			
		||||
    *
 | 
			
		||||
    * @return   array
 | 
			
		||||
    */
 | 
			
		||||
    public function getCookies()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->cookies;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Returns the body of the response
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    * @throws   HTTP_Request2_Exception if body cannot be decoded
 | 
			
		||||
    */
 | 
			
		||||
    public function getBody()
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->bodyEncoded ||
 | 
			
		||||
            !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))
 | 
			
		||||
        ) {
 | 
			
		||||
            return $this->body;
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
 | 
			
		||||
                $oldEncoding = mb_internal_encoding();
 | 
			
		||||
                mb_internal_encoding('iso-8859-1');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                switch (strtolower($this->getHeader('content-encoding'))) {
 | 
			
		||||
                    case 'gzip':
 | 
			
		||||
                        $decoded = self::decodeGzip($this->body);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'deflate':
 | 
			
		||||
                        $decoded = self::decodeDeflate($this->body);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception $e) {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!empty($oldEncoding)) {
 | 
			
		||||
                mb_internal_encoding($oldEncoding);
 | 
			
		||||
            }
 | 
			
		||||
            if (!empty($e)) {
 | 
			
		||||
                throw $e;
 | 
			
		||||
            }
 | 
			
		||||
            return $decoded;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Get the HTTP version of the response
 | 
			
		||||
    *
 | 
			
		||||
    * @return   string
 | 
			
		||||
    */ 
 | 
			
		||||
    public function getVersion()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Decodes the message-body encoded by gzip
 | 
			
		||||
    *
 | 
			
		||||
    * The real decoding work is done by gzinflate() built-in function, this
 | 
			
		||||
    * method only parses the header and checks data for compliance with
 | 
			
		||||
    * RFC 1952
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  gzip-encoded data
 | 
			
		||||
    * @return   string  decoded data
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    * @link     http://tools.ietf.org/html/rfc1952
 | 
			
		||||
    */
 | 
			
		||||
    public static function decodeGzip($data)
 | 
			
		||||
    {
 | 
			
		||||
        $length = strlen($data);
 | 
			
		||||
        // If it doesn't look like gzip-encoded data, don't bother
 | 
			
		||||
        if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
 | 
			
		||||
            return $data;
 | 
			
		||||
        }
 | 
			
		||||
        if (!function_exists('gzinflate')) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available');
 | 
			
		||||
        }
 | 
			
		||||
        $method = ord(substr($data, 2, 1));
 | 
			
		||||
        if (8 != $method) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Error parsing gzip header: unknown compression method');
 | 
			
		||||
        }
 | 
			
		||||
        $flags = ord(substr($data, 3, 1));
 | 
			
		||||
        if ($flags & 224) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Error parsing gzip header: reserved bits are set');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // header is 10 bytes minimum. may be longer, though.
 | 
			
		||||
        $headerLength = 10;
 | 
			
		||||
        // extra fields, need to skip 'em
 | 
			
		||||
        if ($flags & 4) {
 | 
			
		||||
            if ($length - $headerLength - 2 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $extraLength = unpack('v', substr($data, 10, 2));
 | 
			
		||||
            if ($length - $headerLength - 2 - $extraLength[1] < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $headerLength += $extraLength[1] + 2;
 | 
			
		||||
        }
 | 
			
		||||
        // file name, need to skip that
 | 
			
		||||
        if ($flags & 8) {
 | 
			
		||||
            if ($length - $headerLength - 1 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $filenameLength = strpos(substr($data, $headerLength), chr(0));
 | 
			
		||||
            if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $headerLength += $filenameLength + 1;
 | 
			
		||||
        }
 | 
			
		||||
        // comment, need to skip that also
 | 
			
		||||
        if ($flags & 16) {
 | 
			
		||||
            if ($length - $headerLength - 1 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $commentLength = strpos(substr($data, $headerLength), chr(0));
 | 
			
		||||
            if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $headerLength += $commentLength + 1;
 | 
			
		||||
        }
 | 
			
		||||
        // have a CRC for header. let's check
 | 
			
		||||
        if ($flags & 2) {
 | 
			
		||||
            if ($length - $headerLength - 2 < 8) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
 | 
			
		||||
            }
 | 
			
		||||
            $crcReal   = 0xffff & crc32(substr($data, 0, $headerLength));
 | 
			
		||||
            $crcStored = unpack('v', substr($data, $headerLength, 2));
 | 
			
		||||
            if ($crcReal != $crcStored[1]) {
 | 
			
		||||
                throw new HTTP_Request2_Exception('Header CRC check failed');
 | 
			
		||||
            }
 | 
			
		||||
            $headerLength += 2;
 | 
			
		||||
        }
 | 
			
		||||
        // unpacked data CRC and size at the end of encoded data
 | 
			
		||||
        $tmp = unpack('V2', substr($data, -8));
 | 
			
		||||
        $dataCrc  = $tmp[1];
 | 
			
		||||
        $dataSize = $tmp[2];
 | 
			
		||||
 | 
			
		||||
        // finally, call the gzinflate() function
 | 
			
		||||
        // don't pass $dataSize to gzinflate, see bugs #13135, #14370
 | 
			
		||||
        $unpacked = gzinflate(substr($data, $headerLength, -8));
 | 
			
		||||
        if (false === $unpacked) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('gzinflate() call failed');
 | 
			
		||||
        } elseif ($dataSize != strlen($unpacked)) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Data size check failed');
 | 
			
		||||
        } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Data CRC check failed');
 | 
			
		||||
        }
 | 
			
		||||
        return $unpacked;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
    * Decodes the message-body encoded by deflate
 | 
			
		||||
    *
 | 
			
		||||
    * @param    string  deflate-encoded data
 | 
			
		||||
    * @return   string  decoded data
 | 
			
		||||
    * @throws   HTTP_Request2_Exception
 | 
			
		||||
    */
 | 
			
		||||
    public static function decodeDeflate($data)
 | 
			
		||||
    {
 | 
			
		||||
        if (!function_exists('gzuncompress')) {
 | 
			
		||||
            throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available');
 | 
			
		||||
        }
 | 
			
		||||
        // RFC 2616 defines 'deflate' encoding as zlib format from RFC 1950,
 | 
			
		||||
        // while many applications send raw deflate stream from RFC 1951.
 | 
			
		||||
        // We should check for presence of zlib header and use gzuncompress() or
 | 
			
		||||
        // gzinflate() as needed. See bug #15305
 | 
			
		||||
        $header = unpack('n', substr($data, 0, 2));
 | 
			
		||||
        return (0 == $header[1] % 31)? gzuncompress($data): gzinflate($data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
@@ -1,44 +1,58 @@
 | 
			
		||||
<?php
 | 
			
		||||
// +-----------------------------------------------------------------------+
 | 
			
		||||
// | Copyright (c) 2007-2008, Christian Schmidt, Peytz & Co. A/S           |
 | 
			
		||||
// | All rights reserved.                                                  |
 | 
			
		||||
// |                                                                       |
 | 
			
		||||
// | Redistribution and use in source and binary forms, with or without    |
 | 
			
		||||
// | modification, are permitted provided that the following conditions    |
 | 
			
		||||
// | are met:                                                              |
 | 
			
		||||
// |                                                                       |
 | 
			
		||||
// | o Redistributions of source code must retain the above copyright      |
 | 
			
		||||
// |   notice, this list of conditions and the following disclaimer.       |
 | 
			
		||||
// | o Redistributions in binary form must reproduce the above copyright   |
 | 
			
		||||
// |   notice, this list of conditions and the following disclaimer in the |
 | 
			
		||||
// |   documentation and/or other materials provided with the distribution.|
 | 
			
		||||
// | o The names of the authors may not be used to endorse or promote      |
 | 
			
		||||
// |   products derived from this software without specific prior written  |
 | 
			
		||||
// |   permission.                                                         |
 | 
			
		||||
// |                                                                       |
 | 
			
		||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
 | 
			
		||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
 | 
			
		||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
 | 
			
		||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
 | 
			
		||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
 | 
			
		||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
 | 
			
		||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
 | 
			
		||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
 | 
			
		||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
 | 
			
		||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
 | 
			
		||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
 | 
			
		||||
// |                                                                       |
 | 
			
		||||
// +-----------------------------------------------------------------------+
 | 
			
		||||
// | Author: Christian Schmidt <schmidt at php dot net>                    |
 | 
			
		||||
// +-----------------------------------------------------------------------+
 | 
			
		||||
//
 | 
			
		||||
// $Id: URL2.php,v 1.10 2008/04/26 21:57:08 schmidt Exp $
 | 
			
		||||
//
 | 
			
		||||
// Net_URL2 Class (PHP5 Only)
 | 
			
		||||
 | 
			
		||||
// This code is released under the BSD License - http://www.opensource.org/licenses/bsd-license.php
 | 
			
		||||
/**
 | 
			
		||||
 * @license BSD License
 | 
			
		||||
 * Net_URL2, a class representing a URL as per RFC 3986.
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENSE:
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2007-2009, Peytz & Co. A/S
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *     notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *     notice, this list of conditions and the following disclaimer in
 | 
			
		||||
 *     the documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   * Neither the name of the PHP_LexerGenerator nor the names of its
 | 
			
		||||
 *     contributors may be used to endorse or promote products derived
 | 
			
		||||
 *     from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
			
		||||
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
			
		||||
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
			
		||||
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | 
			
		||||
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 * @category  Networking
 | 
			
		||||
 * @package   Net_URL2
 | 
			
		||||
 * @author    Christian Schmidt <chsc@peytz.dk>
 | 
			
		||||
 * @copyright 2007-2008 Peytz & Co. A/S
 | 
			
		||||
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version   CVS: $Id: URL2.php 286661 2009-08-02 12:50:54Z schmidt $
 | 
			
		||||
 * @link      http://www.rfc-editor.org/rfc/rfc3986.txt
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a URL as per RFC 3986.
 | 
			
		||||
 *
 | 
			
		||||
 * @category  Networking
 | 
			
		||||
 * @package   Net_URL2
 | 
			
		||||
 * @author    Christian Schmidt <chsc@peytz.dk>
 | 
			
		||||
 * @copyright 2007-2008 Peytz & Co. ApS
 | 
			
		||||
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
 | 
			
		||||
 * @version   Release: @package_version@
 | 
			
		||||
 * @link      http://pear.php.net/package/Net_URL2
 | 
			
		||||
 */
 | 
			
		||||
class Net_URL2
 | 
			
		||||
{
 | 
			
		||||
@@ -75,7 +89,7 @@ class Net_URL2
 | 
			
		||||
    /**
 | 
			
		||||
     * Default options corresponds to how PHP handles $_GET.
 | 
			
		||||
     */
 | 
			
		||||
    private $options = array(
 | 
			
		||||
    private $_options = array(
 | 
			
		||||
        self::OPTION_STRICT           => true,
 | 
			
		||||
        self::OPTION_USE_BRACKETS     => true,
 | 
			
		||||
        self::OPTION_ENCODE_KEYS      => true,
 | 
			
		||||
@@ -86,41 +100,43 @@ class Net_URL2
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $scheme = false;
 | 
			
		||||
    private $_scheme = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $userinfo = false;
 | 
			
		||||
    private $_userinfo = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $host = false;
 | 
			
		||||
    private $_host = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  int|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $port = false;
 | 
			
		||||
    private $_port = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    private $path = '';
 | 
			
		||||
    private $_path = '';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $query = false;
 | 
			
		||||
    private $_query = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    private $fragment = false;
 | 
			
		||||
    private $_fragment = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $url     an absolute or relative URL
 | 
			
		||||
     * @param array  $options
 | 
			
		||||
     * @param array  $options an array of OPTION_xxx constants
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($url, $options = null)
 | 
			
		||||
    {
 | 
			
		||||
@@ -130,12 +146,12 @@ class Net_URL2
 | 
			
		||||
                         ini_get('arg_separator.output'));
 | 
			
		||||
        if (is_array($options)) {
 | 
			
		||||
            foreach ($options as $optionName => $value) {
 | 
			
		||||
                $this->setOption($optionName);
 | 
			
		||||
                $this->setOption($optionName, $value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (preg_match('@^([a-z][a-z0-9.+-]*):@i', $url, $reg)) {
 | 
			
		||||
            $this->scheme = $reg[1];
 | 
			
		||||
            $this->_scheme = $reg[1];
 | 
			
		||||
            $url = substr($url, strlen($reg[0]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -145,19 +161,58 @@ class Net_URL2
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $i = strcspn($url, '?#');
 | 
			
		||||
        $this->path = substr($url, 0, $i);
 | 
			
		||||
        $this->_path = substr($url, 0, $i);
 | 
			
		||||
        $url = substr($url, $i);
 | 
			
		||||
 | 
			
		||||
        if (preg_match('@^\?([^#]*)@', $url, $reg)) {
 | 
			
		||||
            $this->query = $reg[1];
 | 
			
		||||
            $this->_query = $reg[1];
 | 
			
		||||
            $url = substr($url, strlen($reg[0]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($url) {
 | 
			
		||||
            $this->fragment = substr($url, 1);
 | 
			
		||||
            $this->_fragment = substr($url, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Magic Setter.
 | 
			
		||||
     *
 | 
			
		||||
     * This method will magically set the value of a private variable ($var)
 | 
			
		||||
     * with the value passed as the args
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string $var      The private variable to set.
 | 
			
		||||
     * @param  mixed  $arg      An argument of any type.
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function __set($var, $arg)
 | 
			
		||||
    {
 | 
			
		||||
        $method = 'set' . $var;
 | 
			
		||||
        if (method_exists($this, $method)) {
 | 
			
		||||
            $this->$method($arg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Magic Getter.
 | 
			
		||||
     *
 | 
			
		||||
     * This is the magic get method to retrieve the private variable 
 | 
			
		||||
     * that was set by either __set() or it's setter...
 | 
			
		||||
     * 
 | 
			
		||||
     * @param  string $var         The property name to retrieve.
 | 
			
		||||
     * @return mixed  $this->$var  Either a boolean false if the
 | 
			
		||||
     *                             property is not set or the value
 | 
			
		||||
     *                             of the private property.
 | 
			
		||||
     */
 | 
			
		||||
    public function __get($var)
 | 
			
		||||
    {
 | 
			
		||||
        $method = 'get' . $var;
 | 
			
		||||
        if (method_exists($this, $method)) {
 | 
			
		||||
            return $this->$method();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the scheme, e.g. "http" or "urn", or false if there is no
 | 
			
		||||
     * scheme specified, i.e. if this is a relative URL.
 | 
			
		||||
@@ -166,18 +221,23 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getScheme()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->scheme;
 | 
			
		||||
        return $this->_scheme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string|bool $scheme
 | 
			
		||||
     * Sets the scheme, e.g. "http" or "urn". Specify false if there is no
 | 
			
		||||
     * scheme specified, i.e. if this is a relative URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|bool $scheme e.g. "http" or "urn", or false if there is no
 | 
			
		||||
     *                            scheme specified, i.e. if this is a relative
 | 
			
		||||
     *                            URL
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     * @see    getScheme()
 | 
			
		||||
     */
 | 
			
		||||
    public function setScheme($scheme)
 | 
			
		||||
    {
 | 
			
		||||
        $this->scheme = $scheme;
 | 
			
		||||
        $this->_scheme = $scheme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -188,7 +248,9 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getUser()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->userinfo !== false ? preg_replace('@:.*$@', '', $this->userinfo) : false;
 | 
			
		||||
        return $this->_userinfo !== false
 | 
			
		||||
            ? preg_replace('@:.*$@', '', $this->_userinfo)
 | 
			
		||||
            : false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -201,7 +263,9 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getPassword()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->userinfo !== false ? substr(strstr($this->userinfo, ':'), 1) : false;
 | 
			
		||||
        return $this->_userinfo !== false
 | 
			
		||||
            ? substr(strstr($this->_userinfo, ':'), 1)
 | 
			
		||||
            : false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -212,7 +276,7 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getUserinfo()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->userinfo;
 | 
			
		||||
        return $this->_userinfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -220,15 +284,15 @@ class Net_URL2
 | 
			
		||||
     * in the userinfo part as username ":" password.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|bool $userinfo userinfo or username
 | 
			
		||||
     * @param string|bool $password
 | 
			
		||||
     * @param string|bool $password optional password, or false
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setUserinfo($userinfo, $password = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->userinfo = $userinfo;
 | 
			
		||||
        $this->_userinfo = $userinfo;
 | 
			
		||||
        if ($password !== false) {
 | 
			
		||||
            $this->userinfo .= ':' . $password;
 | 
			
		||||
            $this->_userinfo .= ':' . $password;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -236,21 +300,24 @@ class Net_URL2
 | 
			
		||||
     * Returns the host part, or false if there is no authority part, e.g.
 | 
			
		||||
     * relative URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string|bool
 | 
			
		||||
     * @return  string|bool a hostname, an IP address, or false
 | 
			
		||||
     */
 | 
			
		||||
    public function getHost()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->host;
 | 
			
		||||
        return $this->_host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string|bool $host
 | 
			
		||||
     * Sets the host part. Specify false if there is no authority part, e.g.
 | 
			
		||||
     * relative URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|bool $host a hostname, an IP address, or false
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setHost($host)
 | 
			
		||||
    {
 | 
			
		||||
        $this->host = $host;
 | 
			
		||||
        $this->_host = $host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -261,65 +328,72 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getPort()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->port;
 | 
			
		||||
        return $this->_port;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param int|bool $port
 | 
			
		||||
     * Sets the port number. Specify false if there is no port number specified,
 | 
			
		||||
     * i.e. if the default port is to be used.
 | 
			
		||||
     *
 | 
			
		||||
     * @param int|bool $port a port number, or false
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setPort($port)
 | 
			
		||||
    {
 | 
			
		||||
        $this->port = intval($port);
 | 
			
		||||
        $this->_port = intval($port);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
 | 
			
		||||
     * false if there is no authority none.
 | 
			
		||||
     * false if there is no authority.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string|bool
 | 
			
		||||
     */
 | 
			
		||||
    public function getAuthority()
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->host) {
 | 
			
		||||
        if (!$this->_host) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $authority = '';
 | 
			
		||||
 | 
			
		||||
        if ($this->userinfo !== false) {
 | 
			
		||||
            $authority .= $this->userinfo . '@';
 | 
			
		||||
        if ($this->_userinfo !== false) {
 | 
			
		||||
            $authority .= $this->_userinfo . '@';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $authority .= $this->host;
 | 
			
		||||
        $authority .= $this->_host;
 | 
			
		||||
 | 
			
		||||
        if ($this->port !== false) {
 | 
			
		||||
            $authority .= ':' . $this->port;
 | 
			
		||||
        if ($this->_port !== false) {
 | 
			
		||||
            $authority .= ':' . $this->_port;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $authority;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string|false $authority
 | 
			
		||||
     * Sets the authority part, i.e. [ userinfo "@" ] host [ ":" port ]. Specify
 | 
			
		||||
     * false if there is no authority.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|false $authority a hostname or an IP addresse, possibly
 | 
			
		||||
     *                                with userinfo prefixed and port number
 | 
			
		||||
     *                                appended, e.g. "foo:bar@example.org:81".
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setAuthority($authority)
 | 
			
		||||
    {
 | 
			
		||||
        $this->user = false;
 | 
			
		||||
        $this->pass = false;
 | 
			
		||||
        $this->host = false;
 | 
			
		||||
        $this->port = false;
 | 
			
		||||
        if (preg_match('@^(([^\@]+)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
 | 
			
		||||
        $this->_userinfo = false;
 | 
			
		||||
        $this->_host     = false;
 | 
			
		||||
        $this->_port     = false;
 | 
			
		||||
        if (preg_match('@^(([^\@]*)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
 | 
			
		||||
            if ($reg[1]) {
 | 
			
		||||
                $this->userinfo = $reg[2];
 | 
			
		||||
                $this->_userinfo = $reg[2];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->host = $reg[3];
 | 
			
		||||
            $this->_host = $reg[3];
 | 
			
		||||
            if (isset($reg[5])) {
 | 
			
		||||
                $this->port = intval($reg[5]);
 | 
			
		||||
                $this->_port = intval($reg[5]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -331,65 +405,74 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function getPath()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->path;
 | 
			
		||||
        return $this->_path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $path
 | 
			
		||||
     * Sets the path part (possibly an empty string).
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $path a path
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setPath($path)
 | 
			
		||||
    {
 | 
			
		||||
        $this->path = $path;
 | 
			
		||||
        $this->_path = $path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the query string (excluding the leading "?"), or false if "?"
 | 
			
		||||
     * isn't present in the URL.
 | 
			
		||||
     * is not present in the URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string|bool
 | 
			
		||||
     * @see     self::getQueryVariables()
 | 
			
		||||
     */
 | 
			
		||||
    public function getQuery()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->query;
 | 
			
		||||
        return $this->_query;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string|bool $query
 | 
			
		||||
     * Sets the query string (excluding the leading "?"). Specify false if "?"
 | 
			
		||||
     * is not present in the URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|bool $query a query string, e.g. "foo=1&bar=2"
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     * @see   self::setQueryVariables()
 | 
			
		||||
     */
 | 
			
		||||
    public function setQuery($query)
 | 
			
		||||
    {
 | 
			
		||||
        $this->query = $query;
 | 
			
		||||
        $this->_query = $query;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the fragment name, or false if "#" isn't present in the URL.
 | 
			
		||||
     * Returns the fragment name, or false if "#" is not present in the URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string|bool
 | 
			
		||||
     */
 | 
			
		||||
    public function getFragment()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->fragment;
 | 
			
		||||
        return $this->_fragment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string|bool $fragment
 | 
			
		||||
     * Sets the fragment name. Specify false if "#" is not present in the URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|bool $fragment a fragment excluding the leading "#", or
 | 
			
		||||
     *                              false
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function setFragment($fragment)
 | 
			
		||||
    {
 | 
			
		||||
        $this->fragment = $fragment;
 | 
			
		||||
        $this->_fragment = $fragment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the query string like an array as the variables would appear in
 | 
			
		||||
     * $_GET in a PHP script.
 | 
			
		||||
     * $_GET in a PHP script. If the URL does not contain a "?", an empty array
 | 
			
		||||
     * is returned.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  array
 | 
			
		||||
     */
 | 
			
		||||
@@ -398,7 +481,7 @@ class Net_URL2
 | 
			
		||||
        $pattern = '/[' .
 | 
			
		||||
                   preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') .
 | 
			
		||||
                   ']/';
 | 
			
		||||
        $parts   = preg_split($pattern, $this->query, -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
        $parts   = preg_split($pattern, $this->_query, -1, PREG_SPLIT_NO_EMPTY);
 | 
			
		||||
        $return  = array();
 | 
			
		||||
 | 
			
		||||
        foreach ($parts as $part) {
 | 
			
		||||
@@ -445,6 +528,8 @@ class Net_URL2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the query string to the specified variable in the query string.
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $array (name => value) array
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
@@ -452,11 +537,11 @@ class Net_URL2
 | 
			
		||||
    public function setQueryVariables(array $array)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$array) {
 | 
			
		||||
            $this->query = false;
 | 
			
		||||
            $this->_query = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            foreach ($array as $name => $value) {
 | 
			
		||||
                if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
 | 
			
		||||
                    $name = rawurlencode($name);
 | 
			
		||||
                    $name = self::urlencode($name);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (is_array($value)) {
 | 
			
		||||
@@ -466,19 +551,21 @@ class Net_URL2
 | 
			
		||||
                            : ($name . '=' . $v);
 | 
			
		||||
                    }
 | 
			
		||||
                } elseif (!is_null($value)) {
 | 
			
		||||
                    $parts[] = $name . '=' . $value;
 | 
			
		||||
                    $parts[] = $name . '=' . self::urlencode($value);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $parts[] = $name;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $this->query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT),
 | 
			
		||||
            $this->_query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT),
 | 
			
		||||
                                    $parts);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $name
 | 
			
		||||
     * @param mixed  $value
 | 
			
		||||
     * Sets the specified variable in the query string.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $name  variable name
 | 
			
		||||
     * @param mixed  $value variable value
 | 
			
		||||
     *
 | 
			
		||||
     * @return  array
 | 
			
		||||
     */
 | 
			
		||||
@@ -490,7 +577,9 @@ class Net_URL2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $name
 | 
			
		||||
     * Removes the specifed variable from the query string.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $name a query string variable, e.g. "foo" in "?foo=1"
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
@@ -511,27 +600,38 @@ class Net_URL2
 | 
			
		||||
        // See RFC 3986, section 5.3
 | 
			
		||||
        $url = "";
 | 
			
		||||
 | 
			
		||||
        if ($this->scheme !== false) {
 | 
			
		||||
            $url .= $this->scheme . ':';
 | 
			
		||||
        if ($this->_scheme !== false) {
 | 
			
		||||
            $url .= $this->_scheme . ':';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $authority = $this->getAuthority();
 | 
			
		||||
        if ($authority !== false) {
 | 
			
		||||
            $url .= '//' . $authority;
 | 
			
		||||
        }
 | 
			
		||||
        $url .= $this->path;
 | 
			
		||||
        $url .= $this->_path;
 | 
			
		||||
 | 
			
		||||
        if ($this->query !== false) {
 | 
			
		||||
            $url .= '?' . $this->query;
 | 
			
		||||
        if ($this->_query !== false) {
 | 
			
		||||
            $url .= '?' . $this->_query;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->fragment !== false) {
 | 
			
		||||
            $url .= '#' . $this->fragment;
 | 
			
		||||
        if ($this->_fragment !== false) {
 | 
			
		||||
            $url .= '#' . $this->_fragment;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        return $url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a string representation of this URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string
 | 
			
		||||
     * @see toString()
 | 
			
		||||
     */
 | 
			
		||||
    public function __toString()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getURL();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 
 | 
			
		||||
     * Returns a normalized string representation of this URL. This is useful
 | 
			
		||||
     * for comparison of URLs.
 | 
			
		||||
@@ -555,36 +655,38 @@ class Net_URL2
 | 
			
		||||
        // See RFC 3886, section 6
 | 
			
		||||
 | 
			
		||||
        // Schemes are case-insensitive
 | 
			
		||||
        if ($this->scheme) {
 | 
			
		||||
            $this->scheme = strtolower($this->scheme);
 | 
			
		||||
        if ($this->_scheme) {
 | 
			
		||||
            $this->_scheme = strtolower($this->_scheme);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Hostnames are case-insensitive
 | 
			
		||||
        if ($this->host) {
 | 
			
		||||
            $this->host = strtolower($this->host);
 | 
			
		||||
        if ($this->_host) {
 | 
			
		||||
            $this->_host = strtolower($this->_host);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Remove default port number for known schemes (RFC 3986, section 6.2.3)
 | 
			
		||||
        if ($this->port &&
 | 
			
		||||
            $this->scheme &&
 | 
			
		||||
            $this->port == getservbyname($this->scheme, 'tcp')) {
 | 
			
		||||
        if ($this->_port &&
 | 
			
		||||
            $this->_scheme &&
 | 
			
		||||
            $this->_port == getservbyname($this->_scheme, 'tcp')) {
 | 
			
		||||
 | 
			
		||||
            $this->port = false;
 | 
			
		||||
            $this->_port = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
 | 
			
		||||
        foreach (array('userinfo', 'host', 'path') as $part) {
 | 
			
		||||
        foreach (array('_userinfo', '_host', '_path') as $part) {
 | 
			
		||||
            if ($this->$part) {
 | 
			
		||||
                $this->$part  = preg_replace('/%[0-9a-f]{2}/ie', 'strtoupper("\0")', $this->$part);
 | 
			
		||||
                $this->$part = preg_replace('/%[0-9a-f]{2}/ie',
 | 
			
		||||
                                            'strtoupper("\0")',
 | 
			
		||||
                                            $this->$part);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Path segment normalization (RFC 3986, section 6.2.2.3)
 | 
			
		||||
        $this->path = self::removeDotSegments($this->path);
 | 
			
		||||
        $this->_path = self::removeDotSegments($this->_path);
 | 
			
		||||
 | 
			
		||||
        // Scheme based normalization (RFC 3986, section 6.2.3)
 | 
			
		||||
        if ($this->host && !$this->path) {
 | 
			
		||||
            $this->path = '/';
 | 
			
		||||
        if ($this->_host && !$this->_path) {
 | 
			
		||||
            $this->_path = '/';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -595,7 +697,7 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function isAbsolute()
 | 
			
		||||
    {
 | 
			
		||||
        return (bool) $this->scheme;
 | 
			
		||||
        return (bool) $this->_scheme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -608,7 +710,7 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    public function resolve($reference)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($reference)) {
 | 
			
		||||
        if (!$reference instanceof Net_URL2) {
 | 
			
		||||
            $reference = new self($reference);
 | 
			
		||||
        }
 | 
			
		||||
        if (!$this->isAbsolute()) {
 | 
			
		||||
@@ -617,54 +719,54 @@ class Net_URL2
 | 
			
		||||
 | 
			
		||||
        // A non-strict parser may ignore a scheme in the reference if it is
 | 
			
		||||
        // identical to the base URI's scheme.
 | 
			
		||||
        if (!$this->getOption(self::OPTION_STRICT) && $reference->scheme == $this->scheme) {
 | 
			
		||||
            $reference->scheme = false;
 | 
			
		||||
        if (!$this->getOption(self::OPTION_STRICT) && $reference->_scheme == $this->_scheme) {
 | 
			
		||||
            $reference->_scheme = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $target = new self('');
 | 
			
		||||
        if ($reference->scheme !== false) {
 | 
			
		||||
            $target->scheme = $reference->scheme;
 | 
			
		||||
        if ($reference->_scheme !== false) {
 | 
			
		||||
            $target->_scheme = $reference->_scheme;
 | 
			
		||||
            $target->setAuthority($reference->getAuthority());
 | 
			
		||||
            $target->path  = self::removeDotSegments($reference->path);
 | 
			
		||||
            $target->query = $reference->query;
 | 
			
		||||
            $target->_path  = self::removeDotSegments($reference->_path);
 | 
			
		||||
            $target->_query = $reference->_query;
 | 
			
		||||
        } else {
 | 
			
		||||
            $authority = $reference->getAuthority();
 | 
			
		||||
            if ($authority !== false) {
 | 
			
		||||
                $target->setAuthority($authority);
 | 
			
		||||
                $target->path  = self::removeDotSegments($reference->path);
 | 
			
		||||
                $target->query = $reference->query;
 | 
			
		||||
                $target->_path  = self::removeDotSegments($reference->_path);
 | 
			
		||||
                $target->_query = $reference->_query;
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($reference->path == '') {
 | 
			
		||||
                    $target->path = $this->path;
 | 
			
		||||
                    if ($reference->query !== false) {
 | 
			
		||||
                        $target->query = $reference->query;
 | 
			
		||||
                if ($reference->_path == '') {
 | 
			
		||||
                    $target->_path = $this->_path;
 | 
			
		||||
                    if ($reference->_query !== false) {
 | 
			
		||||
                        $target->_query = $reference->_query;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $target->query = $this->query;
 | 
			
		||||
                        $target->_query = $this->_query;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (substr($reference->path, 0, 1) == '/') {
 | 
			
		||||
                        $target->path = self::removeDotSegments($reference->path);
 | 
			
		||||
                    if (substr($reference->_path, 0, 1) == '/') {
 | 
			
		||||
                        $target->_path = self::removeDotSegments($reference->_path);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Merge paths (RFC 3986, section 5.2.3)
 | 
			
		||||
                        if ($this->host !== false && $this->path == '') {
 | 
			
		||||
                            $target->path = '/' . $this->path;
 | 
			
		||||
                        if ($this->_host !== false && $this->_path == '') {
 | 
			
		||||
                            $target->_path = '/' . $this->_path;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $i = strrpos($this->path, '/');
 | 
			
		||||
                            $i = strrpos($this->_path, '/');
 | 
			
		||||
                            if ($i !== false) {
 | 
			
		||||
                                $target->path = substr($this->path, 0, $i + 1);
 | 
			
		||||
                                $target->_path = substr($this->_path, 0, $i + 1);
 | 
			
		||||
                            }
 | 
			
		||||
                            $target->path .= $reference->path;
 | 
			
		||||
                            $target->_path .= $reference->_path;
 | 
			
		||||
                        }
 | 
			
		||||
                        $target->path = self::removeDotSegments($target->path);
 | 
			
		||||
                        $target->_path = self::removeDotSegments($target->_path);
 | 
			
		||||
                    }
 | 
			
		||||
                    $target->query = $reference->query;
 | 
			
		||||
                    $target->_query = $reference->_query;
 | 
			
		||||
                }
 | 
			
		||||
                $target->setAuthority($this->getAuthority());
 | 
			
		||||
            }
 | 
			
		||||
            $target->scheme = $this->scheme;
 | 
			
		||||
            $target->_scheme = $this->_scheme;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $target->fragment = $reference->fragment;
 | 
			
		||||
        $target->_fragment = $reference->_fragment;
 | 
			
		||||
 | 
			
		||||
        return $target;
 | 
			
		||||
    }
 | 
			
		||||
@@ -677,7 +779,7 @@ class Net_URL2
 | 
			
		||||
     *
 | 
			
		||||
     * @return string a path
 | 
			
		||||
     */
 | 
			
		||||
    private static function removeDotSegments($path)
 | 
			
		||||
    public static function removeDotSegments($path)
 | 
			
		||||
    {
 | 
			
		||||
        $output = '';
 | 
			
		||||
 | 
			
		||||
@@ -685,28 +787,25 @@ class Net_URL2
 | 
			
		||||
        // method
 | 
			
		||||
        $j = 0; 
 | 
			
		||||
        while ($path && $j++ < 100) {
 | 
			
		||||
            // Step A
 | 
			
		||||
            if (substr($path, 0, 2) == './') {
 | 
			
		||||
                // Step 2.A
 | 
			
		||||
                $path = substr($path, 2);
 | 
			
		||||
            } elseif (substr($path, 0, 3) == '../') {
 | 
			
		||||
                // Step 2.A
 | 
			
		||||
                $path = substr($path, 3);
 | 
			
		||||
 | 
			
		||||
            // Step B
 | 
			
		||||
            } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
 | 
			
		||||
                // Step 2.B
 | 
			
		||||
                $path = '/' . substr($path, 3);
 | 
			
		||||
 | 
			
		||||
            // Step C
 | 
			
		||||
            } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
 | 
			
		||||
                // Step 2.C
 | 
			
		||||
                $path   = '/' . substr($path, 4);
 | 
			
		||||
                $i      = strrpos($output, '/');
 | 
			
		||||
                $output = $i === false ? '' : substr($output, 0, $i);
 | 
			
		||||
 | 
			
		||||
            // Step D
 | 
			
		||||
            } elseif ($path == '.' || $path == '..') {
 | 
			
		||||
                // Step 2.D
 | 
			
		||||
                $path = '';
 | 
			
		||||
 | 
			
		||||
            // Step E
 | 
			
		||||
            } else {
 | 
			
		||||
                // Step 2.E
 | 
			
		||||
                $i = strpos($path, '/');
 | 
			
		||||
                if ($i === 0) {
 | 
			
		||||
                    $i = strpos($path, '/', 1);
 | 
			
		||||
@@ -722,6 +821,22 @@ class Net_URL2
 | 
			
		||||
        return $output;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Percent-encodes all non-alphanumeric characters except these: _ . - ~
 | 
			
		||||
     * Similar to PHP's rawurlencode(), except that it also encodes ~ in PHP
 | 
			
		||||
     * 5.2.x and earlier.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  $raw the string to encode
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public static function urlencode($string)
 | 
			
		||||
    {
 | 
			
		||||
    	$encoded = rawurlencode($string);
 | 
			
		||||
	// This is only necessary in PHP < 5.3.
 | 
			
		||||
	$encoded = str_replace('%7E', '~', $encoded);
 | 
			
		||||
	return $encoded;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a Net_URL2 instance representing the canonical URL of the
 | 
			
		||||
     * currently executing PHP script.
 | 
			
		||||
@@ -737,13 +852,13 @@ class Net_URL2
 | 
			
		||||
 | 
			
		||||
        // Begin with a relative URL
 | 
			
		||||
        $url = new self($_SERVER['PHP_SELF']);
 | 
			
		||||
        $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
 | 
			
		||||
        $url->host = $_SERVER['SERVER_NAME'];
 | 
			
		||||
        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
 | 
			
		||||
        $url->_host   = $_SERVER['SERVER_NAME'];
 | 
			
		||||
        $port = intval($_SERVER['SERVER_PORT']);
 | 
			
		||||
        if ($url->scheme == 'http' && $port != 80 ||
 | 
			
		||||
            $url->scheme == 'https' && $port != 443) {
 | 
			
		||||
        if ($url->_scheme == 'http' && $port != 80 ||
 | 
			
		||||
            $url->_scheme == 'https' && $port != 443) {
 | 
			
		||||
 | 
			
		||||
            $url->port = $port;
 | 
			
		||||
            $url->_port = $port;
 | 
			
		||||
        }
 | 
			
		||||
        return $url;
 | 
			
		||||
    }
 | 
			
		||||
@@ -773,7 +888,7 @@ class Net_URL2
 | 
			
		||||
 | 
			
		||||
        // Begin with a relative URL
 | 
			
		||||
        $url = new self($_SERVER['REQUEST_URI']);
 | 
			
		||||
        $url->scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
 | 
			
		||||
        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
 | 
			
		||||
        // Set host and possibly port
 | 
			
		||||
        $url->setAuthority($_SERVER['HTTP_HOST']);
 | 
			
		||||
        return $url;
 | 
			
		||||
@@ -792,10 +907,10 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    function setOption($optionName, $value)
 | 
			
		||||
    {
 | 
			
		||||
        if (!array_key_exists($optionName, $this->options)) {
 | 
			
		||||
        if (!array_key_exists($optionName, $this->_options)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $this->options[$optionName] = $value;
 | 
			
		||||
        $this->_options[$optionName] = $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -807,7 +922,7 @@ class Net_URL2
 | 
			
		||||
     */
 | 
			
		||||
    function getOption($optionName)
 | 
			
		||||
    {
 | 
			
		||||
        return isset($this->options[$optionName])
 | 
			
		||||
            ? $this->options[$optionName] : false;
 | 
			
		||||
        return isset($this->_options[$optionName])
 | 
			
		||||
            ? $this->_options[$optionName] : false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,13 @@ $external_libraries=array(
 | 
			
		||||
        'include'=>'HTTP/Request.php',
 | 
			
		||||
        'check_class'=>'HTTP_Request'
 | 
			
		||||
    ),
 | 
			
		||||
    array(
 | 
			
		||||
        'name'=>'HTTP_Request2',
 | 
			
		||||
        'pear'=>'HTTP_Request2',
 | 
			
		||||
        'url'=>'http://pear.php.net/package/HTTP_Request2',
 | 
			
		||||
        'include'=>'HTTP/Request2.php',
 | 
			
		||||
        'check_class'=>'HTTP_Request2'
 | 
			
		||||
    ),
 | 
			
		||||
    array(
 | 
			
		||||
        'name'=>'Mail',
 | 
			
		||||
        'pear'=>'Mail',
 | 
			
		||||
 
 | 
			
		||||
@@ -41,22 +41,17 @@ abstract class ShortUrlApi
 | 
			
		||||
        return strlen($url) >= common_config('site', 'shorturllength');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function http_post($data) {
 | 
			
		||||
        $ch = curl_init();
 | 
			
		||||
        curl_setopt($ch, CURLOPT_URL, $this->service_url);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POST, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
        if (($code < 200) || ($code >= 400)) return false;
 | 
			
		||||
        return $response;
 | 
			
		||||
    protected function http_post($data)
 | 
			
		||||
    {
 | 
			
		||||
        $request = new HTTPClient($this->service_url);
 | 
			
		||||
        return $request->post($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function http_get($url) {
 | 
			
		||||
        $encoded_url = urlencode($url);
 | 
			
		||||
        return file_get_contents("{$this->service_url}$encoded_url");
 | 
			
		||||
    protected function http_get($url)
 | 
			
		||||
    {
 | 
			
		||||
        $service = $this->service_url . urlencode($url);
 | 
			
		||||
        $request = new HTTPClient($service);
 | 
			
		||||
        return $request->get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function tidy($response) {
 | 
			
		||||
 
 | 
			
		||||
@@ -169,6 +169,7 @@ if (isset($conffile)) {
 | 
			
		||||
    $_config_files[] = INSTALLDIR.'/config.php';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
global $_have_a_config;
 | 
			
		||||
$_have_a_config = false;
 | 
			
		||||
 | 
			
		||||
foreach ($_config_files as $_config_file) {
 | 
			
		||||
@@ -187,7 +188,7 @@ function _have_config()
 | 
			
		||||
// XXX: Throw a conniption if database not installed
 | 
			
		||||
// XXX: Find a way to use htmlwriter for this instead of handcoded markup
 | 
			
		||||
if (!_have_config()) {
 | 
			
		||||
  echo '<p>'. _('No configuation file found. ') .'</p>';
 | 
			
		||||
  echo '<p>'. _('No configuration file found. ') .'</p>';
 | 
			
		||||
  echo '<p>'. _('I looked for configuration files in the following places: ') .'<br/> '. implode($_config_files, '<br/>');
 | 
			
		||||
  echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>';
 | 
			
		||||
  echo '<a href="install.php">'. _('Go to the installer.') .'</a>';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,179 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * StatusNet, the distributed open-source microblogging tool
 | 
			
		||||
 *
 | 
			
		||||
 * Utility class for wrapping Curl
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5
 | 
			
		||||
 *
 | 
			
		||||
 * LICENCE: This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * @category  HTTP
 | 
			
		||||
 * @package   StatusNet
 | 
			
		||||
 * @author    Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @copyright 2009 StatusNet, Inc.
 | 
			
		||||
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link      http://status.net/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
if (!defined('STATUSNET')) {
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define(CURLCLIENT_VERSION, "0.1");
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Wrapper for Curl
 | 
			
		||||
 *
 | 
			
		||||
 * Makes Curl HTTP client calls within our HTTPClient framework
 | 
			
		||||
 *
 | 
			
		||||
 * @category HTTP
 | 
			
		||||
 * @package  StatusNet
 | 
			
		||||
 * @author   Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link     http://status.net/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class CurlClient extends HTTPClient
 | 
			
		||||
{
 | 
			
		||||
    function __construct()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function head($url, $headers=null)
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
 | 
			
		||||
        $this->setup($ch);
 | 
			
		||||
 | 
			
		||||
        curl_setopt_array($ch,
 | 
			
		||||
                          array(CURLOPT_NOBODY => true));
 | 
			
		||||
 | 
			
		||||
        if (!is_null($headers)) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return $this->parseResults($result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function get($url, $headers=null)
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
 | 
			
		||||
        $this->setup($ch);
 | 
			
		||||
 | 
			
		||||
        if (!is_null($headers)) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return $this->parseResults($result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function post($url, $headers=null, $body=null)
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
 | 
			
		||||
        $this->setup($ch);
 | 
			
		||||
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POST, true);
 | 
			
		||||
 | 
			
		||||
        if (!is_null($body)) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!is_null($headers)) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return $this->parseResults($result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setup($ch)
 | 
			
		||||
    {
 | 
			
		||||
        curl_setopt_array($ch,
 | 
			
		||||
                          array(CURLOPT_USERAGENT => $this->userAgent(),
 | 
			
		||||
                                CURLOPT_HEADER => true,
 | 
			
		||||
                                CURLOPT_RETURNTRANSFER => true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function userAgent()
 | 
			
		||||
    {
 | 
			
		||||
        $version = curl_version();
 | 
			
		||||
        return parent::userAgent() . " CurlClient/".CURLCLIENT_VERSION . " cURL/" . $version['version'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function parseResults($results)
 | 
			
		||||
    {
 | 
			
		||||
        $resp = new HTTPResponse();
 | 
			
		||||
 | 
			
		||||
        $lines = explode("\r\n", $results);
 | 
			
		||||
 | 
			
		||||
        if (preg_match("#^HTTP/1.[01] (\d\d\d) .+$#", $lines[0], $match)) {
 | 
			
		||||
            $resp->code = $match[1];
 | 
			
		||||
        } else {
 | 
			
		||||
            throw Exception("Bad format: initial line is not HTTP status line");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $lastk = null;
 | 
			
		||||
 | 
			
		||||
        for ($i = 1; $i < count($lines); $i++) {
 | 
			
		||||
            $l =& $lines[$i];
 | 
			
		||||
            if (mb_strlen($l) == 0) {
 | 
			
		||||
                $resp->body = implode("\r\n", array_slice($lines, $i + 1));
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (preg_match("#^(\S+):\s+(.*)$#", $l, $match)) {
 | 
			
		||||
                $k = $match[1];
 | 
			
		||||
                $v = $match[2];
 | 
			
		||||
 | 
			
		||||
                if (array_key_exists($k, $resp->headers)) {
 | 
			
		||||
                    if (is_array($resp->headers[$k])) {
 | 
			
		||||
                        $resp->headers[$k][] = $v;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $resp->headers[$k] = array($resp->headers[$k], $v);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $resp->headers[$k] = $v;
 | 
			
		||||
                }
 | 
			
		||||
                $lastk = $k;
 | 
			
		||||
            } else if (preg_match("#^\s+(.*)$#", $l, $match)) {
 | 
			
		||||
                // continuation line
 | 
			
		||||
                if (is_null($lastk)) {
 | 
			
		||||
                    throw Exception("Bad format: initial whitespace in headers");
 | 
			
		||||
                }
 | 
			
		||||
                $h =& $resp->headers[$lastk];
 | 
			
		||||
                if (is_array($h)) {
 | 
			
		||||
                    $n = count($h);
 | 
			
		||||
                    $h[$n-1] .= $match[1];
 | 
			
		||||
                } else {
 | 
			
		||||
                    $h .= $match[1];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $resp;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -228,8 +228,6 @@ $default =
 | 
			
		||||
        array('contentlimit' => null),
 | 
			
		||||
        'message' =>
 | 
			
		||||
        array('contentlimit' => null),
 | 
			
		||||
        'http' =>
 | 
			
		||||
        array('client' => 'curl'), // XXX: should this be the default?
 | 
			
		||||
        'location' =>
 | 
			
		||||
        array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,9 @@ if (!defined('STATUSNET')) {
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
require_once 'HTTP/Request2.php';
 | 
			
		||||
require_once 'HTTP/Request2/Response.php';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Useful structure for HTTP responses
 | 
			
		||||
 *
 | 
			
		||||
@@ -38,18 +41,42 @@ if (!defined('STATUSNET')) {
 | 
			
		||||
 * ways of doing them. This class hides the specifics of what underlying
 | 
			
		||||
 * library (curl or PHP-HTTP or whatever) that's used.
 | 
			
		||||
 *
 | 
			
		||||
 * This extends the HTTP_Request2_Response class with methods to get info
 | 
			
		||||
 * about any followed redirects.
 | 
			
		||||
 *
 | 
			
		||||
 * @category HTTP
 | 
			
		||||
 * @package StatusNet
 | 
			
		||||
 * @author Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @author Brion Vibber <brion@status.net>
 | 
			
		||||
 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link http://status.net/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class HTTPResponse
 | 
			
		||||
class HTTPResponse extends HTTP_Request2_Response
 | 
			
		||||
{
 | 
			
		||||
    public $code = null;
 | 
			
		||||
    public $headers = array();
 | 
			
		||||
    public $body = null;
 | 
			
		||||
    function __construct(HTTP_Request2_Response $response, $url, $redirects=0)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (get_object_vars($response) as $key => $val) {
 | 
			
		||||
            $this->$key = $val;
 | 
			
		||||
        }
 | 
			
		||||
        $this->url = strval($url);
 | 
			
		||||
        $this->redirectCount = intval($redirects);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the count of redirects that have been followed, if any.
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    function getRedirectCount() {
 | 
			
		||||
        return $this->redirectCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the final target URL, after any redirects have been followed.
 | 
			
		||||
     * @return string URL
 | 
			
		||||
     */
 | 
			
		||||
    function getUrl() {
 | 
			
		||||
        return $this->url;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -59,64 +86,133 @@ class HTTPResponse
 | 
			
		||||
 * ways of doing them. This class hides the specifics of what underlying
 | 
			
		||||
 * library (curl or PHP-HTTP or whatever) that's used.
 | 
			
		||||
 *
 | 
			
		||||
 * This extends the PEAR HTTP_Request2 package:
 | 
			
		||||
 * - sends StatusNet-specific User-Agent header
 | 
			
		||||
 * - 'follow_redirects' config option, defaulting off
 | 
			
		||||
 * - 'max_redirs' config option, defaulting to 10
 | 
			
		||||
 * - extended response class adds getRedirectCount() and getUrl() methods
 | 
			
		||||
 * - get() and post() convenience functions return body content directly
 | 
			
		||||
 *
 | 
			
		||||
 * @category HTTP
 | 
			
		||||
 * @package  StatusNet
 | 
			
		||||
 * @author   Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @author   Brion Vibber <brion@status.net>
 | 
			
		||||
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link     http://status.net/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class HTTPClient
 | 
			
		||||
class HTTPClient extends HTTP_Request2
 | 
			
		||||
{
 | 
			
		||||
    static $_client = null;
 | 
			
		||||
 | 
			
		||||
    static function start()
 | 
			
		||||
    function __construct($url=null, $method=self::METHOD_GET, $config=array())
 | 
			
		||||
    {
 | 
			
		||||
        if (!is_null(self::$_client)) {
 | 
			
		||||
            return self::$_client;
 | 
			
		||||
        $this->config['max_redirs'] = 10;
 | 
			
		||||
        $this->config['follow_redirects'] = false;
 | 
			
		||||
        parent::__construct($url, $method, $config);
 | 
			
		||||
        $this->setHeader('User-Agent', $this->userAgent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        $type = common_config('http', 'client');
 | 
			
		||||
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
         case 'curl':
 | 
			
		||||
            self::$_client = new CurlClient();
 | 
			
		||||
            break;
 | 
			
		||||
         default:
 | 
			
		||||
            throw new Exception("Unknown HTTP client type '$type'");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return self::$_client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function head($url, $headers)
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to run a get request and return the response body.
 | 
			
		||||
     * Use when you don't need to get into details of the response.
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed string on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    function get()
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception("HEAD method unimplemented");
 | 
			
		||||
        $this->setMethod(self::METHOD_GET);
 | 
			
		||||
        return $this->doRequest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function get($url, $headers)
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to post form data and return the response body.
 | 
			
		||||
     * Use when you don't need to get into details of the response.
 | 
			
		||||
     *
 | 
			
		||||
     * @param array associative array of form data to submit
 | 
			
		||||
     * @return mixed string on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    public function post($data=array())
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception("GET method unimplemented");
 | 
			
		||||
        $this->setMethod(self::METHOD_POST);
 | 
			
		||||
        if ($data) {
 | 
			
		||||
            $this->addPostParameter($data);
 | 
			
		||||
        }
 | 
			
		||||
        return $this->doRequest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function post($url, $headers, $body)
 | 
			
		||||
    /**
 | 
			
		||||
     * @return mixed string on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    protected function doRequest()
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception("POST method unimplemented");
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->send();
 | 
			
		||||
            $code = $response->getStatus();
 | 
			
		||||
            if (($code < 200) || ($code >= 400)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            return $response->getBody();
 | 
			
		||||
        } catch (HTTP_Request2_Exception $e) {
 | 
			
		||||
            $this->log(LOG_ERR, $e->getMessage());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    function put($url, $headers, $body)
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception("PUT method unimplemented");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function delete($url, $headers)
 | 
			
		||||
    {
 | 
			
		||||
        throw new Exception("DELETE method unimplemented");
 | 
			
		||||
    protected function log($level, $detail) {
 | 
			
		||||
        $method = $this->getMethod();
 | 
			
		||||
        $url = $this->getUrl();
 | 
			
		||||
        common_log($level, __CLASS__ . ": HTTP $method $url - $detail");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pulls up StatusNet's customized user-agent string, so services
 | 
			
		||||
     * we hit can track down the responsible software.
 | 
			
		||||
     */
 | 
			
		||||
    function userAgent()
 | 
			
		||||
    {
 | 
			
		||||
        return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function send()
 | 
			
		||||
    {
 | 
			
		||||
        $maxRedirs = intval($this->config['max_redirs']);
 | 
			
		||||
        if (empty($this->config['follow_redirects'])) {
 | 
			
		||||
            $maxRedirs = 0;
 | 
			
		||||
        }
 | 
			
		||||
        $redirs = 0;
 | 
			
		||||
        do {
 | 
			
		||||
            try {
 | 
			
		||||
                $response = parent::send();
 | 
			
		||||
            } catch (HTTP_Request2_Exception $e) {
 | 
			
		||||
                $this->log(LOG_ERR, $e->getMessage());
 | 
			
		||||
                throw $e;
 | 
			
		||||
            }
 | 
			
		||||
            $code = $response->getStatus();
 | 
			
		||||
            if ($code >= 200 && $code < 300) {
 | 
			
		||||
                $reason = $response->getReasonPhrase();
 | 
			
		||||
                $this->log(LOG_INFO, "$code $reason");
 | 
			
		||||
            } elseif ($code >= 300 && $code < 400) {
 | 
			
		||||
                $url = $this->getUrl();
 | 
			
		||||
                $target = $response->getHeader('Location');
 | 
			
		||||
                
 | 
			
		||||
                if (++$redirs >= $maxRedirs) {
 | 
			
		||||
                    common_log(LOG_ERR, __CLASS__ . ": Too many redirects: skipping $code redirect from $url to $target");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    $this->setUrl($target);
 | 
			
		||||
                    $this->setHeader('Referer', $url);
 | 
			
		||||
                    common_log(LOG_INFO, __CLASS__ . ": Following $code redirect from $url to $target");
 | 
			
		||||
                    continue;
 | 
			
		||||
                } catch (HTTP_Request2_Exception $e) {
 | 
			
		||||
                    common_log(LOG_ERR, __CLASS__ . ": Invalid $code redirect from $url to $target");
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $reason = $response->getReasonPhrase();
 | 
			
		||||
                $this->log(LOG_ERR, "$code $reason");
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        } while ($maxRedirs);
 | 
			
		||||
        return new HTTPResponse($response, $this->getUrl(), $redirs);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ require_once 'OAuth.php';
 | 
			
		||||
 * @link     http://status.net/
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class OAuthClientCurlException extends Exception
 | 
			
		||||
class OAuthClientException extends Exception
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -97,9 +97,14 @@ class OAuthClient
 | 
			
		||||
    function getRequestToken($url)
 | 
			
		||||
    {
 | 
			
		||||
        $response = $this->oAuthGet($url);
 | 
			
		||||
        parse_str($response);
 | 
			
		||||
        $token = new OAuthToken($oauth_token, $oauth_token_secret);
 | 
			
		||||
        $arr = array();
 | 
			
		||||
        parse_str($response, $arr);
 | 
			
		||||
        if (isset($arr['oauth_token']) && isset($arr['oauth_token_secret'])) {
 | 
			
		||||
            $token = new OAuthToken($arr['oauth_token'], @$arr['oauth_token_secret']);
 | 
			
		||||
            return $token;
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new OAuthClientException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -177,7 +182,7 @@ class OAuthClient
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make a HTTP request using cURL.
 | 
			
		||||
     * Make a HTTP request.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $url    Where to make the
 | 
			
		||||
     * @param array  $params post parameters
 | 
			
		||||
@@ -186,40 +191,32 @@ class OAuthClient
 | 
			
		||||
     */
 | 
			
		||||
    function httpRequest($url, $params = null)
 | 
			
		||||
    {
 | 
			
		||||
        $options = array(
 | 
			
		||||
            CURLOPT_RETURNTRANSFER => true,
 | 
			
		||||
            CURLOPT_FAILONERROR    => true,
 | 
			
		||||
            CURLOPT_HEADER         => false,
 | 
			
		||||
            CURLOPT_FOLLOWLOCATION => true,
 | 
			
		||||
            CURLOPT_USERAGENT      => 'StatusNet',
 | 
			
		||||
            CURLOPT_CONNECTTIMEOUT => 120,
 | 
			
		||||
            CURLOPT_TIMEOUT        => 120,
 | 
			
		||||
            CURLOPT_HTTPAUTH       => CURLAUTH_ANY,
 | 
			
		||||
            CURLOPT_SSL_VERIFYPEER => false,
 | 
			
		||||
        $request = new HTTPClient($url);
 | 
			
		||||
        $request->setConfig(array(
 | 
			
		||||
            'connect_timeout' => 120,
 | 
			
		||||
            'timeout' => 120,
 | 
			
		||||
            'follow_redirects' => true,
 | 
			
		||||
            'ssl_verify_peer' => false,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // Twitter is strict about accepting invalid "Expect" headers
 | 
			
		||||
 | 
			
		||||
            CURLOPT_HTTPHEADER => array('Expect:')
 | 
			
		||||
        );
 | 
			
		||||
        $request->setHeader('Expect', '');
 | 
			
		||||
 | 
			
		||||
        if (isset($params)) {
 | 
			
		||||
            $options[CURLOPT_POST]       = true;
 | 
			
		||||
            $options[CURLOPT_POSTFIELDS] = $params;
 | 
			
		||||
            $request->setMethod(HTTP_Request2::METHOD_POST);
 | 
			
		||||
            $request->setBody($params);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
        curl_setopt_array($ch, $options);
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        if ($response === false) {
 | 
			
		||||
            $msg  = curl_error($ch);
 | 
			
		||||
            $code = curl_errno($ch);
 | 
			
		||||
            throw new OAuthClientCurlException($msg, $code);
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $request->send();
 | 
			
		||||
            $code = $response->getStatus();
 | 
			
		||||
            if ($code < 200 || $code >= 400) {
 | 
			
		||||
                throw new OAuthClientException($response->getBody(), $code);
 | 
			
		||||
            }
 | 
			
		||||
            return $response->getBody();
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            throw new OAuthClientException($e->getMessage(), $e->getCode());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								lib/ping.php
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/ping.php
									
									
									
									
									
								
							@@ -44,20 +44,18 @@ function ping_broadcast_notice($notice) {
 | 
			
		||||
																array('nickname' => $profile->nickname)),
 | 
			
		||||
											   $tags));
 | 
			
		||||
 | 
			
		||||
            $context = stream_context_create(array('http' => array('method' => "POST",
 | 
			
		||||
                                                                   'header' =>
 | 
			
		||||
                                                                   "Content-Type: text/xml\r\n".
 | 
			
		||||
                                                                   "User-Agent: StatusNet/".STATUSNET_VERSION."\r\n",
 | 
			
		||||
                                                                   'content' => $req)));
 | 
			
		||||
            $file = file_get_contents($notify_url, false, $context);
 | 
			
		||||
            $request = new HTTPClient($notify_url, HTTP_Request2::METHOD_POST);
 | 
			
		||||
            $request->setHeader('Content-Type', 'text/xml');
 | 
			
		||||
            $request->setBody($req);
 | 
			
		||||
            $httpResponse = $request->send();
 | 
			
		||||
 | 
			
		||||
            if ($file === false || mb_strlen($file) == 0) {
 | 
			
		||||
            if (!$httpResponse || mb_strlen($httpResponse->getBody()) == 0) {
 | 
			
		||||
                common_log(LOG_WARNING,
 | 
			
		||||
                           "XML-RPC empty results for ping ($notify_url, $notice->id) ");
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $response = xmlrpc_decode($file);
 | 
			
		||||
            $response = xmlrpc_decode($httpResponse->getBody());
 | 
			
		||||
 | 
			
		||||
            if (is_array($response) && xmlrpc_is_fault($response)) {
 | 
			
		||||
                common_log(LOG_WARNING,
 | 
			
		||||
 
 | 
			
		||||
@@ -172,26 +172,11 @@ class Snapshot
 | 
			
		||||
    {
 | 
			
		||||
        // XXX: Use OICU2 and OAuth to make authorized requests
 | 
			
		||||
 | 
			
		||||
        $postdata = http_build_query($this->stats);
 | 
			
		||||
 | 
			
		||||
        $opts =
 | 
			
		||||
          array('http' =>
 | 
			
		||||
                array(
 | 
			
		||||
                      'method'  => 'POST',
 | 
			
		||||
                      'header'  => 'Content-type: '.
 | 
			
		||||
                                   'application/x-www-form-urlencoded',
 | 
			
		||||
                      'content' => $postdata,
 | 
			
		||||
                      'user_agent' => 'StatusNet/'.STATUSNET_VERSION
 | 
			
		||||
                      )
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
        $context = stream_context_create($opts);
 | 
			
		||||
 | 
			
		||||
        $reporturl = common_config('snapshot', 'reporturl');
 | 
			
		||||
 | 
			
		||||
        $result = @file_get_contents($reporturl, false, $context);
 | 
			
		||||
 | 
			
		||||
        return $result;
 | 
			
		||||
        $request = new HTTPClient($reporturl, HTTP_Request2::METHOD_POST);
 | 
			
		||||
        $request->addPostParameter($this->stats);
 | 
			
		||||
        $request->send();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
 * @category  Plugin
 | 
			
		||||
 * @package   StatusNet
 | 
			
		||||
 * @author    Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @author    Brion Vibber <brion@status.net>
 | 
			
		||||
 * @copyright 2009 StatusNet, Inc.
 | 
			
		||||
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link      http://status.net/
 | 
			
		||||
@@ -69,14 +70,14 @@ class BlogspamNetPlugin extends Plugin
 | 
			
		||||
    {
 | 
			
		||||
        $args = $this->testArgs($notice);
 | 
			
		||||
        common_debug("Blogspamnet args = " . print_r($args, TRUE));
 | 
			
		||||
        $request = xmlrpc_encode_request('testComment', array($args));
 | 
			
		||||
        $context = stream_context_create(array('http' => array('method' => "POST",
 | 
			
		||||
                                                               'header' =>
 | 
			
		||||
                                                               "Content-Type: text/xml\r\n".
 | 
			
		||||
                                                               "User-Agent: " . $this->userAgent(),
 | 
			
		||||
                                                               'content' => $request)));
 | 
			
		||||
        $file = file_get_contents($this->baseUrl, false, $context);
 | 
			
		||||
        $response = xmlrpc_decode($file);
 | 
			
		||||
        $requestBody = xmlrpc_encode_request('testComment', array($args));
 | 
			
		||||
        
 | 
			
		||||
        $request = new HTTPClient($this->baseUrl, HTTP_Request2::METHOD_POST);
 | 
			
		||||
        $request->addHeader('Content-Type: text/xml');
 | 
			
		||||
        $request->setBody($requestBody);
 | 
			
		||||
        $httpResponse = $request->send();
 | 
			
		||||
        
 | 
			
		||||
        $response = xmlrpc_decode($httpResponse->getBody());
 | 
			
		||||
        if (xmlrpc_is_fault($response)) {
 | 
			
		||||
            throw new ServerException("$response[faultString] ($response[faultCode])", 500);
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -129,18 +129,17 @@ class LinkbackPlugin extends Plugin
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $request = xmlrpc_encode_request('pingback.ping', $args);
 | 
			
		||||
        $context = stream_context_create(array('http' => array('method' => "POST",
 | 
			
		||||
                                                               'header' =>
 | 
			
		||||
                                                               "Content-Type: text/xml\r\n".
 | 
			
		||||
                                                               "User-Agent: " . $this->userAgent(),
 | 
			
		||||
                                                               'content' => $request)));
 | 
			
		||||
        $file = file_get_contents($endpoint, false, $context);
 | 
			
		||||
        if (!$file) {
 | 
			
		||||
        $request = new HTTPClient($endpoint, 'POST');
 | 
			
		||||
        $request->setHeader('User-Agent', $this->userAgent());
 | 
			
		||||
        $request->setHeader('Content-Type', 'text/xml');
 | 
			
		||||
        $request->setBody(xmlrpc_encode_request('pingback.ping', $args));
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $request->send();
 | 
			
		||||
        } catch (HTTP_Request2_Exception $e) {
 | 
			
		||||
            common_log(LOG_WARNING,
 | 
			
		||||
                   "Pingback request failed for '$url' ($endpoint)");
 | 
			
		||||
        } else {
 | 
			
		||||
            $response = xmlrpc_decode($file);
 | 
			
		||||
        }
 | 
			
		||||
        $response = xmlrpc_decode($response->getBody());
 | 
			
		||||
        if (xmlrpc_is_fault($response)) {
 | 
			
		||||
            common_log(LOG_WARNING,
 | 
			
		||||
                   "Pingback error for '$url' ($endpoint): ".
 | 
			
		||||
@@ -151,7 +150,6 @@ class LinkbackPlugin extends Plugin
 | 
			
		||||
                   "'$response'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Largely cadged from trackback_cls.php by
 | 
			
		||||
    // Ran Aroussi <ran@blogish.org>, GPL2 or any later version
 | 
			
		||||
 
 | 
			
		||||
@@ -65,15 +65,6 @@ class SimpleUrlPlugin extends Plugin
 | 
			
		||||
class SimpleUrl extends ShortUrlApi
 | 
			
		||||
{
 | 
			
		||||
    protected function shorten_imp($url) {
 | 
			
		||||
        $curlh = curl_init();
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
 | 
			
		||||
 | 
			
		||||
        curl_setopt($curlh, CURLOPT_URL, $this->service_url.urlencode($url));
 | 
			
		||||
        $short_url = curl_exec($curlh);
 | 
			
		||||
 | 
			
		||||
        curl_close($curlh);
 | 
			
		||||
        return $short_url;
 | 
			
		||||
        return $this->http_get($url);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -152,8 +152,8 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
 | 
			
		||||
            $friends_ids = $client->friendsIds();
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            common_log(LOG_WARNING, $this->name() .
 | 
			
		||||
                       ' - cURL error getting friend ids ' .
 | 
			
		||||
                       $e->getCode() . ' - ' . $e->getMessage());
 | 
			
		||||
                       ' - error getting friend ids: ' .
 | 
			
		||||
                       $e->getMessage());
 | 
			
		||||
            return $friends;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -109,12 +109,16 @@ class TwitterStatusFetcher extends ParallelizingDaemon
 | 
			
		||||
        $flink->find();
 | 
			
		||||
 | 
			
		||||
        $flinks = array();
 | 
			
		||||
        common_log(LOG_INFO, "hello");
 | 
			
		||||
 | 
			
		||||
        while ($flink->fetch()) {
 | 
			
		||||
 | 
			
		||||
            if (($flink->noticesync & FOREIGN_NOTICE_RECV) ==
 | 
			
		||||
                FOREIGN_NOTICE_RECV) {
 | 
			
		||||
                $flinks[] = clone($flink);
 | 
			
		||||
                common_log(LOG_INFO, "sync: foreign id $flink->foreign_id");
 | 
			
		||||
            } else {
 | 
			
		||||
                common_log(LOG_INFO, "nothing to sync");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -515,31 +519,34 @@ class TwitterStatusFetcher extends ParallelizingDaemon
 | 
			
		||||
        return $id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch a remote avatar image and save to local storage.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $url avatar source URL
 | 
			
		||||
     * @param string $filename bare local filename for download
 | 
			
		||||
     * @return bool true on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    function fetchAvatar($url, $filename)
 | 
			
		||||
    {
 | 
			
		||||
        $avatarfile = Avatar::path($filename);
 | 
			
		||||
        common_debug($this->name() . " - Fetching Twitter avatar: $url");
 | 
			
		||||
 | 
			
		||||
        $out = fopen($avatarfile, 'wb');
 | 
			
		||||
        if (!$out) {
 | 
			
		||||
        $request = new HTTPClient($url, 'GET', array(
 | 
			
		||||
            'follow_redirects' => true,
 | 
			
		||||
        ));
 | 
			
		||||
        $data = $request->get();
 | 
			
		||||
        if ($data) {
 | 
			
		||||
            $avatarfile = Avatar::path($filename);
 | 
			
		||||
            $ok = file_put_contents($avatarfile, $data);
 | 
			
		||||
            if (!$ok) {
 | 
			
		||||
                common_log(LOG_WARNING, $this->name() .
 | 
			
		||||
                           " - Couldn't open file $filename");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        common_debug($this->name() . " - Fetching Twitter avatar: $url");
 | 
			
		||||
 | 
			
		||||
        $ch = curl_init();
 | 
			
		||||
        curl_setopt($ch, CURLOPT_URL, $url);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_FILE, $out);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
 | 
			
		||||
        $result = curl_exec($ch);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        fclose($out);
 | 
			
		||||
 | 
			
		||||
        return $result;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -215,7 +215,7 @@ function broadcast_basicauth($notice, $flink)
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        $status = $client->statusesUpdate($statustxt);
 | 
			
		||||
    } catch (BasicAuthCurlException $e) {
 | 
			
		||||
    } catch (HTTP_Request2_Exception $e) {
 | 
			
		||||
        return process_error($e, $flink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,7 @@ class TwitterauthorizationAction extends Action
 | 
			
		||||
 | 
			
		||||
            $auth_link = $client->getAuthorizeLink($req_tok);
 | 
			
		||||
 | 
			
		||||
        } catch (TwitterOAuthClientException $e) {
 | 
			
		||||
        } catch (OAuthClientException $e) {
 | 
			
		||||
            $msg = sprintf('OAuth client cURL error - code: %1s, msg: %2s',
 | 
			
		||||
                           $e->getCode(), $e->getMessage());
 | 
			
		||||
            $this->serverError(_('Couldn\'t link your Twitter account.'));
 | 
			
		||||
 
 | 
			
		||||
@@ -31,26 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception wrapper for cURL errors
 | 
			
		||||
 *
 | 
			
		||||
 * @category Integration
 | 
			
		||||
 * @package  StatusNet
 | 
			
		||||
 * @author Adrian Lang <mail@adrianlang.de>
 | 
			
		||||
 * @author Brenda Wallace <shiny@cpan.org>
 | 
			
		||||
 * @author Craig Andrews <candrews@integralblue.com>
 | 
			
		||||
 * @author Dan Moore <dan@moore.cx>
 | 
			
		||||
 * @author Evan Prodromou <evan@status.net>
 | 
			
		||||
 * @author mEDI <medi@milaro.net>
 | 
			
		||||
 * @author Sarven Capadisli <csarven@status.net>
 | 
			
		||||
 * @author Zach Copley <zach@status.net> * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 | 
			
		||||
 * @link     http://status.net/
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class BasicAuthCurlException extends Exception
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class for talking to the Twitter API with HTTP Basic Auth.
 | 
			
		||||
 *
 | 
			
		||||
@@ -198,45 +178,27 @@ class TwitterBasicAuthClient
 | 
			
		||||
     */
 | 
			
		||||
    function httpRequest($url, $params = null, $auth = true)
 | 
			
		||||
    {
 | 
			
		||||
        $options = array(
 | 
			
		||||
                         CURLOPT_RETURNTRANSFER => true,
 | 
			
		||||
                         CURLOPT_FAILONERROR    => true,
 | 
			
		||||
                         CURLOPT_HEADER         => false,
 | 
			
		||||
                         CURLOPT_FOLLOWLOCATION => true,
 | 
			
		||||
                         CURLOPT_USERAGENT      => 'StatusNet',
 | 
			
		||||
                         CURLOPT_CONNECTTIMEOUT => 120,
 | 
			
		||||
                         CURLOPT_TIMEOUT        => 120,
 | 
			
		||||
                         CURLOPT_HTTPAUTH       => CURLAUTH_ANY,
 | 
			
		||||
                         CURLOPT_SSL_VERIFYPEER => false,
 | 
			
		||||
        $request = new HTTPClient($url, 'GET', array(
 | 
			
		||||
            'follow_redirects' => true,
 | 
			
		||||
            'connect_timeout' => 120,
 | 
			
		||||
            'timeout' => 120,
 | 
			
		||||
            'ssl_verifypeer' => false,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // Twitter is strict about accepting invalid "Expect" headers
 | 
			
		||||
 | 
			
		||||
                         CURLOPT_HTTPHEADER => array('Expect:')
 | 
			
		||||
                         );
 | 
			
		||||
        $request->setHeader('Expect', '');
 | 
			
		||||
 | 
			
		||||
        if (isset($params)) {
 | 
			
		||||
            $options[CURLOPT_POST]       = true;
 | 
			
		||||
            $options[CURLOPT_POSTFIELDS] = $params;
 | 
			
		||||
            $request->setMethod('POST');
 | 
			
		||||
            $request->addPostParameter($params);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($auth) {
 | 
			
		||||
            $options[CURLOPT_USERPWD] = $this->screen_name .
 | 
			
		||||
              ':' . $this->password;
 | 
			
		||||
            $request->setAuth($this->screen_name, $this->password);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
        curl_setopt_array($ch, $options);
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        if ($response === false) {
 | 
			
		||||
            $msg  = curl_error($ch);
 | 
			
		||||
            $code = curl_errno($ch);
 | 
			
		||||
            throw new BasicAuthCurlException($msg, $code);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
        $response = $request->send();
 | 
			
		||||
        return $response->getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -68,10 +68,8 @@ class WikiHashtagsPlugin extends Plugin
 | 
			
		||||
                $editurl = sprintf('http://hashtags.wikia.com/index.php?title=%s&action=edit',
 | 
			
		||||
                                   urlencode($tag));
 | 
			
		||||
 | 
			
		||||
                $context = stream_context_create(array('http' => array('method' => "GET",
 | 
			
		||||
                                                                       'header' =>
 | 
			
		||||
                                                                       "User-Agent: " . $this->userAgent())));
 | 
			
		||||
                $html = @file_get_contents($url, false, $context);
 | 
			
		||||
                $request = new HTTPClient($url);
 | 
			
		||||
                $html = $request->get();
 | 
			
		||||
 | 
			
		||||
                $action->elementStart('div', array('id' => 'wikihashtags', 'class' => 'section'));
 | 
			
		||||
 | 
			
		||||
@@ -100,10 +98,4 @@ class WikiHashtagsPlugin extends Plugin
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function userAgent()
 | 
			
		||||
    {
 | 
			
		||||
        return 'WikiHashtagsPlugin/'.WIKIHASHTAGSPLUGIN_VERSION .
 | 
			
		||||
          ' StatusNet/' . STATUSNET_VERSION;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -87,35 +87,19 @@ class EnjitQueueHandler extends QueueHandler
 | 
			
		||||
        $atom .= "</entry>\n";
 | 
			
		||||
 | 
			
		||||
        $url  = common_config('enjit', 'apiurl') . "/submit/". common_config('enjit','apikey');
 | 
			
		||||
                $data = "msg=$atom";
 | 
			
		||||
        $data = array(
 | 
			
		||||
            'msg' => $atom,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
        # POST the message to $config['enjit']['apiurl']
 | 
			
		||||
        #
 | 
			
		||||
        $ch   = curl_init();
 | 
			
		||||
        $request = new HTTPClient($url, HTTP_Request2::METHOD_POST);
 | 
			
		||||
        $request->addPostFields($data);
 | 
			
		||||
        $response = $request->send();
 | 
			
		||||
        
 | 
			
		||||
        curl_setopt($ch, CURLOPT_URL, $url);
 | 
			
		||||
 | 
			
		||||
                curl_setopt($ch, CURLOPT_HEADER, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POST, 1) ;
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
 | 
			
		||||
 | 
			
		||||
                # SSL and Debugging options
 | 
			
		||||
                #
 | 
			
		||||
        # curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
 | 
			
		||||
        # curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
 | 
			
		||||
                # curl_setopt($ch, CURLOPT_VERBOSE, 1);
 | 
			
		||||
 | 
			
		||||
        $result = curl_exec($ch);
 | 
			
		||||
 | 
			
		||||
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE );
 | 
			
		||||
 | 
			
		||||
                $this->log(LOG_INFO, "Response Code: $code");
 | 
			
		||||
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
                return $code;
 | 
			
		||||
        // @fixme handle_notice() is supposed to return true/false. Somethin' funky?
 | 
			
		||||
        return $response->getStatus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user