Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/packages/zendframework/http.php

[return to app]
1
<?php
/**
 * Zend Framework Http class isolated from ZF dependencies to work in Vork
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
 * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
 * responses.
 *
 * The class contains an array of Zend_Http_Cookie objects. Cookies can be added to the jar
 * automatically from a request or manually. Then, the jar can find and return the cookies
 * needed for a specific HTTP request.
 *
 * A special parameter can be passed to all methods of this class that return cookies: Cookies
 * can be returned either in their native form (as Zend_Http_Cookie objects) or as strings -
 * the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
 * You can also choose, when returning more than one cookie, whether to get an array of strings
 * (by passing Zend_Http_CookieJar::COOKIE_STRING_ARRAY) or one unified string for all cookies
 * (by passing Zend_Http_CookieJar::COOKIE_STRING_CONCAT).
 *
 * @link       http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
 * @category   Zend
 * @package    Zend_Http
 * @subpackage CookieJar
 * @version    $Id: CookieJar.php 20096 2010-01-06 02:05:09Z bkarwin $
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Http_CookieJar implements Countable, IteratorAggregate {
    /**
     * Return cookie(s) as a Zend_Http_Cookie object
     *
     */
    const COOKIE_OBJECT = 0;

    /**
     * Return cookie(s) as a string (suitable for sending in an HTTP request)
     *
     */
    const COOKIE_STRING_ARRAY = 1;

    /**
     * Return all cookies as one long string (suitable for sending in an HTTP request)
     *
     */
    const COOKIE_STRING_CONCAT = 2;

    /**
     * Array storing cookies
     *
     * Cookies are stored according to domain and path:
     * $cookies
     *  + www.mydomain.com
     *    + /
     *      - cookie1
     *      - cookie2
     *    + /somepath
     *      - othercookie
     *  + www.otherdomain.net
     *    + /
     *      - alsocookie
     *
     * @var array
     */
    protected $cookies = array();

    /**
     * The Zend_Http_Cookie array
     *
     * @var array
     */
    protected $_rawCookies = array();

    /**
     * Construct a new CookieJar object
     *
     */
    public function __construct() { }

    /**
     * Add a cookie to the jar. Cookie should be passed either as a Zend_Http_Cookie object
     * or as a string - in which case an object is created from the string.
     *
     * @param Zend_Http_Cookie|string $cookie
     * @param Zend_Uri_Http|string    $ref_uri Optional reference URI (for domain, path, secure)
     */
    public function addCookie($cookie, $ref_uri = null) {
        if (is_string($cookie)) {
            $cookie = Zend_Http_Cookie::fromString($cookie, $ref_uri);
        }

        if ($cookie instanceof Zend_Http_Cookie) {
            $domain = $cookie->getDomain();
            $path = $cookie->getPath();
            if (! isset($this->cookies[$domain])) $this->cookies[$domain] = array();
            if (! isset($this->cookies[$domain][$path])) $this->cookies[$domain][$path] = array();
            $this->cookies[$domain][$path][$cookie->getName()] = $cookie;
            $this->_rawCookies[] = $cookie;
        } else {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Exception('Supplient argument is not a valid cookie string or object');
        }
    }

    /**
     * Parse an HTTP response, adding all the cookies set in that response
     * to the cookie jar.
     *
     * @param Zend_Http_Response $response
     * @param Zend_Uri_Http|string $ref_uri Requested URI
     */
    public function addCookiesFromResponse($response, $ref_uri) {
        if (! $response instanceof Zend_Http_Response) {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Exception('$response is expected to be a Response object, ' .
                gettype($response) . ' was passed');
        }

        $cookie_hdrs = $response->getHeader('Set-Cookie');

        if (is_array($cookie_hdrs)) {
            foreach ($cookie_hdrs as $cookie) {
                $this->addCookie($cookie, $ref_uri);
            }
        } elseif (is_string($cookie_hdrs)) {
            $this->addCookie($cookie_hdrs, $ref_uri);
        }
    }

    /**
     * Get all cookies in the cookie jar as an array
     *
     * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
     * @return array|string
     */
    public function getAllCookies($ret_as = self::COOKIE_OBJECT) {
        $cookies = $this->_flattenCookiesArray($this->cookies, $ret_as);
        return $cookies;
    }

    /**
     * Return an array of all cookies matching a specific request according to the request URI,
     * whether session cookies should be sent or not, and the time to consider as "now" when
     * checking cookie expiry time.
     *
     * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
     * @param boolean $matchSessionCookies Whether to send session cookies
     * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
     * @param int $now Override the current time when checking for expiry time
     * @return array|string
     */
    public function getMatchingCookies($uri, $matchSessionCookies = true,
        $ret_as = self::COOKIE_OBJECT, $now = null) {
        if (is_string($uri)) $uri = Zend_Uri::factory($uri);
        if (! $uri instanceof Zend_Uri_Http) {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Exception("Invalid URI string or object passed");
        }

        // First, reduce the array of cookies to only those matching domain and path
        $cookies = $this->_matchDomain($uri->getHost());
        $cookies = $this->_matchPath($cookies, $uri->getPath());
        $cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT);

        // Next, run Cookie->match on all cookies to check secure, time and session mathcing
        $ret = array();
        foreach ($cookies as $cookie)
            if ($cookie->match($uri, $matchSessionCookies, $now))
                $ret[] = $cookie;

        // Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
        $ret = $this->_flattenCookiesArray($ret, $ret_as);

        return $ret;
    }

    /**
     * Get a specific cookie according to a URI and name
     *
     * @param Zend_Uri_Http|string $uri The uri (domain and path) to match
     * @param string $cookie_name The cookie's name
     * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
     * @return Zend_Http_Cookie|string
     */
    public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT) {
        if (is_string($uri)) {
            $uri = Zend_Uri::factory($uri);
        }

        if (! $uri instanceof Zend_Uri_Http) {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Exception('Invalid URI specified');
        }

        // Get correct cookie path
        $path = $uri->getPath();
        $path = substr($path, 0, strrpos($path, '/'));
        if (! $path) $path = '/';

        if (isset($this->cookies[$uri->getHost()][$path][$cookie_name])) {
            $cookie = $this->cookies[$uri->getHost()][$path][$cookie_name];

            switch ($ret_as) {
                case self::COOKIE_OBJECT:
                    return $cookie;
                    break;

                case self::COOKIE_STRING_ARRAY:
                case self::COOKIE_STRING_CONCAT:
                    return $cookie->__toString();
                    break;

                default:
                    if (!class_exists('Zend_Exception')) require 'exception.php';
                    throw new Zend_Http_Exception("Invalid value passed for \$ret_as: {$ret_as}");
                    break;
            }
        } else {
            return false;
        }
    }

    /**
     * Helper function to recursivly flatten an array. Shoud be used when exporting the
     * cookies array (or parts of it)
     *
     * @param Zend_Http_Cookie|array $ptr
     * @param int $ret_as What value to return
     * @return array|string
     */
    protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT) {
        if (is_array($ptr)) {
            $ret = ($ret_as == self::COOKIE_STRING_CONCAT ? '' : array());
            foreach ($ptr as $item) {
                if ($ret_as == self::COOKIE_STRING_CONCAT) {
                    $ret .= $this->_flattenCookiesArray($item, $ret_as);
                } else {
                    $ret = array_merge($ret, $this->_flattenCookiesArray($item, $ret_as));
                }
            }
            return $ret;
        } elseif ($ptr instanceof Zend_Http_Cookie) {
            switch ($ret_as) {
                case self::COOKIE_STRING_ARRAY:
                    return array($ptr->__toString());
                    break;

                case self::COOKIE_STRING_CONCAT:
                    return $ptr->__toString();
                    break;

                case self::COOKIE_OBJECT:
                default:
                    return array($ptr);
                    break;
            }
        }

        return null;
    }

    /**
     * Return a subset of the cookies array matching a specific domain
     *
     * @param string $domain
     * @return array
     */
    protected function _matchDomain($domain) {
        $ret = array();

        foreach (array_keys($this->cookies) as $cdom) {
            if (Zend_Http_Cookie::matchCookieDomain($cdom, $domain)) {
                $ret[$cdom] = $this->cookies[$cdom];
            }
        }

        return $ret;
    }

    /**
     * Return a subset of a domain-matching cookies that also match a specified path
     *
     * @param array $dom_array
     * @param string $path
     * @return array
     */
    protected function _matchPath($domains, $path) {
        $ret = array();

        foreach ($domains as $dom => $paths_array) {
            foreach (array_keys($paths_array) as $cpath) {
                if (Zend_Http_Cookie::matchCookiePath($cpath, $path)) {
                    if (! isset($ret[$dom])) {
                        $ret[$dom] = array();
                    }

                    $ret[$dom][$cpath] = $paths_array[$cpath];
                }
            }
        }

        return $ret;
    }

    /**
     * Create a new CookieJar object and automatically load into it all the
     * cookies set in an Http_Response object. If $uri is set, it will be
     * considered as the requested URI for setting default domain and path
     * of the cookie.
     *
     * @param Zend_Http_Response $response HTTP Response object
     * @param Zend_Uri_Http|string $uri The requested URI
     * @return Zend_Http_CookieJar
     * @todo Add the $uri functionality.
     */
    public static function fromResponse(Zend_Http_Response $response, $ref_uri) {
        $jar = new self();
        $jar->addCookiesFromResponse($response, $ref_uri);
        return $jar;
    }

    /**
     * Required by Countable interface
     *
     * @return int
     */
    public function count() {
        return count($this->_rawCookies);
    }

    /**
     * Required by IteratorAggregate interface
     *
     * @return ArrayIterator
     */
    public function getIterator() {
        return new ArrayIterator($this->_rawCookies);
    }

    /**
     * Tells if the jar is empty of any cookie
     *
     * @return bool
     */
    public function isEmpty() {
        return count($this) == 0;
    }

    /**
     * Empties the cookieJar of any cookie
     *
     * @return Zend_Http_CookieJar
     */
    public function reset() {
        $this->cookies = $this->_rawCookies = array();
        return $this;
    }
}

