File indexing completed on 2024-12-22 05:36:47

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Http
0018  * @subpackage Client
0019  * @version    $Id$
0020  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0021  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0022  */
0023 
0024 /**
0025  * @see Zend_Loader
0026  */
0027 // require_once 'Zend/Loader.php';
0028 
0029 
0030 /**
0031  * @see Zend_Uri
0032  */
0033 // require_once 'Zend/Uri.php';
0034 
0035 
0036 /**
0037  * @see Zend_Http_Client_Adapter_Interface
0038  */
0039 // require_once 'Zend/Http/Client/Adapter/Interface.php';
0040 
0041 
0042 /**
0043  * @see Zend_Http_Header_HeaderValue
0044  */
0045 // require_once 'Zend/Http/Header/HeaderValue.php';
0046 
0047 
0048 /**
0049  * @see Zend_Http_Response
0050  */
0051 // require_once 'Zend/Http/Response.php';
0052 
0053 /**
0054  * @see Zend_Http_Response_Stream
0055  */
0056 // require_once 'Zend/Http/Response/Stream.php';
0057 
0058 /**
0059  * Zend_Http_Client is an implementation of an HTTP client in PHP. The client
0060  * supports basic features like sending different HTTP requests and handling
0061  * redirections, as well as more advanced features like proxy settings, HTTP
0062  * authentication and cookie persistence (using a Zend_Http_CookieJar object)
0063  *
0064  * @todo Implement proxy settings
0065  * @category   Zend
0066  * @package    Zend_Http
0067  * @subpackage Client
0068  * @throws     Zend_Http_Client_Exception
0069  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0070  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0071  */
0072 class Zend_Http_Client
0073 {
0074     /**
0075      * HTTP request methods
0076      */
0077     const GET     = 'GET';
0078     const POST    = 'POST';
0079     const PUT     = 'PUT';
0080     const HEAD    = 'HEAD';
0081     const DELETE  = 'DELETE';
0082     const TRACE   = 'TRACE';
0083     const OPTIONS = 'OPTIONS';
0084     const CONNECT = 'CONNECT';
0085     const MERGE   = 'MERGE';
0086     const PATCH   = 'PATCH';
0087 
0088     /**
0089      * Supported HTTP Authentication methods
0090      */
0091     const AUTH_BASIC = 'basic';
0092     //const AUTH_DIGEST = 'digest'; <-- not implemented yet
0093 
0094     /**
0095      * HTTP protocol versions
0096      */
0097     const HTTP_1 = '1.1';
0098     const HTTP_0 = '1.0';
0099 
0100     /**
0101      * Content attributes
0102      */
0103     const CONTENT_TYPE   = 'Content-Type';
0104     const CONTENT_LENGTH = 'Content-Length';
0105 
0106     /**
0107      * POST data encoding methods
0108      */
0109     const ENC_URLENCODED = 'application/x-www-form-urlencoded';
0110     const ENC_FORMDATA   = 'multipart/form-data';
0111 
0112     /**
0113      * Value types for Body key/value pairs
0114      */
0115     const VTYPE_SCALAR  = 'SCALAR';
0116     const VTYPE_FILE    = 'FILE';
0117 
0118     /**
0119      * Configuration array, set using the constructor or using ::setConfig()
0120      *
0121      * @var array
0122      */
0123     protected $config = array(
0124         'maxredirects'    => 5,
0125         'strictredirects' => false,
0126         'useragent'       => 'Zend_Http_Client',
0127         'timeout'         => 10,
0128         'adapter'         => 'Zend_Http_Client_Adapter_Socket',
0129         'httpversion'     => self::HTTP_1,
0130         'keepalive'       => false,
0131         'storeresponse'   => true,
0132         'strict'          => true,
0133         'output_stream'   => false,
0134         'encodecookies'   => true,
0135         'rfc3986_strict'  => false
0136     );
0137 
0138     /**
0139      * The adapter used to perform the actual connection to the server
0140      *
0141      * @var Zend_Http_Client_Adapter_Interface
0142      */
0143     protected $adapter = null;
0144 
0145     /**
0146      * Request URI
0147      *
0148      * @var Zend_Uri_Http
0149      */
0150     protected $uri = null;
0151 
0152     /**
0153      * Associative array of request headers
0154      *
0155      * @var array
0156      */
0157     protected $headers = array();
0158 
0159     /**
0160      * HTTP request method
0161      *
0162      * @var string
0163      */
0164     protected $method = self::GET;
0165 
0166     /**
0167      * Associative array of GET parameters
0168      *
0169      * @var array
0170      */
0171     protected $paramsGet = array();
0172 
0173     /**
0174      * Associative array of POST parameters
0175      *
0176      * @var array
0177      */
0178     protected $paramsPost = array();
0179 
0180     /**
0181      * Request body content type (for POST requests)
0182      *
0183      * @var string
0184      */
0185     protected $enctype = null;
0186 
0187     /**
0188      * The raw post data to send. Could be set by setRawData($data, $enctype).
0189      *
0190      * @var string
0191      */
0192     protected $raw_post_data = null;
0193 
0194     /**
0195      * HTTP Authentication settings
0196      *
0197      * Expected to be an associative array with this structure:
0198      * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
0199      * Where 'type' should be one of the supported authentication types (see the AUTH_*
0200      * constants), for example 'basic' or 'digest'.
0201      *
0202      * If null, no authentication will be used.
0203      *
0204      * @var array|null
0205      */
0206     protected $auth;
0207 
0208     /**
0209      * File upload arrays (used in POST requests)
0210      *
0211      * An associative array, where each element is of the format:
0212      *   'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
0213      *
0214      * @var array
0215      */
0216     protected $files = array();
0217 
0218     /**
0219      * Ordered list of keys from key/value pair data to include in body
0220      *
0221      * An associative array, where each element is of the format:
0222      *   '<field name>' => VTYPE_SCALAR | VTYPE_FILE
0223      *
0224      * @var array
0225      */
0226     protected $body_field_order = array();
0227 
0228     /**
0229      * The client's cookie jar
0230      *
0231      * @var Zend_Http_CookieJar
0232      */
0233     protected $cookiejar = null;
0234 
0235     /**
0236      * The last HTTP request sent by the client, as string
0237      *
0238      * @var string
0239      */
0240     protected $last_request = null;
0241 
0242     /**
0243      * The last HTTP response received by the client
0244      *
0245      * @var Zend_Http_Response
0246      */
0247     protected $last_response = null;
0248 
0249     /**
0250      * Redirection counter
0251      *
0252      * @var int
0253      */
0254     protected $redirectCounter = 0;
0255 
0256     /**
0257      * Status for unmasking GET array params
0258      *
0259      * @var boolean
0260      */
0261     protected $_unmaskStatus = false;
0262 
0263     /**
0264      * Status if the http_build_query function escapes brackets
0265      *
0266      * @var boolean
0267      */
0268     protected $_queryBracketsEscaped = true;
0269 
0270     /**
0271      * Fileinfo magic database resource
0272      *
0273      * This variable is populated the first time _detectFileMimeType is called
0274      * and is then reused on every call to this method
0275      *
0276      * @var resource
0277      */
0278     protected static $_fileInfoDb = null;
0279 
0280     /**
0281      * Constructor method. Will create a new HTTP client. Accepts the target
0282      * URL and optionally configuration array.
0283      *
0284      * @param Zend_Uri_Http|string $uri
0285      * @param array                $config Configuration key-value pairs.
0286      * @throws Zend_Http_Client_Exception
0287      * @throws Zend_Uri_Exception
0288      */
0289     public function __construct($uri = null, $config = null)
0290     {
0291         if ($uri !== null) {
0292             $this->setUri($uri);
0293         }
0294         if ($config !== null) {
0295             $this->setConfig($config);
0296         }
0297 
0298         $this->_queryBracketsEscaped = version_compare(phpversion(), '5.1.3', '>=');
0299     }
0300 
0301     /**
0302      * Set the URI for the next request
0303      *
0304      * @param Zend_Uri_Http|string $uri
0305      * @return Zend_Http_Client
0306      * @throws Zend_Http_Client_Exception
0307      * @throws Zend_Uri_Exception
0308      */
0309     public function setUri($uri)
0310     {
0311         if ($uri instanceof Zend_Uri_Http) {
0312             // clone the URI in order to keep the passed parameter constant
0313             $uri = clone $uri;
0314         } elseif (is_string($uri)) {
0315             $uri = Zend_Uri::factory($uri);
0316         }
0317 
0318         if (!$uri instanceof Zend_Uri_Http) {
0319             /** @see Zend_Http_Client_Exception */
0320             // require_once 'Zend/Http/Client/Exception.php';
0321             throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
0322         }
0323 
0324         // Set auth if username and password has been specified in the uri
0325         if ($uri->getUsername() && $uri->getPassword()) {
0326             $this->setAuth($uri->getUsername(), $uri->getPassword());
0327         }
0328 
0329         // We have no ports, set the defaults
0330         if (! $uri->getPort()) {
0331             $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80));
0332         }
0333 
0334         $this->uri = $uri;
0335 
0336         return $this;
0337     }
0338 
0339     /**
0340      * Get the URI for the next request
0341      *
0342      * @param boolean $as_string If true, will return the URI as a string
0343      * @return Zend_Uri_Http|string
0344      */
0345     public function getUri($as_string = false)
0346     {
0347         if ($as_string && $this->uri instanceof Zend_Uri_Http) {
0348             return $this->uri->__toString();
0349         } else {
0350             return $this->uri;
0351         }
0352     }
0353 
0354     /**
0355      * Set configuration parameters for this HTTP client
0356      *
0357      * @param  Zend_Config | array $config
0358      * @return Zend_Http_Client
0359      * @throws Zend_Http_Client_Exception
0360      */
0361     public function setConfig($config = array())
0362     {
0363         if ($config instanceof Zend_Config) {
0364             $config = $config->toArray();
0365 
0366         } elseif (! is_array($config)) {
0367             /** @see Zend_Http_Client_Exception */
0368             // require_once 'Zend/Http/Client/Exception.php';
0369             throw new Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' . gettype($config));
0370         }
0371 
0372         foreach ($config as $k => $v) {
0373             $this->config[strtolower($k)] = $v;
0374         }
0375 
0376         // Pass configuration options to the adapter if it exists
0377         if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
0378             $this->adapter->setConfig($config);
0379         }
0380 
0381         return $this;
0382     }
0383 
0384     /**
0385      * Set the next request's method
0386      *
0387      * Validated the passed method and sets it. If we have files set for
0388      * POST requests, and the new method is not POST, the files are silently
0389      * dropped.
0390      *
0391      * @param string $method
0392      * @return Zend_Http_Client
0393      * @throws Zend_Http_Client_Exception
0394      */
0395     public function setMethod($method = self::GET)
0396     {
0397         if (! preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/', $method)) {
0398             // require_once 'Zend/Http/Client/Exception.php';
0399             throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
0400         }
0401 
0402         if (($method == self::POST
0403                 || $method == self::PUT
0404                 || $method == self::DELETE
0405                 || $method == self::PATCH
0406                 || $method == self::OPTIONS)
0407             && $this->enctype === null
0408         ) {
0409             $this->setEncType(self::ENC_URLENCODED);
0410         }
0411 
0412         $this->method = $method;
0413 
0414         return $this;
0415     }
0416 
0417     /**
0418      * Set one or more request headers
0419      *
0420      * This function can be used in several ways to set the client's request
0421      * headers:
0422      * 1. By providing two parameters: $name as the header to set (e.g. 'Host')
0423      *    and $value as it's value (e.g. 'www.example.com').
0424      * 2. By providing a single header string as the only parameter
0425      *    e.g. 'Host: www.example.com'
0426      * 3. By providing an array of headers as the first parameter
0427      *    e.g. array('host' => 'www.example.com', 'x-foo: bar'). In This case
0428      *    the function will call itself recursively for each array item.
0429      *
0430      * @param string|array $name Header name, full header string ('Header: value')
0431      *     or an array of headers
0432      * @param mixed $value Header value or null
0433      * @return Zend_Http_Client
0434      * @throws Zend_Http_Client_Exception
0435      */
0436     public function setHeaders($name, $value = null)
0437     {
0438         // If we got an array, go recursive!
0439         if (is_array($name)) {
0440             foreach ($name as $k => $v) {
0441                 if (is_string($k)) {
0442                     $this->setHeaders($k, $v);
0443                     continue;
0444                 }
0445                 $this->setHeaders($v, null);
0446             }
0447             return $this;
0448         }
0449 
0450         // Check if $name needs to be split
0451         if ($value === null && (strpos($name, ':') > 0)) {
0452             list($name, $value) = explode(':', $name, 2);
0453         }
0454 
0455         // Make sure the name is valid if we are in strict mode
0456         if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) {
0457             // require_once 'Zend/Http/Client/Exception.php';
0458             throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
0459         }
0460 
0461         $normalized_name = strtolower($name);
0462 
0463         // If $value is null or false, unset the header
0464         if ($value === null || $value === false) {
0465             unset($this->headers[$normalized_name]);
0466             return $this;
0467         }
0468 
0469         // Validate value
0470         $this->_validateHeaderValue($value);
0471 
0472         // Header names are stored lowercase internally.
0473         if (is_string($value)) {
0474             $value = trim($value);
0475         }
0476         $this->headers[$normalized_name] = array($name, $value);
0477 
0478         return $this;
0479     }
0480 
0481     /**
0482      * Get the value of a specific header
0483      *
0484      * Note that if the header has more than one value, an array
0485      * will be returned.
0486      *
0487      * @param string $key
0488      * @return string|array|null The header value or null if it is not set
0489      */
0490     public function getHeader($key)
0491     {
0492         $key = strtolower($key);
0493         if (isset($this->headers[$key])) {
0494             return $this->headers[$key][1];
0495         } else {
0496             return null;
0497         }
0498     }
0499 
0500     /**
0501      * Set a GET parameter for the request. Wrapper around _setParameter
0502      *
0503      * @param string|array $name
0504      * @param string $value
0505      * @return Zend_Http_Client
0506      */
0507     public function setParameterGet($name, $value = null)
0508     {
0509         if (is_array($name)) {
0510             foreach ($name as $k => $v)
0511                 $this->_setParameter('GET', $k, $v);
0512         } else {
0513             $this->_setParameter('GET', $name, $value);
0514         }
0515 
0516         return $this;
0517     }
0518 
0519     /**
0520      * Set a POST parameter for the request. Wrapper around _setParameter
0521      *
0522      * @param string|array $name
0523      * @param string $value
0524      * @return Zend_Http_Client
0525      */
0526     public function setParameterPost($name, $value = null)
0527     {
0528         if (is_array($name)) {
0529             foreach ($name as $k => $v)
0530                 $this->_setParameter('POST', $k, $v);
0531         } else {
0532             $this->_setParameter('POST', $name, $value);
0533         }
0534 
0535         return $this;
0536     }
0537 
0538     /**
0539      * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
0540      *
0541      * @param string $type GET or POST
0542      * @param string $name
0543      * @param string $value
0544      * @return null
0545      */
0546     protected function _setParameter($type, $name, $value)
0547     {
0548         $parray = array();
0549         $type = strtolower($type);
0550         switch ($type) {
0551             case 'get':
0552                 $parray = &$this->paramsGet;
0553                 break;
0554             case 'post':
0555                 $parray = &$this->paramsPost;
0556                 if ( $value === null ) {
0557                     if (isset($this->body_field_order[$name]))
0558                         unset($this->body_field_order[$name]);
0559                 } else {
0560                     $this->body_field_order[$name] = self::VTYPE_SCALAR;
0561                 }
0562                 break;
0563         }
0564 
0565         if ($value === null) {
0566             if (isset($parray[$name])) unset($parray[$name]);
0567         } else {
0568             $parray[$name] = $value;
0569         }
0570     }
0571 
0572     /**
0573      * Get the number of redirections done on the last request
0574      *
0575      * @return int
0576      */
0577     public function getRedirectionsCount()
0578     {
0579         return $this->redirectCounter;
0580     }
0581 
0582     /**
0583      * Set HTTP authentication parameters
0584      *
0585      * $type should be one of the supported types - see the self::AUTH_*
0586      * constants.
0587      *
0588      * To enable authentication:
0589      * <code>
0590      * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
0591      * </code>
0592      *
0593      * To disable authentication:
0594      * <code>
0595      * $this->setAuth(false);
0596      * </code>
0597      *
0598      * @see http://www.faqs.org/rfcs/rfc2617.html
0599      * @param string|false $user User name or false disable authentication
0600      * @param string $password Password
0601      * @param string $type Authentication type
0602      * @return Zend_Http_Client
0603      * @throws Zend_Http_Client_Exception
0604      */
0605     public function setAuth($user, $password = '', $type = self::AUTH_BASIC)
0606     {
0607         // If we got false or null, disable authentication
0608         if ($user === false || $user === null) {
0609             $this->auth = null;
0610 
0611             // Clear the auth information in the uri instance as well
0612             if ($this->uri instanceof Zend_Uri_Http) {
0613                 $this->getUri()->setUsername('');
0614                 $this->getUri()->setPassword('');
0615             }
0616         // Else, set up authentication
0617         } else {
0618             // Check we got a proper authentication type
0619             if (! defined('self::AUTH_' . strtoupper($type))) {
0620                 /** @see Zend_Http_Client_Exception */
0621                 // require_once 'Zend/Http/Client/Exception.php';
0622                 throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
0623             }
0624 
0625             $this->auth = array(
0626                 'user' => (string) $user,
0627                 'password' => (string) $password,
0628                 'type' => $type
0629             );
0630         }
0631 
0632         return $this;
0633     }
0634 
0635     /**
0636      * Set the HTTP client's cookie jar.
0637      *
0638      * A cookie jar is an object that holds and maintains cookies across HTTP requests
0639      * and responses.
0640      *
0641      * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable
0642      * @return Zend_Http_Client
0643      * @throws Zend_Http_Client_Exception
0644      */
0645     public function setCookieJar($cookiejar = true)
0646     {
0647         Zend_Loader::loadClass('Zend_Http_CookieJar');
0648 
0649         if ($cookiejar instanceof Zend_Http_CookieJar) {
0650             $this->cookiejar = $cookiejar;
0651         } elseif ($cookiejar === true) {
0652             $this->cookiejar = new Zend_Http_CookieJar();
0653         } elseif (! $cookiejar) {
0654             $this->cookiejar = null;
0655         } else {
0656             /** @see Zend_Http_Client_Exception */
0657             // require_once 'Zend/Http/Client/Exception.php';
0658             throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar');
0659         }
0660 
0661         return $this;
0662     }
0663 
0664     /**
0665      * Return the current cookie jar or null if none.
0666      *
0667      * @return Zend_Http_CookieJar|null
0668      */
0669     public function getCookieJar()
0670     {
0671         return $this->cookiejar;
0672     }
0673 
0674     /**
0675      * Add a cookie to the request. If the client has no Cookie Jar, the cookies
0676      * will be added directly to the headers array as "Cookie" headers.
0677      *
0678      * @param Zend_Http_Cookie|string $cookie
0679      * @param string|null $value If "cookie" is a string, this is the cookie value.
0680      * @return Zend_Http_Client
0681      * @throws Zend_Http_Client_Exception
0682      */
0683     public function setCookie($cookie, $value = null)
0684     {
0685         Zend_Loader::loadClass('Zend_Http_Cookie');
0686 
0687         if (is_array($cookie)) {
0688             foreach ($cookie as $c => $v) {
0689                 if (is_string($c)) {
0690                     $this->setCookie($c, $v);
0691                 } else {
0692                     $this->setCookie($v);
0693                 }
0694             }
0695 
0696             return $this;
0697         }
0698 
0699         if ($value !== null && $this->config['encodecookies']) {
0700             $value = urlencode($value);
0701         }
0702 
0703         if (isset($this->cookiejar)) {
0704             if ($cookie instanceof Zend_Http_Cookie) {
0705                 $this->cookiejar->addCookie($cookie);
0706             } elseif (is_string($cookie) && $value !== null) {
0707                 $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}",
0708                                                        $this->uri,
0709                                                        $this->config['encodecookies']);
0710                 $this->cookiejar->addCookie($cookie);
0711             }
0712         } else {
0713             if ($cookie instanceof Zend_Http_Cookie) {
0714                 $name = $cookie->getName();
0715                 $value = $cookie->getValue();
0716                 $cookie = $name;
0717             }
0718 
0719             if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) {
0720                 /** @see Zend_Http_Client_Exception */
0721                 // require_once 'Zend/Http/Client/Exception.php';
0722                 throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})");
0723             }
0724 
0725             $value = addslashes($value);
0726 
0727             if (! isset($this->headers['cookie'])) {
0728                 $this->headers['cookie'] = array('Cookie', '');
0729             }
0730             $this->headers['cookie'][1] .= $cookie . '=' . $value . '; ';
0731         }
0732 
0733         return $this;
0734     }
0735 
0736     /**
0737      * Set a file to upload (using a POST request)
0738      *
0739      * Can be used in two ways:
0740      *
0741      * 1. $data is null (default): $filename is treated as the name if a local file which
0742      *    will be read and sent. Will try to guess the content type using mime_content_type().
0743      * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
0744      *    contents and no file is read from the file system. In this case, you need to
0745      *    manually set the Content-Type ($ctype) or it will default to
0746      *    application/octet-stream.
0747      *
0748      * @param string $filename Name of file to upload, or name to save as
0749      * @param string $formname Name of form element to send as
0750      * @param string $data Data to send (if null, $filename is read and sent)
0751      * @param string $ctype Content type to use (if $data is set and $ctype is
0752      *     null, will be application/octet-stream)
0753      * @return Zend_Http_Client
0754      * @throws Zend_Http_Client_Exception
0755      */
0756     public function setFileUpload($filename, $formname, $data = null, $ctype = null)
0757     {
0758         if ($data === null) {
0759             if (($data = @file_get_contents($filename)) === false) {
0760                 /** @see Zend_Http_Client_Exception */
0761                 // require_once 'Zend/Http/Client/Exception.php';
0762                 throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
0763             }
0764 
0765             if (! $ctype) {
0766                 $ctype = $this->_detectFileMimeType($filename);
0767             }
0768         }
0769 
0770         // Force enctype to multipart/form-data
0771         $this->setEncType(self::ENC_FORMDATA);
0772 
0773         $this->files[] = array(
0774             'formname' => $formname,
0775             'filename' => basename($filename),
0776             'ctype'    => $ctype,
0777             'data'     => $data
0778         );
0779 
0780         $this->body_field_order[$formname] = self::VTYPE_FILE;
0781 
0782         return $this;
0783     }
0784 
0785     /**
0786      * Set the encoding type for POST data
0787      *
0788      * @param string $enctype
0789      * @return Zend_Http_Client
0790      */
0791     public function setEncType($enctype = self::ENC_URLENCODED)
0792     {
0793         $this->enctype = $enctype;
0794 
0795         return $this;
0796     }
0797 
0798     /**
0799      * Set the raw (already encoded) POST data.
0800      *
0801      * This function is here for two reasons:
0802      * 1. For advanced user who would like to set their own data, already encoded
0803      * 2. For backwards compatibilty: If someone uses the old post($data) method.
0804      *    this method will be used to set the encoded data.
0805      *
0806      * $data can also be stream (such as file) from which the data will be read.
0807      *
0808      * @param string|resource $data
0809      * @param string $enctype
0810      * @return Zend_Http_Client
0811      */
0812     public function setRawData($data, $enctype = null)
0813     {
0814         $this->raw_post_data = $data;
0815         $this->setEncType($enctype);
0816         if (is_resource($data)) {
0817             // We've got stream data
0818             $stat = @fstat($data);
0819             if($stat) {
0820                 $this->setHeaders(self::CONTENT_LENGTH, $stat['size']);
0821             }
0822         }
0823         return $this;
0824     }
0825 
0826     /**
0827      * Set the unmask feature for GET parameters as array
0828      *
0829      * Example:
0830      * foo%5B0%5D=a&foo%5B1%5D=b
0831      * becomes
0832      * foo=a&foo=b
0833      *
0834      * This is usefull for some services
0835      *
0836      * @param boolean $status
0837      * @return Zend_Http_Client
0838      */
0839     public function setUnmaskStatus($status = true)
0840     {
0841         $this->_unmaskStatus = (BOOL)$status;
0842         return $this;
0843     }
0844 
0845     /**
0846      * Returns the currently configured unmask status
0847      *
0848      * @return boolean
0849      */
0850     public function getUnmaskStatus()
0851     {
0852         return $this->_unmaskStatus;
0853     }
0854 
0855     /**
0856      * Clear all GET and POST parameters
0857      *
0858      * Should be used to reset the request parameters if the client is
0859      * used for several concurrent requests.
0860      *
0861      * clearAll parameter controls if we clean just parameters or also
0862      * headers and last_*
0863      *
0864      * @param bool $clearAll Should all data be cleared?
0865      * @return Zend_Http_Client
0866      */
0867     public function resetParameters($clearAll = false)
0868     {
0869         // Reset parameter data
0870         $this->paramsGet     = array();
0871         $this->paramsPost    = array();
0872         $this->files         = array();
0873         $this->raw_post_data = null;
0874         $this->enctype       = null;
0875 
0876         if($clearAll) {
0877             $this->headers = array();
0878             $this->last_request = null;
0879             $this->last_response = null;
0880         } else {
0881             // Clear outdated headers
0882             if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) {
0883                 unset($this->headers[strtolower(self::CONTENT_TYPE)]);
0884             }
0885             if (isset($this->headers[strtolower(self::CONTENT_LENGTH)])) {
0886                 unset($this->headers[strtolower(self::CONTENT_LENGTH)]);
0887             }
0888         }
0889 
0890         return $this;
0891     }
0892 
0893     /**
0894      * Get the last HTTP request as string
0895      *
0896      * @return string
0897      */
0898     public function getLastRequest()
0899     {
0900         return $this->last_request;
0901     }
0902 
0903     /**
0904      * Get the last HTTP response received by this client
0905      *
0906      * If $config['storeresponse'] is set to false, or no response was
0907      * stored yet, will return null
0908      *
0909      * @return Zend_Http_Response or null if none
0910      */
0911     public function getLastResponse()
0912     {
0913         return $this->last_response;
0914     }
0915 
0916     /**
0917      * Load the connection adapter
0918      *
0919      * While this method is not called more than one for a client, it is
0920      * seperated from ->request() to preserve logic and readability
0921      *
0922      * @param Zend_Http_Client_Adapter_Interface|string $adapter
0923      * @return null
0924      * @throws Zend_Http_Client_Exception
0925      */
0926     public function setAdapter($adapter)
0927     {
0928         if (is_string($adapter)) {
0929             try {
0930                 Zend_Loader::loadClass($adapter);
0931             } catch (Zend_Exception $e) {
0932                 /** @see Zend_Http_Client_Exception */
0933                 // require_once 'Zend/Http/Client/Exception.php';
0934                 throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}", 0, $e);
0935             }
0936 
0937             $adapter = new $adapter;
0938         }
0939 
0940         if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) {
0941             /** @see Zend_Http_Client_Exception */
0942             // require_once 'Zend/Http/Client/Exception.php';
0943             throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
0944         }
0945 
0946         $this->adapter = $adapter;
0947         $config = $this->config;
0948         unset($config['adapter']);
0949         $this->adapter->setConfig($config);
0950     }
0951 
0952     /**
0953      * Load the connection adapter
0954      *
0955      * @return Zend_Http_Client_Adapter_Interface $adapter
0956      */
0957     public function getAdapter()
0958     {
0959         if (null === $this->adapter) {
0960             $this->setAdapter($this->config['adapter']);
0961         }
0962 
0963         return $this->adapter;
0964     }
0965 
0966     /**
0967      * Set streaming for received data
0968      *
0969      * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming
0970      * @return Zend_Http_Client
0971      */
0972     public function setStream($streamfile = true)
0973     {
0974         $this->setConfig(array("output_stream" => $streamfile));
0975         return $this;
0976     }
0977 
0978     /**
0979      * Get status of streaming for received data
0980      * @return boolean|string
0981      */
0982     public function getStream()
0983     {
0984         return $this->config["output_stream"];
0985     }
0986 
0987     /**
0988      * Create temporary stream
0989      *
0990      * @return resource
0991      */
0992     protected function _openTempStream()
0993     {
0994         $this->_stream_name = $this->config['output_stream'];
0995         if(!is_string($this->_stream_name)) {
0996             // If name is not given, create temp name
0997             $this->_stream_name = tempnam(isset($this->config['stream_tmp_dir'])?$this->config['stream_tmp_dir']:sys_get_temp_dir(),
0998                  'Zend_Http_Client');
0999         }
1000 
1001         if (false === ($fp = @fopen($this->_stream_name, "w+b"))) {
1002                 if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
1003                     $this->adapter->close();
1004                 }
1005                 // require_once 'Zend/Http/Client/Exception.php';
1006                 throw new Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}");
1007         }
1008 
1009         return $fp;
1010     }
1011 
1012     /**
1013      * Send the HTTP request and return an HTTP response object
1014      *
1015      * @param string $method
1016      * @return Zend_Http_Response
1017      * @throws Zend_Http_Client_Exception
1018      */
1019     public function request($method = null)
1020     {
1021         if (! $this->uri instanceof Zend_Uri_Http) {
1022             /** @see Zend_Http_Client_Exception */
1023             // require_once 'Zend/Http/Client/Exception.php';
1024             throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
1025         }
1026 
1027         if ($method) {
1028             $this->setMethod($method);
1029         }
1030         $this->redirectCounter = 0;
1031         $response = null;
1032 
1033         // Make sure the adapter is loaded
1034         if ($this->adapter == null) {
1035             $this->setAdapter($this->config['adapter']);
1036         }
1037 
1038         // Send the first request. If redirected, continue.
1039         do {
1040             // Clone the URI and add the additional GET parameters to it
1041             $uri = clone $this->uri;
1042             if (! empty($this->paramsGet)) {
1043                 $query = $uri->getQuery();
1044                    if (! empty($query)) {
1045                        $query .= '&';
1046                    }
1047                 $query .= http_build_query($this->paramsGet, null, '&');
1048                 if ($this->config['rfc3986_strict']) {
1049                     $query = str_replace('+', '%20', $query);
1050                 }
1051 
1052                 // @see ZF-11671 to unmask for some services to foo=val1&foo=val2
1053                 if ($this->getUnmaskStatus()) {
1054                     if ($this->_queryBracketsEscaped) {
1055                         $query = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $query);
1056                     } else {
1057                         $query = preg_replace('/\\[(?:[0-9]|[1-9][0-9]+)\\]=/', '=', $query);
1058                     }
1059                 }
1060 
1061                 $uri->setQuery($query);
1062             }
1063 
1064             $body = $this->_prepareBody();
1065             $headers = $this->_prepareHeaders();
1066 
1067             // check that adapter supports streaming before using it
1068             if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) {
1069                 /** @see Zend_Http_Client_Exception */
1070                 // require_once 'Zend/Http/Client/Exception.php';
1071                 throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1072             }
1073 
1074             // Open the connection, send the request and read the response
1075             $this->adapter->connect($uri->getHost(), $uri->getPort(),
1076                 ($uri->getScheme() == 'https' ? true : false));
1077 
1078             if($this->config['output_stream']) {
1079                 if($this->adapter instanceof Zend_Http_Client_Adapter_Stream) {
1080                     $stream = $this->_openTempStream();
1081                     $this->adapter->setOutputStream($stream);
1082                 } else {
1083                     /** @see Zend_Http_Client_Exception */
1084                     // require_once 'Zend/Http/Client/Exception.php';
1085                     throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1086                 }
1087             }
1088 
1089             $this->last_request = $this->adapter->write($this->method,
1090                 $uri, $this->config['httpversion'], $headers, $body);
1091 
1092             $response = $this->adapter->read();
1093             if (! $response) {
1094                 /** @see Zend_Http_Client_Exception */
1095                 // require_once 'Zend/Http/Client/Exception.php';
1096                 throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
1097             }
1098 
1099             if($this->config['output_stream']) {
1100                 $streamMetaData = stream_get_meta_data($stream);
1101                 if ($streamMetaData['seekable']) {
1102                     rewind($stream);
1103                 }
1104                 // cleanup the adapter
1105                 $this->adapter->setOutputStream(null);
1106                 $response = Zend_Http_Response_Stream::fromStream($response, $stream);
1107                 $response->setStreamName($this->_stream_name);
1108                 if(!is_string($this->config['output_stream'])) {
1109                     // we used temp name, will need to clean up
1110                     $response->setCleanup(true);
1111                 }
1112             } else {
1113                 $response = Zend_Http_Response::fromString($response);
1114             }
1115 
1116             if ($this->config['storeresponse']) {
1117                 $this->last_response = $response;
1118             }
1119 
1120             // Load cookies into cookie jar
1121             if (isset($this->cookiejar)) {
1122                 $this->cookiejar->addCookiesFromResponse($response, $uri, $this->config['encodecookies']);
1123             }
1124 
1125             // If we got redirected, look for the Location header
1126             if ($response->isRedirect() && ($location = $response->getHeader('location'))) {
1127 
1128                 // Avoid problems with buggy servers that add whitespace at the
1129                 // end of some headers (See ZF-11283)
1130                 $location = trim($location);
1131 
1132                 // Check whether we send the exact same request again, or drop the parameters
1133                 // and send a GET request
1134                 if ($response->getStatus() == 303 ||
1135                    ((! $this->config['strictredirects']) && ($response->getStatus() == 302 ||
1136                        $response->getStatus() == 301))) {
1137 
1138                     $this->resetParameters();
1139                     $this->setMethod(self::GET);
1140                 }
1141 
1142                 // If we got a well formed absolute URI
1143                 if (($scheme = substr($location, 0, 6)) && ($scheme == 'http:/' || $scheme == 'https:')) {
1144                     $this->setHeaders('host', null);
1145                     $this->setUri($location);
1146 
1147                 } else {
1148 
1149                     // Split into path and query and set the query
1150                     if (strpos($location, '?') !== false) {
1151                         list($location, $query) = explode('?', $location, 2);
1152                     } else {
1153                         $query = '';
1154                     }
1155                     $this->uri->setQuery($query);
1156 
1157                     // Else, if we got just an absolute path, set it
1158                     if(strpos($location, '/') === 0) {
1159                         $this->uri->setPath($location);
1160 
1161                         // Else, assume we have a relative path
1162                     } else {
1163                         // Get the current path directory, removing any trailing slashes
1164                         $path = $this->uri->getPath();
1165                         $path = rtrim(substr($path, 0, strrpos($path, '/')), "/");
1166                         $this->uri->setPath($path . '/' . $location);
1167                     }
1168                 }
1169                 ++$this->redirectCounter;
1170 
1171             } else {
1172                 // If we didn't get any location, stop redirecting
1173                 break;
1174             }
1175 
1176         } while ($this->redirectCounter < $this->config['maxredirects']);
1177 
1178         return $response;
1179     }
1180 
1181     /**
1182      * Prepare the request headers
1183      *
1184      * @return array
1185      */
1186     protected function _prepareHeaders()
1187     {
1188         $headers = array();
1189 
1190         // Set the host header
1191         if (! isset($this->headers['host'])) {
1192             $host = $this->uri->getHost();
1193 
1194             // If the port is not default, add it
1195             if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
1196                   ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
1197                 $host .= ':' . $this->uri->getPort();
1198             }
1199 
1200             $headers[] = "Host: {$host}";
1201         }
1202 
1203         // Set the connection header
1204         if (! isset($this->headers['connection'])) {
1205             if (! $this->config['keepalive']) {
1206                 $headers[] = "Connection: close";
1207             }
1208         }
1209 
1210         // Set the Accept-encoding header if not set - depending on whether
1211         // zlib is available or not.
1212         if (! isset($this->headers['accept-encoding'])) {
1213             if (function_exists('gzinflate')) {
1214                 $headers[] = 'Accept-encoding: gzip, deflate';
1215             } else {
1216                 $headers[] = 'Accept-encoding: identity';
1217             }
1218         }
1219 
1220         // Set the Content-Type header
1221         if (($this->method == self::POST || $this->method == self::PUT) &&
1222            (! isset($this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) {
1223 
1224             $headers[] = self::CONTENT_TYPE . ': ' . $this->enctype;
1225         }
1226 
1227         // Set the user agent header
1228         if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
1229             $headers[] = "User-Agent: {$this->config['useragent']}";
1230         }
1231 
1232         // Set HTTP authentication if needed
1233         if (is_array($this->auth)) {
1234             $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
1235             $headers[] = "Authorization: {$auth}";
1236         }
1237 
1238         // Load cookies from cookie jar
1239         if (isset($this->cookiejar)) {
1240             $cookstr = $this->cookiejar->getMatchingCookies($this->uri,
1241                 true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT);
1242 
1243             if ($cookstr) {
1244                 $headers[] = "Cookie: {$cookstr}";
1245             }
1246         }
1247 
1248         // Add all other user defined headers
1249         foreach ($this->headers as $header) {
1250             list($name, $value) = $header;
1251             if (is_array($value)) {
1252                 $value = implode(', ', $value);
1253             }
1254 
1255             $headers[] = "$name: $value";
1256         }
1257 
1258         return $headers;
1259     }
1260 
1261     /**
1262      * Prepare the request body (for POST and PUT requests)
1263      *
1264      * @return string
1265      * @throws Zend_Http_Client_Exception
1266      */
1267     protected function _prepareBody()
1268     {
1269         // According to RFC2616, a TRACE request should not have a body.
1270         if ($this->method == self::TRACE) {
1271             return '';
1272         }
1273 
1274         if (isset($this->raw_post_data) && is_resource($this->raw_post_data)) {
1275             return $this->raw_post_data;
1276         }
1277         // If mbstring overloads substr and strlen functions, we have to
1278         // override it's internal encoding
1279         if (function_exists('mb_internal_encoding') &&
1280            ((int) ini_get('mbstring.func_overload')) & 2) {
1281 
1282             $mbIntEnc = mb_internal_encoding();
1283             mb_internal_encoding('ASCII');
1284         }
1285 
1286         // If we have raw_post_data set, just use it as the body.
1287         if (isset($this->raw_post_data)) {
1288             $this->setHeaders(self::CONTENT_LENGTH, strlen($this->raw_post_data));
1289             if (isset($mbIntEnc)) {
1290                 mb_internal_encoding($mbIntEnc);
1291             }
1292 
1293             return $this->raw_post_data;
1294         }
1295 
1296         $body = '';
1297 
1298         // If we have files to upload, force enctype to multipart/form-data
1299         if (count ($this->files) > 0) {
1300             $this->setEncType(self::ENC_FORMDATA);
1301         }
1302 
1303         // If we have POST parameters or files, encode and add them to the body
1304         if (count($this->paramsPost) > 0 || count($this->files) > 0) {
1305             switch($this->enctype) {
1306                 case self::ENC_FORMDATA:
1307                     // Encode body as multipart/form-data
1308                     $boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
1309                     $this->setHeaders(self::CONTENT_TYPE, self::ENC_FORMDATA . "; boundary={$boundary}");
1310 
1311                     // Encode all files and POST vars in the order they were given
1312                     foreach ($this->body_field_order as $fieldName=>$fieldType) {
1313                         switch ($fieldType) {
1314                             case self::VTYPE_FILE:
1315                                 foreach ($this->files as $file) {
1316                                     if ($file['formname']===$fieldName) {
1317                                         $fhead = array(self::CONTENT_TYPE => $file['ctype']);
1318                                         $body .= self::encodeFormData($boundary, $file['formname'], $file['data'], $file['filename'], $fhead);
1319                                     }
1320                                 }
1321                                 break;
1322                             case self::VTYPE_SCALAR:
1323                                 if (isset($this->paramsPost[$fieldName])) {
1324                                     if (is_array($this->paramsPost[$fieldName])) {
1325                                         $flattened = self::_flattenParametersArray($this->paramsPost[$fieldName], $fieldName);
1326                                         foreach ($flattened as $pp) {
1327                                             $body .= self::encodeFormData($boundary, $pp[0], $pp[1]);
1328                                         }
1329                                     } else {
1330                                         $body .= self::encodeFormData($boundary, $fieldName, $this->paramsPost[$fieldName]);
1331                                     }
1332                                 }
1333                                 break;
1334                         }
1335                     }
1336 
1337                     $body .= "--{$boundary}--\r\n";
1338                     break;
1339 
1340                 case self::ENC_URLENCODED:
1341                     // Encode body as application/x-www-form-urlencoded
1342                     $this->setHeaders(self::CONTENT_TYPE, self::ENC_URLENCODED);
1343                     $body = http_build_query($this->paramsPost, '', '&');
1344                     break;
1345 
1346                 default:
1347                     if (isset($mbIntEnc)) {
1348                         mb_internal_encoding($mbIntEnc);
1349                     }
1350 
1351                     /** @see Zend_Http_Client_Exception */
1352                     // require_once 'Zend/Http/Client/Exception.php';
1353                     throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." .
1354                         " Please use Zend_Http_Client::setRawData to send this kind of content.");
1355                     break;
1356             }
1357         }
1358 
1359         // Set the Content-Length if we have a body or if request is POST/PUT
1360         if ($body || $this->method == self::POST || $this->method == self::PUT) {
1361             $this->setHeaders(self::CONTENT_LENGTH, strlen($body));
1362         }
1363 
1364         if (isset($mbIntEnc)) {
1365             mb_internal_encoding($mbIntEnc);
1366         }
1367 
1368         return $body;
1369     }
1370 
1371     /**
1372      * Helper method that gets a possibly multi-level parameters array (get or
1373      * post) and flattens it.
1374      *
1375      * The method returns an array of (key, value) pairs (because keys are not
1376      * necessarily unique. If one of the parameters in as array, it will also
1377      * add a [] suffix to the key.
1378      *
1379      * This method is deprecated since Zend Framework 1.9 in favour of
1380      * self::_flattenParametersArray() and will be dropped in 2.0
1381      *
1382      * @deprecated since 1.9
1383      *
1384      * @param  array $parray    The parameters array
1385      * @param  bool  $urlencode Whether to urlencode the name and value
1386      * @return array
1387      */
1388     protected function _getParametersRecursive($parray, $urlencode = false)
1389     {
1390         // Issue a deprecated notice
1391         trigger_error("The " .  __METHOD__ . " method is deprecated and will be dropped in 2.0.",
1392             E_USER_NOTICE);
1393 
1394         if (! is_array($parray)) {
1395             return $parray;
1396         }
1397         $parameters = array();
1398 
1399         foreach ($parray as $name => $value) {
1400             if ($urlencode) {
1401                 $name = urlencode($name);
1402             }
1403 
1404             // If $value is an array, iterate over it
1405             if (is_array($value)) {
1406                 $name .= ($urlencode ? '%5B%5D' : '[]');
1407                 foreach ($value as $subval) {
1408                     if ($urlencode) {
1409                         $subval = urlencode($subval);
1410                     }
1411                     $parameters[] = array($name, $subval);
1412                 }
1413             } else {
1414                 if ($urlencode) {
1415                     $value = urlencode($value);
1416                 }
1417                 $parameters[] = array($name, $value);
1418             }
1419         }
1420 
1421         return $parameters;
1422     }
1423 
1424     /**
1425      * Attempt to detect the MIME type of a file using available extensions
1426      *
1427      * This method will try to detect the MIME type of a file. If the fileinfo
1428      * extension is available, it will be used. If not, the mime_magic
1429      * extension which is deprected but is still available in many PHP setups
1430      * will be tried.
1431      *
1432      * If neither extension is available, the default application/octet-stream
1433      * MIME type will be returned
1434      *
1435      * @param  string $file File path
1436      * @return string       MIME type
1437      */
1438     protected function _detectFileMimeType($file)
1439     {
1440         $type = null;
1441 
1442         // First try with fileinfo functions
1443         if (function_exists('finfo_open')) {
1444             if (self::$_fileInfoDb === null) {
1445                 self::$_fileInfoDb = @finfo_open(FILEINFO_MIME);
1446             }
1447 
1448             if (self::$_fileInfoDb) {
1449                 $type = finfo_file(self::$_fileInfoDb, $file);
1450             }
1451 
1452         } elseif (function_exists('mime_content_type')) {
1453             $type = mime_content_type($file);
1454         }
1455 
1456         // Fallback to the default application/octet-stream
1457         if (! $type) {
1458             $type = 'application/octet-stream';
1459         }
1460 
1461         return $type;
1462     }
1463 
1464     /**
1465      * Encode data to a multipart/form-data part suitable for a POST request.
1466      *
1467      * @param string $boundary
1468      * @param string $name
1469      * @param mixed $value
1470      * @param string $filename
1471      * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" => "binary")
1472      * @return string
1473      */
1474     public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array())
1475     {
1476         $ret = "--{$boundary}\r\n" .
1477             'Content-Disposition: form-data; name="' . $name .'"';
1478 
1479         if ($filename) {
1480             $ret .= '; filename="' . $filename . '"';
1481         }
1482         $ret .= "\r\n";
1483 
1484         foreach ($headers as $hname => $hvalue) {
1485             $ret .= "{$hname}: {$hvalue}\r\n";
1486         }
1487         $ret .= "\r\n";
1488 
1489         $ret .= "{$value}\r\n";
1490 
1491         return $ret;
1492     }
1493 
1494     /**
1495      * Create a HTTP authentication "Authorization:" header according to the
1496      * specified user, password and authentication method.
1497      *
1498      * @see http://www.faqs.org/rfcs/rfc2617.html
1499      * @param string $user
1500      * @param string $password
1501      * @param string $type
1502      * @return string
1503      * @throws Zend_Http_Client_Exception
1504      */
1505     public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
1506     {
1507         $authHeader = null;
1508 
1509         switch ($type) {
1510             case self::AUTH_BASIC:
1511                 // In basic authentication, the user name cannot contain ":"
1512                 if (strpos($user, ':') !== false) {
1513                     /** @see Zend_Http_Client_Exception */
1514                     // require_once 'Zend/Http/Client/Exception.php';
1515                     throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication");
1516                 }
1517 
1518                 $authHeader = 'Basic ' . base64_encode($user . ':' . $password);
1519                 break;
1520 
1521             //case self::AUTH_DIGEST:
1522                 /**
1523                  * @todo Implement digest authentication
1524                  */
1525             //    break;
1526 
1527             default:
1528                 /** @see Zend_Http_Client_Exception */
1529                 // require_once 'Zend/Http/Client/Exception.php';
1530                 throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
1531         }
1532 
1533         return $authHeader;
1534     }
1535 
1536     /**
1537      * Convert an array of parameters into a flat array of (key, value) pairs
1538      *
1539      * Will flatten a potentially multi-dimentional array of parameters (such
1540      * as POST parameters) into a flat array of (key, value) paris. In case
1541      * of multi-dimentional arrays, square brackets ([]) will be added to the
1542      * key to indicate an array.
1543      *
1544      * @since  1.9
1545      *
1546      * @param  array  $parray
1547      * @param  string $prefix
1548      * @return array
1549      */
1550     protected static function _flattenParametersArray($parray, $prefix = null)
1551     {
1552         if (! is_array($parray)) {
1553             return $parray;
1554         }
1555 
1556         $parameters = array();
1557 
1558         foreach($parray as $name => $value) {
1559 
1560             // Calculate array key
1561             if ($prefix) {
1562                 if (is_int($name)) {
1563                     $key = $prefix . '[]';
1564                 } else {
1565                     $key = $prefix . "[$name]";
1566                 }
1567             } else {
1568                 $key = $name;
1569             }
1570 
1571             if (is_array($value)) {
1572                 $parameters = array_merge($parameters, self::_flattenParametersArray($value, $key));
1573 
1574             } else {
1575                 $parameters[] = array($key, $value);
1576             }
1577         }
1578 
1579         return $parameters;
1580     }
1581 
1582     /**
1583      * Ensure a header value is valid per RFC 7230.
1584      *
1585      * @see http://tools.ietf.org/html/rfc7230#section-3.2
1586      * @param string|object|array $value
1587      * @param bool $recurse
1588      */
1589     protected function _validateHeaderValue($value, $recurse = true)
1590     {
1591         if (is_array($value) && $recurse) {
1592             foreach ($value as $v) {
1593                 $this->_validateHeaderValue($v, false);
1594             }
1595             return;
1596         }
1597 
1598         // Cast integers and floats to strings for purposes of header representation.
1599         if (is_int($value) || is_float($value)) {
1600             $value = (string) $value;
1601         }
1602 
1603         if (! is_string($value) && (! is_object($value) || ! method_exists($value, '__toString'))) {
1604             // require_once 'Zend/Http/Exception.php';
1605             throw new Zend_Http_Exception('Invalid header value detected');
1606         }
1607 
1608         Zend_Http_Header_HeaderValue::assertValid($value);
1609     }
1610 }