/**
 * A testing-purposes adapter.
 *
 * Should be used to test all components that rely on Zend_Http_Client,
 * without actually performing an HTTP request. You should instantiate this
 * object manually, and then set it as the client's adapter. Then, you can
 * set the expected response using the setResponse() method.
 *
 * @category   Zend
 * @package    Zend_Http
 * @subpackage Client_Adapter
 * @version    $Id: Test.php 20096 2010-01-06 02:05:09Z bkarwin $
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Http_Client_Adapter_Test implements Zend_Http_Client_Adapter_Interface {
    /**
     * Parameters array
     *
     * @var array
     */
    protected $config = array();

    /**
     * Buffer of responses to be returned by the read() method.  Can be
     * set using setResponse() and addResponse().
     *
     * @var array
     */
    protected $responses = array("HTTP/1.1 400 Bad Request\r\n\r\n");

    /**
     * Current position in the response buffer
     *
     * @var integer
     */
    protected $responseIndex = 0;

    /**
     * Wether or not the next request will fail with an exception
     *
     * @var boolean
     */
    protected $_nextRequestWillFail = false;

    /**
     * Adapter constructor, currently empty. Config is set using setConfig()
     *
     */
    public function __construct() { }

    /**
     * Set the nextRequestWillFail flag
     *
     * @param boolean $flag
     * @return Zend_Http_Client_Adapter_Test
     */
    public function setNextRequestWillFail($flag) {
        $this->_nextRequestWillFail = (bool) $flag;

        return $this;
    }

    /**
     * Set the configuration array for the adapter
     *
     * @param Zend_Config | array $config
     */
    public function setConfig($config = array()) {
        if ($config instanceof Zend_Config) {
            $config = $config->toArray();

        } elseif (! is_array($config)) {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Client_Adapter_Exception(
                'Array or Zend_Config object expected, got ' . gettype($config)
            );
        }

        foreach ($config as $k => $v) {
            $this->config[strtolower($k)] = $v;
        }
    }

    /**
     * Connect to the remote server
     *
     * @param string  $host
     * @param int     $port
     * @param boolean $secure
     * @param int     $timeout
     * @throws Zend_Http_Client_Adapter_Exception
     */
    public function connect($host, $port = 80, $secure = false) {
        if ($this->_nextRequestWillFail) {
            $this->_nextRequestWillFail = false;
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Client_Adapter_Exception('Request failed');
        }
    }

    /**
     * Send request to the remote server
     *
     * @param string        $method
     * @param Zend_Uri_Http $uri
     * @param string        $http_ver
     * @param array         $headers
     * @param string        $body
     * @return string Request as string
     */
    public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') {
        $host = $uri->getHost();
            $host = (strtolower($uri->getScheme()) == 'https' ? 'sslv2://' . $host : $host);

        // Build request headers
        $path = $uri->getPath();
        if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
        $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
        foreach ($headers as $k => $v) {
            if (is_string($k)) $v = ucfirst($k) . ": $v";
            $request .= "$v\r\n";
        }

        // Add the request body
        $request .= "\r\n" . $body;

        // Do nothing - just return the request as string
        return $request;
    }

    /**
     * Return the response set in $this->setResponse()
     *
     * @return string
     */
    public function read() {
        if ($this->responseIndex >= count($this->responses)) {
            $this->responseIndex = 0;
        }
        return $this->responses[$this->responseIndex++];
    }

    /**
     * Close the connection (dummy)
     *
     */
    public function close() { }

    /**
     * Set the HTTP response(s) to be returned by this adapter
     *
     * @param Zend_Http_Response|array|string $response
     */
    public function setResponse($response) {
        if ($response instanceof Zend_Http_Response) {
            $response = $response->asString("\r\n");
        }

        $this->responses = (array)$response;
        $this->responseIndex = 0;
    }

    /**
     * Add another response to the response buffer.
     *
     * @param string Zend_Http_Response|$response
     */
    public function addResponse($response) {
         if ($response instanceof Zend_Http_Response) {
            $response = $response->asString("\r\n");
        }

        $this->responses[] = $response;
    }

    /**
     * Sets the position of the response buffer.  Selects which
     * response will be returned on the next call to read().
     *
     * @param integer $index
     */
    public function setResponseIndex($index) {
        if ($index < 0 || $index >= count($this->responses)) {
            if (!class_exists('Zend_Exception')) require 'exception.php';
            throw new Zend_Http_Client_Adapter_Exception(
                'Index out of range of response buffer size');
        }
        $this->responseIndex = $index;
    }
}

/**
 * An adapter class for Zend_Http_Client based on the curl extension.
 * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
 *
 * @category   Zend
 * @package    Zend_Http
 * @subpackage Client_Adapter
 * @version    $Id: Curl.php 22221 2010-05-21 07:00:58Z dragonbe $
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream
 
{ /** * Parameters array * * @var array */ protected $_config = array(); /** * What host/port are we connected to? * * @var array */ protected $_connected_to = array(null, null); /** * The curl session handle * * @var resource|null */ protected $_curl = null; /** * List of cURL options that should never be overwritten * * @var array */ protected $_invalidOverwritableCurlOptions; /** * Response gotten from server * * @var string */ protected $_response = null; /** * Stream for storing output * * @var resource */ protected $out_stream; /** * Adapter constructor * * Config is set using setConfig() * * @return void * @throws Zend_Http_Client_Adapter_Exception */ public function __construct() { if (!extension_loaded('curl')) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this
 Zend_Http_Client adapter.'
); } $this->_invalidOverwritableCurlOptions = array( CURLOPT_HTTPGET, CURLOPT_POST, CURLOPT_PUT, CURLOPT_CUSTOMREQUEST, CURLOPT_HEADER, CURLOPT_RETURNTRANSFER, CURLOPT_HTTPHEADER, CURLOPT_POSTFIELDS, CURLOPT_INFILE, CURLOPT_INFILESIZE, CURLOPT_PORT, CURLOPT_MAXREDIRS, CURLOPT_CONNECTTIMEOUT, CURL_HTTP_VERSION_1_1, CURL_HTTP_VERSION_1_0, ); } /** * Set the configuration array for the adapter * * @throws Zend_Http_Client_Adapter_Exception * @param Zend_Config | array $config * @return Zend_Http_Client_Adapter_Curl */ public function setConfig($config = array()) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } elseif (! is_array($config)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception( 'Array or Zend_Config object expected, got ' . gettype($config) ); } if(isset($config['proxy_user']) && isset($config['proxy_pass'])) { $this->setCurlOption(CURLOPT_PROXYUSERPWD, $config['proxy_user'].":".$config['proxy_pass']); unset($config['proxy_user'], $config['proxy_pass']); } foreach ($config as $k => $v) { $option = strtolower($k); switch($option) { case 'proxy_host': $this->setCurlOption(CURLOPT_PROXY, $v); break; case 'proxy_port': $this->setCurlOption(CURLOPT_PROXYPORT, $v); break; default: $this->_config[$option] = $v; break; } } return $this; } /** * Retrieve the array of all configuration options * * @return array */ public function getConfig() { return $this->_config; } /** * Direct setter for cURL adapter related options. * * @param string|int $option * @param mixed $value * @return Zend_Http_Adapter_Curl */ public function setCurlOption($option, $value) { if (!isset($this->_config['curloptions'])) { $this->_config['curloptions'] = array(); } $this->_config['curloptions'][$option] = $value; return $this; } /** * Initialize curl * * @param string $host * @param int $port * @param boolean $secure * @return void * @throws Zend_Http_Client_Adapter_Exception if unable to connect */ public function connect($host, $port = 80, $secure = false) { // If we're already connected, disconnect first if ($this->_curl) { $this->close(); } // If we are connected to a different server or port, disconnect first if ($this->_curl && is_array($this->_connected_to) && ($this->_connected_to[0] != $host || $this->_connected_to[1] != $port) ) { $this->close(); } // Do the actual connection $this->_curl = curl_init(); if ($port != 80) { curl_setopt($this->_curl, CURLOPT_PORT, intval($port)); } // Set timeout curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $this->_config['timeout']); // Set Max redirects curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']); if (!$this->_curl) { $this->close(); if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' . $host . ':' . $port); } if ($secure !== false) { // Behave the same like Zend_Http_Adapter_Socket on SSL options. if (isset($this->_config['sslcert'])) { curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']); } if (isset($this->_config['sslpassphrase'])) { curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']); } } // Update connected_to $this->_connected_to = array($host, $port); } /** * Send request to the remote server * * @param string $method * @param Zend_Uri_Http $uri * @param float $http_ver * @param array $headers * @param string $body * @return string $request * @throws Zend_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file
 
defined, * unsupported method, or unsupported cURL option */ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $body = '') { // Make sure we're properly connected if (!$this->_curl) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected"); } if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong
 host"
); } // set URL curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString()); // ensure correct curl call $curlValue = true; switch ($method) { case Zend_Http_Client::GET: $curlMethod = CURLOPT_HTTPGET; break; case Zend_Http_Client::POST: $curlMethod = CURLOPT_POST; break; case Zend_Http_Client::PUT: // There are two different types of PUT request, either a Raw Data string has been set // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used. if(is_resource($body)) { $this->_config['curloptions'][CURLOPT_INFILE] = $body; } if (isset($this->_config['curloptions'][CURLOPT_INFILE])) { // Now we will probably already have Content-Length set, so that we have to delete it // from $headers at this point: foreach ($headers AS $k => $header) { if (preg_match('/Content-Length:\s*(\d+)/i', $header, $m)) { if(is_resource($body)) { $this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1]; } unset($headers[$k]); } } if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Cannot set a file-handle for cURL option " . "CURLOPT_INFILE without also setting its size in
 CURLOPT_INFILESIZE."
); } if(is_resource($body)) { $body = ''; } $curlMethod = CURLOPT_PUT; } else { $curlMethod = CURLOPT_CUSTOMREQUEST; $curlValue = "PUT"; } break; case Zend_Http_Client::DELETE: $curlMethod = CURLOPT_CUSTOMREQUEST; $curlValue = "DELETE"; break; case Zend_Http_Client::OPTIONS: $curlMethod = CURLOPT_CUSTOMREQUEST; $curlValue = "OPTIONS"; break; case Zend_Http_Client::TRACE: $curlMethod = CURLOPT_CUSTOMREQUEST; $curlValue = "TRACE"; break; case Zend_Http_Client::HEAD: $curlMethod = CURLOPT_CUSTOMREQUEST; $curlValue = "HEAD"; break; default: // For now, through an exception for unsupported request methods if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Method currently not supported"); } if(is_resource($body) && $curlMethod != CURLOPT_PUT) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT"); } // get http version to use $curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0; // mark as HTTP request and set HTTP method curl_setopt($this->_curl, $curlHttp, true); curl_setopt($this->_curl, $curlMethod, $curlValue); if($this->out_stream) { // headers will be read into the response curl_setopt($this->_curl, CURLOPT_HEADER, false); curl_setopt($this->_curl, CURLOPT_HEADERFUNCTION, array($this, "readHeader")); // and data will be written into the file curl_setopt($this->_curl, CURLOPT_FILE, $this->out_stream); } else { // ensure headers are also returned curl_setopt($this->_curl, CURLOPT_HEADER, true); // ensure actual response is returned curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true); } // set additional headers $headers['Accept'] = ''; curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers); /** * Make sure POSTFIELDS is set after $curlMethod is set: * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161 */ if ($method == Zend_Http_Client::POST) { curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body); } elseif ($curlMethod == CURLOPT_PUT) { // this covers a PUT by file-handle: // Make the setting of this options explicit (rather than setting it through the loop following a bit
 lower) // to group common functionality together.
curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]); curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]); unset($this->_config['curloptions'][CURLOPT_INFILE]); unset($this->_config['curloptions'][CURLOPT_INFILESIZE]); } elseif ($method == Zend_Http_Client::PUT) { // This is a PUT by a setRawData string, not by file-handle curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body); } // set additional curl options if (isset($this->_config['curloptions'])) { foreach ((array)$this->_config['curloptions'] as $k => $v) { if (!in_array($k, $this->_invalidOverwritableCurlOptions)) { if (curl_setopt($this->_curl, $k, $v) == false) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set",
 
$k)); } } } } // send the request $response = curl_exec($this->_curl); // if we used streaming, headers are already there if(!is_resource($this->out_stream)) { $this->_response = $response; } $request = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT); $request .= $body; if (empty($this->_response)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl)); } // cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to
 do it again
if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) { $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response); } // Eliminate multiple HTTP responses. do { $parts = preg_split('|(?:\r?\n){2}|m', $this->_response, 2); $again = false; if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) { $this->_response = $parts[1]; $again = true; } } while ($again); // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string: if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) { $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response); } return $request; } /** * Return read response from server * * @return string */ public function read() { return $this->_response; } /** * Close the connection to the server * */ public function close() { if(is_resource($this->_curl)) { curl_close($this->_curl); } $this->_curl = null; $this->_connected_to = array(null, null); } /** * Get cUrl Handle * * @return resource */ public function getHandle() { return $this->_curl; } /** * Set output stream for the response * * @param resource $stream * @return Zend_Http_Client_Adapter_Socket */ public function setOutputStream($stream) { $this->out_stream = $stream; return $this; } /** * Header reader function for CURL * * @param resource $curl * @param string $header * @return int */ public function readHeader($curl, $header) { $this->_response .= $header; return strlen($header); } } /** * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used * on almost every PHP environment, and does not require any special extensions. * * @category Zend * @package Zend_Http * @subpackage Client_Adapter * @version $Id: Socket.php 21778 2010-04-06 11:19:35Z shahar $ * @version $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface,
 
Zend_Http_Client_Adapter_Stream { /** * The socket for server connection * * @var resource|null */ protected $socket = null; /** * What host/port are we connected to? * * @var array */ protected $connected_to = array(null, null); /** * Stream for storing output * * @var resource */ protected $out_stream = null; /** * Parameters array * * @var array */ protected $config = array( 'persistent' => false, 'ssltransport' => 'ssl', 'sslcert' => null, 'sslpassphrase' => null, 'sslusecontext' => false ); /** * Request method - will be set by write() and might be used by read() * * @var string */ protected $method = null; /** * Stream context * * @var resource */ protected $_context = null; /** * Adapter constructor, currently empty. Config is set using setConfig() * */ public function __construct() { } /** * Set the configuration array for the adapter * * @param Zend_Config | array $config */ public function setConfig($config = array()) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } elseif (! is_array($config)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception( 'Array or Zend_Config object expected, got ' . gettype($config) ); } foreach ($config as $k => $v) { $this->config[strtolower($k)] = $v; } } /** * Retrieve the array of all configuration options * * @return array */ public function getConfig() { return $this->config; } /** * Set the stream context for the TCP connection to the server * * Can accept either a pre-existing stream context resource, or an array * of stream options, similar to the options array passed to the * stream_context_create() PHP function. In such case a new stream context * will be created using the passed options. * * @since Zend Framework 1.9 * * @param mixed $context Stream context or array of context options * @return Zend_Http_Client_Adapter_Socket */ public function setStreamContext($context) { if (is_resource($context) && get_resource_type($context) == 'stream-context') { $this->_context = $context; } elseif (is_array($context)) { $this->_context = stream_context_create($context); } else { // Invalid parameter if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception( "Expecting either a stream context resource or array, got " . gettype($context) ); } return $this; } /** * Get the stream context for the TCP connection to the server. * * If no stream context is set, will create a default one. * * @return resource */ public function getStreamContext() { if (! $this->_context) { $this->_context = stream_context_create(); } return $this->_context; } /** * Connect to the remote server * * @param string $host * @param int $port * @param boolean $secure */ public function connect($host, $port = 80, $secure = false) { // If the URI should be accessed via SSL, prepend the Hostname with ssl:// $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host; // If we are connected to the wrong host, disconnect first if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) { if (is_resource($this->socket)) $this->close(); } // Now, if we are not connected, connect if (! is_resource($this->socket) || ! $this->config['keepalive']) { $context = $this->getStreamContext(); if ($secure || $this->config['sslusecontext']) { if ($this->config['sslcert'] !== null) { if (! stream_context_set_option($context, 'ssl', 'local_cert', $this->config['sslcert'])) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option'); } } if ($this->config['sslpassphrase'] !== null) { if (! stream_context_set_option($context, 'ssl', 'passphrase', $this->config['sslpassphrase'])) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option'); } } } $flags = STREAM_CLIENT_CONNECT; if ($this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT; $this->socket = @stream_socket_client($host . ':' . $port, $errno, $errstr, (int) $this->config['timeout'], $flags, $context); if (! $this->socket) { $this->close(); if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception( 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr); } // Set the stream timeout if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout'); } // Update connected_to $this->connected_to = array($host, $port); } } /** * Send request to the remote server * * @param string $method * @param Zend_Uri_Http $uri * @param string $http_ver * @param array $headers * @param string $body * @return string Request as string */ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') { // Make sure we're properly connected if (! $this->socket) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected'); } $host = $uri->getHost(); $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' .
 
$host; if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong
 host'
); } // Save request method for later $this->method = $method; // Build request headers $path = $uri->getPath(); if ($uri->getQuery()) $path .= '?' . $uri->getQuery(); $request = "{$method} {$path} HTTP/{$http_ver}\r\n"; foreach ($headers as $k => $v) { if (is_string($k)) $v = ucfirst($k) . ": $v"; $request .= "$v\r\n"; } if(is_resource($body)) { $request .= "\r\n"; } else { // Add the request body $request .= "\r\n" . $body; } // Send the request if (! @fwrite($this->socket, $request)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); } if(is_resource($body)) { if(stream_copy_to_stream($body, $this->socket) == 0) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); } } return $request; } /** * Read response from server * * @return string */ public function read() { // First, read headers only $response = ''; $gotStatus = false; $stream = !empty($this->config['stream']); while (($line = @fgets($this->socket)) !== false) { $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); if ($gotStatus) { $response .= $line; if (rtrim($line) === '') break; } } $this->_checkSocketReadTimeout(); $statusCode = Zend_Http_Response::extractCode($response); // Handle 100 and 101 responses internally by restarting the read again if ($statusCode == 100 || $statusCode == 101) return $this->read(); // Check headers to see what kind of connection / transfer encoding we have $headers = Zend_Http_Response::extractHeaders($response); /** * Responses to HEAD requests and 204 or 304 responses are not expected * to have a body - stop reading here */ if ($statusCode == 304 || $statusCode == 204 || $this->method == Zend_Http_Client::HEAD) { // Close the connection if requested to do so by the server if (isset($headers['connection']) && $headers['connection'] == 'close') { $this->close(); } return $response; } // If we got a 'transfer-encoding: chunked' header if (isset($headers['transfer-encoding'])) { if (strtolower($headers['transfer-encoding']) == 'chunked') { do { $line = @fgets($this->socket); $this->_checkSocketReadTimeout(); $chunk = $line; // Figure out the next chunk size $chunksize = trim($line); if (! ctype_xdigit($chunksize)) { $this->close(); if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' . $chunksize . '" unable to read chunked body'); } // Convert the hexadecimal value to plain integer $chunksize = hexdec($chunksize); // Read next chunk $read_to = ftell($this->socket) + $chunksize; do { $current_pos = ftell($this->socket); if ($current_pos >= $read_to) break; if($this->out_stream) { if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) ==
 
0) { $this->_checkSocketReadTimeout(); break; } } else { $line = @fread($this->socket, $read_to - $current_pos); if ($line === false || strlen($line) === 0) { $this->_checkSocketReadTimeout(); break; } $chunk .= $line; } } while (! feof($this->socket)); $chunk .= @fgets($this->socket); $this->_checkSocketReadTimeout(); if(!$this->out_stream) { $response .= $chunk; } } while ($chunksize > 0); } else { $this->close(); throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' . $headers['transfer-encoding'] . '" transfer encoding'); } // We automatically decode chunked-messages when writing to a stream // this means we have to disallow the Zend_Http_Response to do it again if ($this->out_stream) { $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response); } // Else, if we got the content-length header, read this number of bytes } elseif (isset($headers['content-length'])) { // If we got more than one Content-Length header (see ZF-9404) use // the last value sent if (is_array($headers['content-length'])) { $contentLength = $headers['content-length'][count($headers['content-length']) - 1]; } else { $contentLength = $headers['content-length']; } $current_pos = ftell($this->socket); $chunk = ''; for ($read_to = $current_pos + $contentLength; $read_to > $current_pos; $current_pos = ftell($this->socket)) { if($this->out_stream) { if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { $this->_checkSocketReadTimeout(); break; } } else { $chunk = @fread($this->socket, $read_to - $current_pos); if ($chunk === false || strlen($chunk) === 0) { $this->_checkSocketReadTimeout(); break; } $response .= $chunk; } // Break if the connection ended prematurely if (feof($this->socket)) break; } // Fallback: just read the response until EOF } else { do { if($this->out_stream) { if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) { $this->_checkSocketReadTimeout(); break; } } else { $buff = @fread($this->socket, 8192); if ($buff === false || strlen($buff) === 0) { $this->_checkSocketReadTimeout(); break; } else { $response .= $buff; } } } while (feof($this->socket) === false); $this->close(); } // Close the connection if requested to do so by the server if (isset($headers['connection']) && $headers['connection'] == 'close') { $this->close(); } return $response; } /** * Close the connection to the server * */ public function close() { if (is_resource($this->socket)) @fclose($this->socket); $this->socket = null; $this->connected_to = array(null, null); } /** * Check if the socket has timed out - if so close connection and throw * an exception * * @throws Zend_Http_Client_Adapter_Exception with READ_TIMEOUT code */ protected function _checkSocketReadTimeout() { if ($this->socket) { $info = stream_get_meta_data($this->socket); $timedout = $info['timed_out']; if ($timedout) { $this->close(); if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception( "Read timed out after {$this->config['timeout']} seconds", Zend_Http_Client_Adapter_Exception::READ_TIMEOUT ); } } } /** * Set output stream for the response * * @param resource $stream * @return Zend_Http_Client_Adapter_Socket */ public function setOutputStream($stream) { $this->out_stream = $stream; return $this; } /** * Destructor: make sure the socket is disconnected * * If we are in persistent TCP mode, will not close the connection * */ public function __destruct() { if (! $this->config['persistent']) { if ($this->socket) $this->close(); } } } /** * An interface description for Zend_Http_Client_Adapter_Stream classes. * * This interface decribes Zend_Http_Client_Adapter which supports streaming. * * @category Zend * @package Zend_Http * @subpackage Client_Adapter * @version $Id: Interface.php 16214 2009-06-21 19:34:03Z thomas $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ interface Zend_Http_Client_Adapter_Stream { /** * Set output stream * * This function sets output stream where the result will be stored. * * @param resource $stream Stream to write the output to * */ function setOutputStream($stream); } /** * HTTP Proxy-supporting Zend_Http_Client adapter class, based on the default * socket based adapter. * * Should be used if proxy HTTP access is required. If no proxy is set, will * fall back to Zend_Http_Client_Adapter_Socket behavior. Just like the * default Socket adapter, this adapter does not require any special extensions * installed. * * @category Zend * @package Zend_Http * @subpackage Client_Adapter * @version $Id: Proxy.php 22445 2010-06-16 09:09:12Z bate $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Client_Adapter_Proxy extends Zend_Http_Client_Adapter_Socket { /** * Parameters array * * @var array */ protected $config = array( 'ssltransport' => 'ssl', 'sslcert' => null, 'sslpassphrase' => null, 'sslusecontext' => false, 'proxy_host' => '', 'proxy_port' => 8080, 'proxy_user' => '', 'proxy_pass' => '', 'proxy_auth' => Zend_Http_Client::AUTH_BASIC, 'persistent' => false ); /** * Whether HTTPS CONNECT was already negotiated with the proxy or not * * @var boolean */ protected $negotiated = false; /** * Connect to the remote server * * Will try to connect to the proxy server. If no proxy was set, will * fall back to the target server (behave like regular Socket adapter) * * @param string $host * @param int $port * @param boolean $secure */ public function connect($host, $port = 80, $secure = false) { // If no proxy is set, fall back to Socket adapter if (! $this->config['proxy_host']) { return parent::connect($host, $port, $secure); } /* Url might require stream context even if proxy connection doesn't */ if ($secure) { $this->config['sslusecontext'] = true; } // Connect (a non-secure connection) to the proxy server return parent::connect( $this->config['proxy_host'], $this->config['proxy_port'], false ); } /** * Send request to the proxy server * * @param string $method * @param Zend_Uri_Http $uri * @param string $http_ver * @param array $headers * @param string $body * @return string Request as string */ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') { // If no proxy is set, fall back to default Socket adapter if (! $this->config['proxy_host']) return parent::write($method, $uri, $http_ver, $headers, $body); // Make sure we're properly connected if (! $this->socket) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected"); } $host = $this->config['proxy_host']; $port = $this->config['proxy_port']; if ($this->connected_to[0] != "tcp://$host" || $this->connected_to[1] != $port) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy
 server"
); } // Add Proxy-Authorization header if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) { $headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader( $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth'] ); } // if we are proxying HTTPS, preform CONNECT handshake with the proxy if ($uri->getScheme() == 'https' && (! $this->negotiated)) { $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers); $this->negotiated = true; } // Save request method for later $this->method = $method; // Build request headers if ($this->negotiated) { $path = $uri->getPath(); if ($uri->getQuery()) { $path .= '?' . $uri->getQuery(); } $request = "$method $path HTTP/$http_ver\r\n"; } else { $request = "$method $uri HTTP/$http_ver\r\n"; } // Add all headers to the request string foreach ($headers as $k => $v) { if (is_string($k)) $v = "$k: $v"; $request .= "$v\r\n"; } if(is_resource($body)) { $request .= "\r\n"; } else { // Add the request body $request .= "\r\n" . $body; } // Send the request if (! @fwrite($this->socket, $request)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); } if(is_resource($body)) { if(stream_copy_to_stream($body, $this->socket) == 0) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); } } return $request; } /** * Preform handshaking with HTTPS proxy using CONNECT method * * @param string $host * @param integer $port * @param string $http_ver * @param array $headers */ protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array()) { $request = "CONNECT $host:$port HTTP/$http_ver\r\n" . "Host: " . $this->config['proxy_host'] . "\r\n"; // Add the user-agent header if (isset($this->config['useragent'])) { $request .= "User-agent: " . $this->config['useragent'] . "\r\n"; } // If the proxy-authorization header is set, send it to proxy but remove // it from headers sent to target host if (isset($headers['proxy-authorization'])) { $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n"; unset($headers['proxy-authorization']); } $request .= "\r\n"; // Send the request if (! @fwrite($this->socket, $request)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); } // Read response headers only $response = ''; $gotStatus = false; while ($line = @fgets($this->socket)) { $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); if ($gotStatus) { $response .= $line; if (!chop($line)) break; } } // Check that the response from the proxy is 200 if (Zend_Http_Response::extractCode($response) != 200) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " .
 
$response); } // If all is good, switch socket to secure mode. We have to fall back // through the different modes $modes = array( STREAM_CRYPTO_METHOD_TLS_CLIENT, STREAM_CRYPTO_METHOD_SSLv3_CLIENT, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, STREAM_CRYPTO_METHOD_SSLv2_CLIENT ); $success = false; foreach($modes as $mode) { $success = stream_socket_enable_crypto($this->socket, true, $mode); if ($success) break; } if (! $success) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Adapter_Exception("Unable to connect to" . " HTTPS server through proxy: could not negotiate secure connection."); } } /** * Close the connection to the server * */ public function close() { parent::close(); $this->negotiated = false; } /** * Destructor: make sure the socket is disconnected * */ public function __destruct() { if ($this->socket) $this->close(); } } /** * An interface description for Zend_Http_Client_Adapter classes. * * These classes are used as connectors for Zend_Http_Client, performing the * tasks of connecting, writing, reading and closing connection to the server. * * @category Zend * @package Zend_Http * @subpackage Client_Adapter * @version $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ interface Zend_Http_Client_Adapter_Interface { /** * Set the configuration array for the adapter * * @param array $config */ public function setConfig($config = array()); /** * Connect to the remote server * * @param string $host * @param int $port * @param boolean $secure */ public function connect($host, $port = 80, $secure = false); /** * Send request to the remote server * * @param string $method * @param Zend_Uri_Http $url * @param string $http_ver * @param array $headers * @param string $body * @return string Request as text */ public function write($method, $url, $http_ver = '1.1', $headers = array(), $body = ''); /** * Read response from server * * @return string */ public function read(); /** * Close the connection to the server * */ public function close(); } /** * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It * includes easy access to all the response's different elemts, as well as some * convenience methods for parsing and validating HTTP responses. * * @package Zend_Http * @subpackage Response * @version $Id: Response.php 20096 2010-01-06 02:05:09Z bkarwin $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Response { /** * List of all known HTTP response codes - used by responseCodeAsText() to * translate numeric codes to messages. * * @var array */ protected static $messages = array( // Informational 1xx 100 => 'Continue', 101 => 'Switching Protocols', // Success 2xx 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', // Redirection 3xx 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', // 1.1 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', // 306 is deprecated but reserved 307 => 'Temporary Redirect', // Client Error 4xx 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', // Server Error 5xx 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' ); /** * The HTTP version (1.0, 1.1) * * @var string */ protected $version; /** * The HTTP response code * * @var int */ protected $code; /** * The HTTP response code as string * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500) * * @var string */ protected $message; /** * The HTTP response headers array * * @var array */ protected $headers = array(); /** * The HTTP response body * * @var string */ protected $body; /** * HTTP response constructor * * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP * response string and create a new Zend_Http_Response object. * * NOTE: The constructor no longer accepts nulls or empty values for the code and * headers and will throw an exception if the passed values do not form a valid HTTP * responses. * * If no message is passed, the message will be guessed according to the response code. * * @param int $code Response code (200, 404, ...) * @param array $headers Headers array * @param string $body Response body * @param string $version HTTP version * @param string $message Response code as text * @throws Zend_Http_Exception */ public function __construct($code, $headers, $body = null, $version = '1.1', $message = null) { // Make sure the response code is valid and set it if (self::responseCodeAsText($code) === null) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("{$code} is not a valid HTTP response code"); } $this->code = $code; // Make sure we got valid headers and set them if (! is_array($headers)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception('No valid headers were passed'); } foreach ($headers as $name => $value) { if (is_int($name)) list($name, $value) = explode(": ", $value, 1); $this->headers[ucwords(strtolower($name))] = $value; } // Set the body $this->body = $body; // Set the HTTP version if (! preg_match('|^\d\.\d$|', $version)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("Invalid HTTP response version: $version"); } $this->version = $version; // If we got the response message, set it. Else, set it according to // the response code if (is_string($message)) { $this->message = $message; } else { $this->message = self::responseCodeAsText($code); } } /** * Check whether the response is an error * * @return boolean */ public function isError() { $restype = floor($this->code / 100); if ($restype == 4 || $restype == 5) { return true; } return false; } /** * Check whether the response in successful * * @return boolean */ public function isSuccessful() { $restype = floor($this->code / 100); if ($restype == 2 || $restype == 1) { // Shouldn't 3xx count as success as well ??? return true; } return false; } /** * Check whether the response is a redirection * * @return boolean */ public function isRedirect() { $restype = floor($this->code / 100); if ($restype == 3) { return true; } return false; } /** * Get the response body as string * * This method returns the body of the HTTP response (the content), as it * should be in it's readable version - that is, after decoding it (if it * was decoded), deflating it (if it was gzip compressed), etc. * * If you want to get the raw body (as transfered on wire) use * $this->getRawBody() instead. * * @return string */ public function getBody() { $body = ''; // Decode the body if it was transfer-encoded switch (strtolower($this->getHeader('transfer-encoding'))) { // Handle chunked body case 'chunked': $body = self::decodeChunkedBody($this->body); break; // No transfer encoding, or unknown encoding extension: // return body as is default: $body = $this->body; break; } // Decode any content-encoding (gzip or deflate) if needed switch (strtolower($this->getHeader('content-encoding'))) { // Handle gzip encoding case 'gzip': $body = self::decodeGzip($body); break; // Handle deflate encoding case 'deflate': $body = self::decodeDeflate($body); break; default: break; } return $body; } /** * Get the raw response body (as transfered "on wire") as string * * If the body is encoded (with Transfer-Encoding, not content-encoding - * IE "chunked" body), gzip compressed, etc. it will not be decoded. * * @return string */ public function getRawBody() { return $this->body; } /** * Get the HTTP version of the response * * @return string */ public function getVersion() { return $this->version; } /** * Get the HTTP response status code * * @return int */ public function getStatus() { return $this->code; } /** * Return a message describing the HTTP response code * (Eg. "OK", "Not Found", "Moved Permanently") * * @return string */ public function getMessage() { return $this->message; } /** * Get the response headers * * @return array */ public function getHeaders() { return $this->headers; } /** * Get a specific header as string, or null if it is not set * * @param string$header * @return string|array|null */ public function getHeader($header) { $header = ucwords(strtolower($header)); if (! is_string($header) || ! isset($this->headers[$header])) return null; return $this->headers[$header]; } /** * Get all headers as string * * @param boolean $status_line Whether to return the first status line (IE "HTTP 200 OK") * @param string $br Line breaks (eg. "\n", "\r\n", "<br />") * @return string */ public function getHeadersAsString($status_line = true, $br = "\n") { $str = ''; if ($status_line) { $str = "HTTP/{$this->version} {$this->code} {$this->message}{$br}"; } // Iterate over the headers and stringify them foreach ($this->headers as $name => $value) { if (is_string($value)) $str .= "{$name}: {$value}{$br}"; elseif (is_array($value)) { foreach ($value as $subval) { $str .= "{$name}: {$subval}{$br}"; } } } return $str; } /** * Get the entire response as string * * @param string $br Line breaks (eg. "\n", "\r\n", "<br />") * @return string */ public function asString($br = "\n") { return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody(); } /** * Implements magic __toString() * * @return string */ public function __toString() { return $this->asString(); } /** * A convenience function that returns a text representation of * HTTP response codes. Returns 'Unknown' for unknown codes. * Returns array of all codes, if $code is not specified. * * Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown') * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference * * @param int $code HTTP response code * @param boolean $http11 Use HTTP version 1.1 * @return string */ public static function responseCodeAsText($code = null, $http11 = true) { $messages = self::$messages; if (! $http11) $messages[302] = 'Moved Temporarily'; if ($code === null) { return $messages; } elseif (isset($messages[$code])) { return $messages[$code]; } else { return 'Unknown'; } } /** * Extract the response code from a response string * * @param string $response_str * @return int */ public static function extractCode($response_str) { preg_match("|^HTTP/[\d\.x]+ (\d+)|", $response_str, $m); if (isset($m[1])) { return (int) $m[1]; } else { return false; } } /** * Extract the HTTP message from a response * * @param string $response_str * @return string */ public static function extractMessage($response_str) { preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $response_str, $m); if (isset($m[1])) { return $m[1]; } else { return false; } } /** * Extract the HTTP version from a response * * @param string $response_str * @return string */ public static function extractVersion($response_str) { preg_match("|^HTTP/([\d\.x]+) \d+|", $response_str, $m); if (isset($m[1])) { return $m[1]; } else { return false; } } /** * Extract the headers from a response string * * @param string $response_str * @return array */ public static function extractHeaders($response_str) { $headers = array(); // First, split body and headers $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2); if (! $parts[0]) return $headers; // Split headers part to lines $lines = explode("\n", $parts[0]); unset($parts); $last_header = null; foreach($lines as $line) { $line = trim($line, "\r\n"); if ($line == "") break; if (preg_match("|^([\w-]+):\s+(.+)|", $line, $m)) { unset($last_header); $h_name = strtolower($m[1]); $h_value = $m[2]; if (isset($headers[$h_name])) { if (! is_array($headers[$h_name])) { $headers[$h_name] = array($headers[$h_name]); } $headers[$h_name][] = $h_value; } else { $headers[$h_name] = $h_value; } $last_header = $h_name; } elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) { if (is_array($headers[$last_header])) { end($headers[$last_header]); $last_header_key = key($headers[$last_header]); $headers[$last_header][$last_header_key] .= $m[1]; } else { $headers[$last_header] .= $m[1]; } } } return $headers; } /** * Extract the body from a response string * * @param string $response_str * @return string */ public static function extractBody($response_str) { $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2); if (isset($parts[1])) { return $parts[1]; } return ''; } /** * Decode a "chunked" transfer-encoded body and return the decoded text * * @param string $body * @return string */ public static function decodeChunkedBody($body) { $decBody = ''; // If mbstring overloads substr and strlen functions, we have to // override it's internal encoding if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { $mbIntEnc = mb_internal_encoding(); mb_internal_encoding('ASCII'); } while (trim($body)) { if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("Error parsing body - doesn't seem to be a chunked message"); } $length = hexdec(trim($m[1])); $cut = strlen($m[0]); $decBody .= substr($body, $cut, $length); $body = substr($body, $cut + $length + 2); } if (isset($mbIntEnc)) { mb_internal_encoding($mbIntEnc); } return $decBody; } /** * Decode a gzip encoded message (when Content-encoding = gzip) * * Currently requires PHP with zlib support * * @param string $body * @return string */ public static function decodeGzip($body) { if (! function_exists('gzinflate')) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception( 'zlib extension is required in order to decode "gzip" encoding' ); } return gzinflate(substr($body, 10)); } /** * Decode a zlib deflated message (when Content-encoding = deflate) * * Currently requires PHP with zlib support * * @param string $body * @return string */ public static function decodeDeflate($body) { if (! function_exists('gzuncompress')) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception( 'zlib extension is required in order to decode "deflate" encoding' ); } /** * Some servers (IIS ?) send a broken deflate response, without the * RFC-required zlib header. * * We try to detect the zlib header, and if it does not exsit we * teat the body is plain DEFLATE content. * * This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov * * @link http://framework.zend.com/issues/browse/ZF-6040 */ $zlibHeader = unpack('n', substr($body, 0, 2)); if ($zlibHeader[1] % 31 == 0) { return gzuncompress($body); } else { return gzinflate($body); } } /** * Create a new Zend_Http_Response object from a string * * @param string $response_str * @return Zend_Http_Response */ public static function fromString($response_str) { $code = self::extractCode($response_str); $headers = self::extractHeaders($response_str); $body = self::extractBody($response_str); $version = self::extractVersion($response_str); $message = self::extractMessage($response_str); return new Zend_Http_Response($code, $headers, $body, $version, $message); } } /** * Zend_Http_Client is an implemetation of an HTTP client in PHP. The client * supports basic features like sending different HTTP requests and handling * redirections, as well as more advanced features like proxy settings, HTTP * authentication and cookie persistance (using a Zend_Http_CookieJar object) * * @todo Implement proxy settings * @category Zend * @package Zend_Http * @subpackage Client * @throws Zend_Http_Client_Exception * @version $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $ * @version $Id: Client.php 21952 2010-04-19 18:44:26Z shahar $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Client { /** * HTTP request methods */ const GET = 'GET'; const POST = 'POST'; const PUT = 'PUT'; const HEAD = 'HEAD'; const DELETE = 'DELETE'; const TRACE = 'TRACE'; const OPTIONS = 'OPTIONS'; const CONNECT = 'CONNECT'; const MERGE = 'MERGE'; /** * Supported HTTP Authentication methods */ const AUTH_BASIC = 'basic'; //const AUTH_DIGEST = 'digest'; <-- not implemented yet /** * HTTP protocol versions */ const HTTP_1 = '1.1'; const HTTP_0 = '1.0'; /** * Content attributes */ const CONTENT_TYPE = 'Content-Type'; const CONTENT_LENGTH = 'Content-Length'; /** * POST data encoding methods */ const ENC_URLENCODED = 'application/x-www-form-urlencoded'; const ENC_FORMDATA = 'multipart/form-data'; /** * Configuration array, set using the constructor or using ::setConfig() * * @var array */ protected $config = array( 'maxredirects' => 5, 'strictredirects' => false, 'useragent' => 'Zend_Http_Client', 'timeout' => 10, 'adapter' => 'Zend_Http_Client_Adapter_Socket', 'httpversion' => self::HTTP_1, 'keepalive' => false, 'storeresponse' => true, 'strict' => true, 'output_stream' => false, 'encodecookies' => true, ); /** * The adapter used to preform the actual connection to the server * * @var Zend_Http_Client_Adapter_Interface */ protected $adapter = null; /** * Request URI * * @var Zend_Uri_Http */ protected $uri = null; /** * Associative array of request headers * * @var array */ protected $headers = array(); /** * HTTP request method * * @var string */ protected $method = self::GET; /** * Associative array of GET parameters * * @var array */ protected $paramsGet = array(); /** * Assiciative array of POST parameters * * @var array */ protected $paramsPost = array(); /** * Request body content type (for POST requests) * * @var string */ protected $enctype = null; /** * The raw post data to send. Could be set by setRawData($data, $enctype). * * @var string */ protected $raw_post_data = null; /** * HTTP Authentication settings * * Expected to be an associative array with this structure: * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic') * Where 'type' should be one of the supported authentication types (see the AUTH_* * constants), for example 'basic' or 'digest'. * * If null, no authentication will be used. * * @var array|null */ protected $auth; /** * File upload arrays (used in POST requests) * * An associative array, where each element is of the format: * 'name' => array('filename.txt', 'text/plain', 'This is the actual file contents') * * @var array */ protected $files = array(); /** * The client's cookie jar * * @var Zend_Http_CookieJar */ protected $cookiejar = null; /** * The last HTTP request sent by the client, as string * * @var string */ protected $last_request = null; /** * The last HTTP response received by the client * * @var Zend_Http_Response */ protected $last_response = null; /** * Redirection counter * * @var int */ protected $redirectCounter = 0; /** * Fileinfo magic database resource * * This varaiable is populated the first time _detectFileMimeType is called * and is then reused on every call to this method * * @var resource */ static protected $_fileInfoDb = null; /** * Contructor method. Will create a new HTTP client. Accepts the target * URL and optionally configuration array. * * @param Zend_Uri_Http|string $uri * @param array $config Configuration key-value pairs. */ public function __construct($uri = null, $config = null) { if ($uri !== null) { $this->setUri($uri); } if ($config !== null) { $this->setConfig($config); } } /** * Set the URI for the next request * * @param Zend_Uri_Http|string $uri * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setUri($uri) { if (is_string($uri)) { $uri = Zend_Uri::factory($uri); } if (!$uri instanceof Zend_Uri_Http) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.'); } // Set auth if username and password has been specified in the uri if ($uri->getUsername() && $uri->getPassword()) { $this->setAuth($uri->getUsername(), $uri->getPassword()); } // We have no ports, set the defaults if (! $uri->getPort()) { $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80)); } $this->uri = $uri; return $this; } /** * Get the URI for the next request * * @param boolean $as_string If true, will return the URI as a string * @return Zend_Uri_Http|string */ public function getUri($as_string = false) { if ($as_string && $this->uri instanceof Zend_Uri_Http) { return $this->uri->__toString(); } else { return $this->uri; } } /** * Set configuration parameters for this HTTP client * * @param Zend_Config | array $config * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setConfig($config = array()) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } elseif (! is_array($config)) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' .
 
gettype($config)); } foreach ($config as $k => $v) { $this->config[strtolower($k)] = $v; } // Pass configuration options to the adapter if it exists if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) { $this->adapter->setConfig($config); } return $this; } /** * Set the next request's method * * Validated the passed method and sets it. If we have files set for * POST requests, and the new method is not POST, the files are silently * dropped. * * @param string $method * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setMethod($method = self::GET) { if (! preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/', $method)) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method."); } if ($method == self::POST && $this->enctype === null) { $this->setEncType(self::ENC_URLENCODED); } $this->method = $method; return $this; } /** * Set one or more request headers * * This function can be used in several ways to set the client's request * headers: * 1. By providing two parameters: $name as the header to set (eg. 'Host') * and $value as it's value (eg. 'www.example.com'). * 2. By providing a single header string as the only parameter * eg. 'Host: www.example.com' * 3. By providing an array of headers as the first parameter * eg. array('host' => 'www.example.com', 'x-foo: bar'). In This case * the function will call itself recursively for each array item. * * @param string|array $name Header name, full header string ('Header: value') * or an array of headers * @param mixed $value Header value or null * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setHeaders($name, $value = null) { // If we got an array, go recusive! if (is_array($name)) { foreach ($name as $k => $v) { if (is_string($k)) { $this->setHeaders($k, $v); } else { $this->setHeaders($v, null); } } } else { // Check if $name needs to be split if ($value === null && (strpos($name, ':') > 0)) { list($name, $value) = explode(':', $name, 2); } // Make sure the name is valid if we are in strict mode if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name"); } $normalized_name = strtolower($name); // If $value is null or false, unset the header if ($value === null || $value === false) { unset($this->headers[$normalized_name]); // Else, set the header } else { // Header names are stored lowercase internally. if (is_string($value)) { $value = trim($value); } $this->headers[$normalized_name] = array($name, $value); } } return $this; } /** * Get the value of a specific header * * Note that if the header has more than one value, an array * will be returned. * * @param string $key * @return string|array|null The header value or null if it is not set */ public function getHeader($key) { $key = strtolower($key); if (isset($this->headers[$key])) { return $this->headers[$key][1]; } else { return null; } } /** * Set a GET parameter for the request. Wrapper around _setParameter * * @param string|array $name * @param string $value * @return Zend_Http_Client */ public function setParameterGet($name, $value = null) { if (is_array($name)) { foreach ($name as $k => $v) $this->_setParameter('GET', $k, $v); } else { $this->_setParameter('GET', $name, $value); } return $this; } /** * Set a POST parameter for the request. Wrapper around _setParameter * * @param string|array $name * @param string $value * @return Zend_Http_Client */ public function setParameterPost($name, $value = null) { if (is_array($name)) { foreach ($name as $k => $v) $this->_setParameter('POST', $k, $v); } else { $this->_setParameter('POST', $name, $value); } return $this; } /** * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost * * @param string $type GET or POST * @param string $name * @param string $value * @return null */ protected function _setParameter($type, $name, $value) { $parray = array(); $type = strtolower($type); switch ($type) { case 'get': $parray = &$this->paramsGet; break; case 'post': $parray = &$this->paramsPost; break; } if ($value === null) { if (isset($parray[$name])) unset($parray[$name]); } else { $parray[$name] = $value; } } /** * Get the number of redirections done on the last request * * @return int */ public function getRedirectionsCount() { return $this->redirectCounter; } /** * Set HTTP authentication parameters * * $type should be one of the supported types - see the self::AUTH_* * constants. * * To enable authentication: * <code> * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC); * </code> * * To disable authentication: * <code> * $this->setAuth(false); * </code> * * @see http://www.faqs.org/rfcs/rfc2617.html * @param string|false $user User name or false disable authentication * @param string $password Password * @param string $type Authentication type * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setAuth($user, $password = '', $type = self::AUTH_BASIC) { // If we got false or null, disable authentication if ($user === false || $user === null) { $this->auth = null; // Clear the auth information in the uri instance as well if ($this->uri instanceof Zend_Uri_Http) { $this->getUri()->setUsername(''); $this->getUri()->setPassword(''); } // Else, set up authentication } else { // Check we got a proper authentication type if (! defined('self::AUTH_' . strtoupper($type))) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'"); } $this->auth = array( 'user' => (string) $user, 'password' => (string) $password, 'type' => $type ); } return $this; } /** * Return the current cookie jar or null if none. * * @return Zend_Http_CookieJar|null */ public function getCookieJar() { return $this->cookiejar; } /** * Set a file to upload (using a POST request) * * Can be used in two ways: * * 1. $data is null (default): $filename is treated as the name if a local file which * will be read and sent. Will try to guess the content type using mime_content_type(). * 2. $data is set - $filename is sent as the file name, but $data is sent as the file * contents and no file is read from the file system. In this case, you need to * manually set the Content-Type ($ctype) or it will default to * application/octet-stream. * * @param string $filename Name of file to upload, or name to save as * @param string $formname Name of form element to send as * @param string $data Data to send (if null, $filename is read and sent) * @param string $ctype Content type to use (if $data is set and $ctype is * null, will be application/octet-stream) * @return Zend_Http_Client * @throws Zend_Http_Client_Exception */ public function setFileUpload($filename, $formname, $data = null, $ctype = null) { if ($data === null) { if (($data = @file_get_contents($filename)) === false) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload"); } if (! $ctype) { $ctype = $this->_detectFileMimeType($filename); } } // Force enctype to multipart/form-data $this->setEncType(self::ENC_FORMDATA); $this->files[] = array( 'formname' => $formname, 'filename' => basename($filename), 'ctype' => $ctype, 'data' => $data ); return $this; } /** * Set the encoding type for POST data * * @param string $enctype * @return Zend_Http_Client */ public function setEncType($enctype = self::ENC_URLENCODED) { $this->enctype = $enctype; return $this; } /** * Set the raw (already encoded) POST data. * * This function is here for two reasons: * 1. For advanced user who would like to set their own data, already encoded * 2. For backwards compatibilty: If someone uses the old post($data) method. * this method will be used to set the encoded data. * * $data can also be stream (such as file) from which the data will be read. * * @param string|resource $data * @param string $enctype * @return Zend_Http_Client */ public function setRawData($data, $enctype = null) { $this->raw_post_data = $data; $this->setEncType($enctype); if (is_resource($data)) { // We've got stream data $stat = @fstat($data); if($stat) { $this->setHeaders(self::CONTENT_LENGTH, $stat['size']); } } return $this; } /** * Clear all GET and POST parameters * * Should be used to reset the request parameters if the client is * used for several concurrent requests. * * clearAll parameter controls if we clean just parameters or also * headers and last_* * * @param bool $clearAll Should all data be cleared? * @return Zend_Http_Client */ public function resetParameters($clearAll = false) { // Reset parameter data $this->paramsGet = array(); $this->paramsPost = array(); $this->files = array(); $this->raw_post_data = null; if($clearAll) { $this->headers = array(); $this->last_request = null; $this->last_response = null; } else { // Clear outdated headers if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) { unset($this->headers[strtolower(self::CONTENT_TYPE)]); } if (isset($this->headers[strtolower(self::CONTENT_LENGTH)])) { unset($this->headers[strtolower(self::CONTENT_LENGTH)]); } } return $this; } /** * Get the last HTTP request as string * * @return string */ public function getLastRequest() { return $this->last_request; } /** * Get the last HTTP response received by this client * * If $config['storeresponse'] is set to false, or no response was * stored yet, will return null * * @return Zend_Http_Response or null if none */ public function getLastResponse() { return $this->last_response; } /** * Load the connection adapter * * While this method is not called more than one for a client, it is * seperated from ->request() to preserve logic and readability * * @param Zend_Http_Client_Adapter_Interface|string $adapter * @return null * @throws Zend_Http_Client_Exception */ public function setAdapter($adapter) { if (is_string($adapter)) { if (!class_exists($adapter)) { try { require_once 'Zend/Loader.php'; Zend_Loader::loadClass($adapter); } catch (Zend_Exception $e) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}",
 
0, $e); } } $adapter = new $adapter; } if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter'); } $this->adapter = $adapter; $config = $this->config; unset($config['adapter']); $this->adapter->setConfig($config); } /** * Load the connection adapter * * @return Zend_Http_Client_Adapter_Interface $adapter */ public function getAdapter() { return $this->adapter; } /** * Set streaming for received data * * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming * @return Zend_Http_Client */ public function setStream($streamfile = true) { $this->setConfig(array("output_stream" => $streamfile)); return $this; } /** * Get status of streaming for received data * @return boolean|string */ public function getStream() { return $this->config["output_stream"]; } /** * Create temporary stream * * @return resource */ protected function _openTempStream() { $this->_stream_name = $this->config['output_stream']; if(!is_string($this->_stream_name)) { // If name is not given, create temp name $this->_stream_name = tempnam(isset($this->config['stream_tmp_dir']) ?
 
$this->config['stream_tmp_dir'] : sys_get_temp_dir(), 'Zend_Http_Client'); } if (false === ($fp = @fopen($this->_stream_name, "w+b"))) { if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) { $this->adapter->close(); } if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}"); } return $fp; } /** * Send the HTTP request and return an HTTP response object * * @param string $method * @return Zend_Http_Response * @throws Zend_Http_Client_Exception */ public function request($method = null) { if (! $this->uri instanceof Zend_Uri_Http) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('No valid URI has been passed to the client'); } if ($method) { $this->setMethod($method); } $this->redirectCounter = 0; $response = null; // Make sure the adapter is loaded if ($this->adapter == null) { $this->setAdapter($this->config['adapter']); } // Send the first request. If redirected, continue. do { // Clone the URI and add the additional GET parameters to it $uri = clone $this->uri; if (! empty($this->paramsGet)) { $query = $uri->getQuery(); if (! empty($query)) { $query .= '&'; } $query .= http_build_query($this->paramsGet, null, '&'); $uri->setQuery($query); } $body = $this->_prepareBody(); $headers = $this->_prepareHeaders(); // check that adapter supports streaming before using it if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Adapter does not support streaming'); } // Open the connection, send the request and read the response $this->adapter->connect($uri->getHost(), $uri->getPort(), ($uri->getScheme() == 'https' ? true : false)); if($this->config['output_stream']) { if($this->adapter instanceof Zend_Http_Client_Adapter_Stream) { $stream = $this->_openTempStream(); $this->adapter->setOutputStream($stream); } else { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Adapter does not support streaming'); } } $this->last_request = $this->adapter->write($this->method, $uri, $this->config['httpversion'], $headers, $body); $response = $this->adapter->read(); if (! $response) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception('Unable to read response, or response is empty'); } if($this->config['output_stream']) { rewind($stream); // cleanup the adapter $this->adapter->setOutputStream(null); $response = Zend_Http_Response_Stream::fromStream($response, $stream); $response->setStreamName($this->_stream_name); if(!is_string($this->config['output_stream'])) { // we used temp name, will need to clean up $response->setCleanup(true); } } else { $response = Zend_Http_Response::fromString($response); } if ($this->config['storeresponse']) { $this->last_response = $response; } // Load cookies into cookie jar if (isset($this->cookiejar)) { $this->cookiejar->addCookiesFromResponse($response, $uri); } // If we got redirected, look for the Location header if ($response->isRedirect() && ($location = $response->getHeader('location'))) { // Check whether we send the exact same request again, or drop the parameters // and send a GET request if ($response->getStatus() == 303 || ((! $this->config['strictredirects']) && ($response->getStatus() == 302 || $response->getStatus() == 301))) { $this->resetParameters(); $this->setMethod(self::GET); } // If we got a well formed absolute URI if (Zend_Uri_Http::check($location)) { $this->setHeaders('host', null); $this->setUri($location); } else { // Split into path and query and set the query if (strpos($location, '?') !== false) { list($location, $query) = explode('?', $location, 2); } else { $query = ''; } $this->uri->setQuery($query); // Else, if we got just an absolute path, set it if(strpos($location, '/') === 0) { $this->uri->setPath($location); // Else, assume we have a relative path } else { // Get the current path directory, removing any trailing slashes $path = $this->uri->getPath(); $path = rtrim(substr($path, 0, strrpos($path, '/')), "/"); $this->uri->setPath($path . '/' . $location); } } ++$this->redirectCounter; } else { // If we didn't get any location, stop redirecting break; } } while ($this->redirectCounter < $this->config['maxredirects']); return $response; } /** * Prepare the request headers * * @return array */ protected function _prepareHeaders() { $headers = array(); // Set the host header if (! isset($this->headers['host'])) { $host = $this->uri->getHost(); // If the port is not default, add it if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) || ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) { $host .= ':' . $this->uri->getPort(); } $headers[] = "Host: {$host}"; } // Set the connection header if (! isset($this->headers['connection'])) { if (! $this->config['keepalive']) { $headers[] = "Connection: close"; } } // Set the Accept-encoding header if not set - depending on whether // zlib is available or not. if (! isset($this->headers['accept-encoding'])) { if (function_exists('gzinflate')) { $headers[] = 'Accept-encoding: gzip, deflate'; } else { $headers[] = 'Accept-encoding: identity'; } } // Set the Content-Type header if ($this->method == self::POST && (! isset($this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) { $headers[] = self::CONTENT_TYPE . ': ' . $this->enctype; } // Set the user agent header if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) { $headers[] = "User-Agent: {$this->config['useragent']}"; } // Set HTTP authentication if needed if (is_array($this->auth)) { $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']); $headers[] = "Authorization: {$auth}"; } // Load cookies from cookie jar if (isset($this->cookiejar)) { $cookstr = $this->cookiejar->getMatchingCookies($this->uri, true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT); if ($cookstr) { $headers[] = "Cookie: {$cookstr}"; } } // Add all other user defined headers foreach ($this->headers as $header) { list($name, $value) = $header; if (is_array($value)) { $value = implode(', ', $value); } $headers[] = "$name: $value"; } return $headers; } /** * Prepare the request body (for POST and PUT requests) * * @return string * @throws Zend_Http_Client_Exception */ protected function _prepareBody() { // According to RFC2616, a TRACE request should not have a body. if ($this->method == self::TRACE) { return ''; } if (isset($this->raw_post_data) && is_resource($this->raw_post_data)) { return $this->raw_post_data; } // If mbstring overloads substr and strlen functions, we have to // override it's internal encoding if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { $mbIntEnc = mb_internal_encoding(); mb_internal_encoding('ASCII'); } // If we have raw_post_data set, just use it as the body. if (isset($this->raw_post_data)) { $this->setHeaders(self::CONTENT_LENGTH, strlen($this->raw_post_data)); if (isset($mbIntEnc)) { mb_internal_encoding($mbIntEnc); } return $this->raw_post_data; } $body = ''; // If we have files to upload, force enctype to multipart/form-data if (count ($this->files) > 0) { $this->setEncType(self::ENC_FORMDATA); } // If we have POST parameters or files, encode and add them to the body if (count($this->paramsPost) > 0 || count($this->files) > 0) { switch($this->enctype) { case self::ENC_FORMDATA: // Encode body as multipart/form-data $boundary = '---ZENDHTTPCLIENT-' . md5(microtime()); $this->setHeaders(self::CONTENT_TYPE, self::ENC_FORMDATA . "; boundary={$boundary}"); // Get POST parameters and encode them $params = self::_flattenParametersArray($this->paramsPost); foreach ($params as $pp) { $body .= self::encodeFormData($boundary, $pp[0], $pp[1]); } // Encode files foreach ($this->files as $file) { $fhead = array(self::CONTENT_TYPE => $file['ctype']); $body .= self::encodeFormData($boundary, $file['formname'], $file['data'],
 
$file['filename'], $fhead); } $body .= "--{$boundary}--\r\n"; break; case self::ENC_URLENCODED: // Encode body as application/x-www-form-urlencoded $this->setHeaders(self::CONTENT_TYPE, self::ENC_URLENCODED); $body = http_build_query($this->paramsPost, '', '&'); break; default: if (isset($mbIntEnc)) { mb_internal_encoding($mbIntEnc); } /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}'
 automatically."
. " Please use Zend_Http_Client::setRawData to send this kind of content."); break; } } // Set the Content-Length if we have a body or if request is POST/PUT if ($body || $this->method == self::POST || $this->method == self::PUT) { $this->setHeaders(self::CONTENT_LENGTH, strlen($body)); } if (isset($mbIntEnc)) { mb_internal_encoding($mbIntEnc); } return $body; } /** * Helper method that gets a possibly multi-level parameters array (get or * post) and flattens it. * * The method returns an array of (key, value) pairs (because keys are not * necessarily unique. If one of the parameters in as array, it will also * add a [] suffix to the key. * * This method is deprecated since Zend Framework 1.9 in favour of * self::_flattenParametersArray() and will be dropped in 2.0 * * @deprecated since 1.9 * * @param array $parray The parameters array * @param bool $urlencode Whether to urlencode the name and value * @return array */ protected function _getParametersRecursive($parray, $urlencode = false) { // Issue a deprecated notice trigger_error("The " . __METHOD__ . " method is deprecated and will be dropped in 2.0.", E_USER_NOTICE); if (! is_array($parray)) { return $parray; } $parameters = array(); foreach ($parray as $name => $value) { if ($urlencode) { $name = urlencode($name); } // If $value is an array, iterate over it if (is_array($value)) { $name .= ($urlencode ? '%5B%5D' : '[]'); foreach ($value as $subval) { if ($urlencode) { $subval = urlencode($subval); } $parameters[] = array($name, $subval); } } else { if ($urlencode) { $value = urlencode($value); } $parameters[] = array($name, $value); } } return $parameters; } /** * Attempt to detect the MIME type of a file using available extensions * * This method will try to detect the MIME type of a file. If the fileinfo * extension is available, it will be used. If not, the mime_magic * extension which is deprected but is still available in many PHP setups * will be tried. * * If neither extension is available, the default application/octet-stream * MIME type will be returned * * @param string $file File path * @return string MIME type */ protected function _detectFileMimeType($file) { $type = null; // First try with fileinfo functions if (function_exists('finfo_open')) { if (self::$_fileInfoDb === null) { self::$_fileInfoDb = @finfo_open(FILEINFO_MIME); } if (self::$_fileInfoDb) { $type = finfo_file(self::$_fileInfoDb, $file); } } elseif (function_exists('mime_content_type')) { $type = mime_content_type($file); } // Fallback to the default application/octet-stream if (! $type) { $type = 'application/octet-stream'; } return $type; } /** * Encode data to a multipart/form-data part suitable for a POST request. * * @param string $boundary * @param string $name * @param mixed $value * @param string $filename * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" =>
 
"binary") * @return string */ public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array()) { $ret = "--{$boundary}\r\n" . 'Content-Disposition: form-data; name="' . $name .'"'; if ($filename) { $ret .= '; filename="' . $filename . '"'; } $ret .= "\r\n"; foreach ($headers as $hname => $hvalue) { $ret .= "{$hname}: {$hvalue}\r\n"; } $ret .= "\r\n"; $ret .= "{$value}\r\n"; return $ret; } /** * Create a HTTP authentication "Authorization:" header according to the * specified user, password and authentication method. * * @see http://www.faqs.org/rfcs/rfc2617.html * @param string $user * @param string $password * @param string $type * @return string * @throws Zend_Http_Client_Exception */ public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC) { $authHeader = null; switch ($type) { case self::AUTH_BASIC: // In basic authentication, the user name cannot contain ":" if (strpos($user, ':') !== false) { /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP
 authentication"
); } $authHeader = 'Basic ' . base64_encode($user . ':' . $password); break; //case self::AUTH_DIGEST: /** * @todo Implement digest authentication */ // break; default: /** @see Zend_Http_Client_Exception */ if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'"); } return $authHeader; } /** * Convert an array of parameters into a flat array of (key, value) pairs * * Will flatten a potentially multi-dimentional array of parameters (such * as POST parameters) into a flat array of (key, value) paris. In case * of multi-dimentional arrays, square brackets ([]) will be added to the * key to indicate an array. * * @since 1.9 * * @param array $parray * @param string $prefix * @return array */ static protected function _flattenParametersArray($parray, $prefix = null) { if (! is_array($parray)) { return $parray; } $parameters = array(); foreach($parray as $name => $value) { // Calculate array key if ($prefix) { if (is_int($name)) { $key = $prefix . '[]'; } else { $key = $prefix . "[$name]"; } } else { $key = $name; } if (is_array($value)) { $parameters = array_merge($parameters, self::_flattenParametersArray($value, $key)); } else { $parameters[] = array($key, $value); } } return $parameters; } } /** * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters. * * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters. The * class also enables validating whether the cookie should be sent to the server in * a specified scenario according to the request URI, the expiry time and whether * session cookies should be used or not. Generally speaking cookies should be * contained in a Cookiejar object, or instantiated manually and added to an HTTP * request. * * See http://wp.netscape.com/newsref/std/cookie_spec.html for some specs. * * @category Zend * @package Zend_Http * @subpackage Cookie * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @version $Id: Cookie.php 21020 2010-02-11 17:27:23Z shahar $ * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Cookie { /** * Cookie name * * @var string */ protected $name; /** * Cookie value * * @var string */ protected $value; /** * Cookie expiry date * * @var int */ protected $expires; /** * Cookie domain * * @var string */ protected $domain; /** * Cookie path * * @var string */ protected $path; /** * Whether the cookie is secure or not * * @var boolean */ protected $secure; /** * Whether the cookie value has been encoded/decoded * * @var boolean */ protected $encodeValue; /** * Cookie object constructor * * @todo Add validation of each one of the parameters (legal domain, etc.) * * @param string $name * @param string $value * @param string $domain * @param int $expires * @param string $path * @param bool $secure */ public function __construct($name, $value, $domain, $expires = null, $path = null, $secure = false) { if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014
 (
{$name})"); } if (! $this->name = (string) $name) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception('Cookies must have a name'); } if (! $this->domain = (string) $domain) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception('Cookies must have a domain'); } $this->value = (string) $value; $this->expires = ($expires === null ? null : (int) $expires); $this->path = ($path ? $path : '/'); $this->secure = $secure; } /** * Get Cookie name * * @return string */ public function getName() { return $this->name; } /** * Get cookie value * * @return string */ public function getValue() { return $this->value; } /** * Get cookie domain * * @return string */ public function getDomain() { return $this->domain; } /** * Get the cookie path * * @return string */ public function getPath() { return $this->path; } /** * Get the expiry time of the cookie, or null if no expiry time is set * * @return int|null */ public function getExpiryTime() { return $this->expires; } /** * Check whether the cookie should only be sent over secure connections * * @return boolean */ public function isSecure() { return $this->secure; } /** * Check whether the cookie has expired * * Always returns false if the cookie is a session cookie (has no expiry time) * * @param int $now Timestamp to consider as "now" * @return boolean */ public function isExpired($now = null) { if ($now === null) $now = time(); if (is_int($this->expires) && $this->expires < $now) { return true; } else { return false; } } /** * Check whether the cookie is a session cookie (has no expiry time set) * * @return boolean */ public function isSessionCookie() { return ($this->expires === null); } /** * Checks whether the cookie should be sent or not in a specific scenario * * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path) * @param boolean $matchSessionCookies Whether to send session cookies * @param int $now Override the current time when checking for expiry time * @return boolean */ public function match($uri, $matchSessionCookies = true, $now = null) { if (is_string ($uri)) { $uri = Zend_Uri_Http::factory($uri); } // Make sure we have a valid Zend_Uri_Http object if (! ($uri->valid() && ($uri->getScheme() == 'http' || $uri->getScheme() == 'https'))) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception('Passed URI is not a valid HTTP or HTTPS URI'); } // Check that the cookie is secure (if required) and not expired if ($this->secure && $uri->getScheme() != 'https') return false; if ($this->isExpired($now)) return false; if ($this->isSessionCookie() && ! $matchSessionCookies) return false; // Check if the domain matches if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) { return false; } // Check that path matches using prefix match if (! self::matchCookiePath($this->getPath(), $uri->getPath())) { return false; } // If we didn't die until now, return true. return true; } /** * Get the cookie as a string, suitable for sending as a "Cookie" header in an * HTTP request * * @return string */ public function __toString() { if ($this->encodeValue) { return $this->name . '=' . urlencode($this->value) . ';'; } return $this->name . '=' . $this->value . ';'; } /** * Generate a new Cookie object from a cookie string * (for example the value of the Set-Cookie HTTP header) * * @param string $cookieStr * @param Zend_Uri_Http|string $refUri Reference URI for default values (domain, path) * @param boolean $encodeValue Weither or not the cookie's value should be * passed through urlencode/urldecode * @return Zend_Http_Cookie A new Zend_Http_Cookie object or false on failure. */ public static function fromString($cookieStr, $refUri = null, $encodeValue = true) { // Set default values if (is_string($refUri)) { $refUri = Zend_Uri_Http::factory($refUri); } $name = ''; $value = ''; $domain = ''; $path = ''; $expires = null; $secure = false; $parts = explode(';', $cookieStr); // If first part does not include '=', fail if (strpos($parts[0], '=') === false) return false; // Get the name and value of the cookie list($name, $value) = explode('=', trim(array_shift($parts)), 2); $name = trim($name); if ($encodeValue) { $value = urldecode(trim($value)); } // Set default domain and path if ($refUri instanceof Zend_Uri_Http) { $domain = $refUri->getHost(); $path = $refUri->getPath(); $path = substr($path, 0, strrpos($path, '/')); } // Set other cookie parameters foreach ($parts as $part) { $part = trim($part); if (strtolower($part) == 'secure') { $secure = true; continue; } $keyValue = explode('=', $part, 2); if (count($keyValue) == 2) { list($k, $v) = $keyValue; switch (strtolower($k)) { case 'expires': if(($expires = strtotime($v)) === false) { /** * The expiration is past Tue, 19 Jan 2038 03:14:07 UTC * the maximum for 32-bit signed integer. Zend_Date * can get around that limit. * * @see Zend_Date */ require_once 'Zend/Date.php'; $expireDate = new Zend_Date($v); $expires = $expireDate->getTimestamp(); } break; case 'path': $path = $v; break; case 'domain': $domain = $v; break; default: break; } } } if ($name !== '') { $ret = new self($name, $value, $domain, $expires, $path, $secure); $ret->encodeValue = ($encodeValue) ? true : false; return $ret; } else { return false; } } /** * Check if a cookie's domain matches a host name. * * Used by Zend_Http_Cookie and Zend_Http_CookieJar for cookie matching * * @param string $cookieDomain * @param string $host * * @return boolean */ public static function matchCookieDomain($cookieDomain, $host) { if (! $cookieDomain) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("\$cookieDomain is expected to be a cookie domain"); } if (! $host) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("\$host is expected to be a host name"); } $cookieDomain = strtolower($cookieDomain); $host = strtolower($host); if ($cookieDomain[0] == '.') { $cookieDomain = substr($cookieDomain, 1); } // Check for either exact match or suffix match return ($cookieDomain == $host || preg_match("/\.$cookieDomain$/", $host)); } /** * Check if a cookie's path matches a URL path * * Used by Zend_Http_Cookie and Zend_Http_CookieJar for cookie matching * * @param string $cookiePath * @param string $path * @return boolean */ public static function matchCookiePath($cookiePath, $path) { if (! $cookiePath) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("\$cookiePath is expected to be a cookie path"); } if (! $path) { if (!class_exists('Zend_Exception')) require 'exception.php'; throw new Zend_Http_Exception("\$path is expected to be a host name"); } return (strpos($path, $cookiePath) === 0); } } /** * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It * includes easy access to all the response's different elemts, as well as some * convenience methods for parsing and validating HTTP responses. * * @package Zend_Http * @subpackage Response * @version $Id: Response.php 17131 2009-07-26 10:03:39Z shahar $ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Http_Response_Stream extends Zend_Http_Response { /** * Response as stream * * @var resource */ protected $stream; /** * The name of the file containing the stream * * Will be empty if stream is not file-based. * * @var string */ protected $stream_name; /** * Should we clean up the stream file when this response is closed? * * @var boolean */ protected $_cleanup; /** * Get the response as stream * * @return resourse */ public function getStream() { return $this->stream; } /** * Set the response stream * * @param resourse $stream * @return Zend_Http_Response_Stream */ public function setStream($stream) { $this->stream = $stream; return $this; } /** * Get the cleanup trigger * * @return boolean */ public function getCleanup() { return $this->_cleanup; } /** * Set the cleanup trigger * * @param $cleanup Set cleanup trigger */ public function setCleanup($cleanup = true) { $this->_cleanup = $cleanup; } /** * Get file name associated with the stream * * @return string */ public function getStreamName() { return $this->stream_name; } /** * Set file name associated with the stream * * @param string $stream_name Name to set * @return Zend_Http_Response_Stream */ public function setStreamName($stream_name) { $this->stream_name = $stream_name; return $this; } /** * HTTP response constructor * * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP * response string and create a new Zend_Http_Response object. * * NOTE: The constructor no longer accepts nulls or empty values for the code and * headers and will throw an exception if the passed values do not form a valid HTTP * responses. * * If no message is passed, the message will be guessed according to the response code. * * @param int $code Response code (200, 404, ...) * @param array $headers Headers array * @param string $body Response body * @param string $version HTTP version * @param string $message Response code as text * @throws Zend_Http_Exception */ public function __construct($code, $headers, $body = null, $version = '1.1', $message = null) { if(is_resource($body)) { $this->setStream($body); $body = ''; } parent::__construct($code, $headers, $body, $version, $message); } /** * Create a new Zend_Http_Response_Stream object from a string * * @param string $response_str * @param resource $stream * @return Zend_Http_Response_Stream */ public static function fromStream($response_str, $stream) { $code = self::extractCode($response_str); $headers = self::extractHeaders($response_str); $version = self::extractVersion($response_str); $message = self::extractMessage($response_str); return new self($code, $headers, $stream, $version, $message); } /** * Get the response body as string * * This method returns the body of the HTTP response (the content), as it * should be in it's readable version - that is, after decoding it (if it * was decoded), deflating it (if it was gzip compressed), etc. * * If you want to get the raw body (as transfered on wire) use * $this->getRawBody() instead. * * @return string */ public function getBody() { if($this->stream != null) { $this->readStream(); } return parent::getBody(); } /** * Get the raw response body (as transfered "on wire") as string * * If the body is encoded (with Transfer-Encoding, not content-encoding - * IE "chunked" body), gzip compressed, etc. it will not be decoded. * * @return string */ public function getRawBody() { if($this->stream) { $this->readStream(); } return $this->body; } /** * Read stream content and return it as string * * Function reads the remainder of the body from the stream and closes the stream. * * @return string */ protected function readStream() { if(!is_resource($this->stream)) { return ''; } if(isset($headers['content-length'])) { $this->body = stream_get_contents($this->stream, $headers['content-length']); } else { $this->body = stream_get_contents($this->stream); } fclose($this->stream); $this->stream = null; } public function __destruct() { if(is_resource($this->stream)) { fclose($this->stream); $this->stream = null; } if($this->_cleanup) { @unlink($this->stream_name); } } }