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

/packages/zendframework/http.php

[return to app]
1 <?php
2
/**
3  * Zend Framework Http class isolated from ZF dependencies to work in Vork
4  *
5  * LICENSE
6  *
7  * This source file is subject to the new BSD license that is bundled
8  * with this package in the file LICENSE.txt.
9  * It is also available through the world-wide-web at this URL:
10  * http://framework.zend.com/license/new-bsd
11  * If you did not receive a copy of the license and are unable to
12  * obtain it through the world-wide-web, please send an email
13  * to license@zend.com so we can send you a copy immediately.
14  *
15  * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
16  * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
17  * responses.
18  *
19  * The class contains an array of Zend_Http_Cookie objects. Cookies can be added to the jar
20  * automatically from a request or manually. Then, the jar can find and return the cookies
21  * needed for a specific HTTP request.
22  *
23  * A special parameter can be passed to all methods of this class that return cookies: Cookies
24  * can be returned either in their native form (as Zend_Http_Cookie objects) or as strings -
25  * the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
26  * You can also choose, when returning more than one cookie, whether to get an array of strings
27  * (by passing Zend_Http_CookieJar::COOKIE_STRING_ARRAY) or one unified string for all cookies
28  * (by passing Zend_Http_CookieJar::COOKIE_STRING_CONCAT).
29  *
30  * @link       http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
31  * @category   Zend
32  * @package    Zend_Http
33  * @subpackage CookieJar
34  * @version    $Id: CookieJar.php 20096 2010-01-06 02:05:09Z bkarwin $
35  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
36  * @license    http://framework.zend.com/license/new-bsd     New BSD License
37  */
38
class Zend_Http_CookieJar implements CountableIteratorAggregate {
39     
/**
40      * Return cookie(s) as a Zend_Http_Cookie object
41      *
42      */
43     
const COOKIE_OBJECT 0;
44
45     
/**
46      * Return cookie(s) as a string (suitable for sending in an HTTP request)
47      *
48      */
49     
const COOKIE_STRING_ARRAY 1;
50
51     
/**
52      * Return all cookies as one long string (suitable for sending in an HTTP request)
53      *
54      */
55     
const COOKIE_STRING_CONCAT 2;
56
57     
/**
58      * Array storing cookies
59      *
60      * Cookies are stored according to domain and path:
61      * $cookies
62      *  + www.mydomain.com
63      *    + /
64      *      - cookie1
65      *      - cookie2
66      *    + /somepath
67      *      - othercookie
68      *  + www.otherdomain.net
69      *    + /
70      *      - alsocookie
71      *
72      * @var array
73      */
74     
protected $cookies = array();
75
76     
/**
77      * The Zend_Http_Cookie array
78      *
79      * @var array
80      */
81     
protected $_rawCookies = array();
82
83     
/**
84      * Construct a new CookieJar object
85      *
86      */
87     
public function __construct() { }
88
89     
/**
90      * Add a cookie to the jar. Cookie should be passed either as a Zend_Http_Cookie object
91      * or as a string - in which case an object is created from the string.
92      *
93      * @param Zend_Http_Cookie|string $cookie
94      * @param Zend_Uri_Http|string    $ref_uri Optional reference URI (for domain, path, secure)
95      */
96     
public function addCookie($cookie$ref_uri null) {
97         if (
is_string($cookie)) {
98             
$cookie Zend_Http_Cookie::fromString($cookie$ref_uri);
99         }
100
101         if (
$cookie instanceof Zend_Http_Cookie) {
102             
$domain $cookie->getDomain();
103             
$path $cookie->getPath();
104             if (! isset(
$this->cookies[$domain])) $this->cookies[$domain] = array();
105             if (! isset(
$this->cookies[$domain][$path])) $this->cookies[$domain][$path] = array();
106             
$this->cookies[$domain][$path][$cookie->getName()] = $cookie;
107             
$this->_rawCookies[] = $cookie;
108         } else {
109             if (!
class_exists('Zend_Exception')) require 'exception.php';
110             throw new 
Zend_Http_Exception('Supplient argument is not a valid cookie string or object');
111         }
112     }
113
114     
/**
115      * Parse an HTTP response, adding all the cookies set in that response
116      * to the cookie jar.
117      *
118      * @param Zend_Http_Response $response
119      * @param Zend_Uri_Http|string $ref_uri Requested URI
120      */
121     
public function addCookiesFromResponse($response$ref_uri) {
122         if (! 
$response instanceof Zend_Http_Response) {
123             if (!
class_exists('Zend_Exception')) require 'exception.php';
124             throw new 
Zend_Http_Exception('$response is expected to be a Response object, ' .
125                 
gettype($response) . ' was passed');
126         }
127
128         
$cookie_hdrs $response->getHeader('Set-Cookie');
129
130         if (
is_array($cookie_hdrs)) {
131             foreach (
$cookie_hdrs as $cookie) {
132                 
$this->addCookie($cookie$ref_uri);
133             }
134         } elseif (
is_string($cookie_hdrs)) {
135             
$this->addCookie($cookie_hdrs$ref_uri);
136         }
137     }
138
139     
/**
140      * Get all cookies in the cookie jar as an array
141      *
142      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
143      * @return array|string
144      */
145     
public function getAllCookies($ret_as self::COOKIE_OBJECT) {
146         
$cookies $this->_flattenCookiesArray($this->cookies$ret_as);
147         return 
$cookies;
148     }
149
150     
/**
151      * Return an array of all cookies matching a specific request according to the request URI,
152      * whether session cookies should be sent or not, and the time to consider as "now" when
153      * checking cookie expiry time.
154      *
155      * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
156      * @param boolean $matchSessionCookies Whether to send session cookies
157      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
158      * @param int $now Override the current time when checking for expiry time
159      * @return array|string
160      */
161     
public function getMatchingCookies($uri$matchSessionCookies true,
162         
$ret_as self::COOKIE_OBJECT$now null) {
163         if (
is_string($uri)) $uri Zend_Uri::factory($uri);
164         if (! 
$uri instanceof Zend_Uri_Http) {
165             if (!
class_exists('Zend_Exception')) require 'exception.php';
166             throw new 
Zend_Http_Exception("Invalid URI string or object passed");
167         }
168
169         
// First, reduce the array of cookies to only those matching domain and path
170         
$cookies $this->_matchDomain($uri->getHost());
171         
$cookies $this->_matchPath($cookies$uri->getPath());
172         
$cookies $this->_flattenCookiesArray($cookiesself::COOKIE_OBJECT);
173
174         
// Next, run Cookie->match on all cookies to check secure, time and session mathcing
175         
$ret = array();
176         foreach (
$cookies as $cookie)
177             if (
$cookie->match($uri$matchSessionCookies$now))
178                 
$ret[] = $cookie;
179
180         
// Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
181         
$ret $this->_flattenCookiesArray($ret$ret_as);
182
183         return 
$ret;
184     }
185
186     
/**
187      * Get a specific cookie according to a URI and name
188      *
189      * @param Zend_Uri_Http|string $uri The uri (domain and path) to match
190      * @param string $cookie_name The cookie's name
191      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
192      * @return Zend_Http_Cookie|string
193      */
194     
public function getCookie($uri$cookie_name$ret_as self::COOKIE_OBJECT) {
195         if (
is_string($uri)) {
196             
$uri Zend_Uri::factory($uri);
197         }
198
199         if (! 
$uri instanceof Zend_Uri_Http) {
200             if (!
class_exists('Zend_Exception')) require 'exception.php';
201             throw new 
Zend_Http_Exception('Invalid URI specified');
202         }
203
204         
// Get correct cookie path
205         
$path $uri->getPath();
206         
$path substr($path0strrpos($path'/'));
207         if (! 
$path$path '/';
208
209         if (isset(
$this->cookies[$uri->getHost()][$path][$cookie_name])) {
210             
$cookie $this->cookies[$uri->getHost()][$path][$cookie_name];
211
212             switch (
$ret_as) {
213                 case 
self::COOKIE_OBJECT:
214                     return 
$cookie;
215                     break;
216
217                 case 
self::COOKIE_STRING_ARRAY:
218                 case 
self::COOKIE_STRING_CONCAT:
219                     return 
$cookie->__toString();
220                     break;
221
222                 default:
223                     if (!
class_exists('Zend_Exception')) require 'exception.php';
224                     throw new 
Zend_Http_Exception("Invalid value passed for \$ret_as: {$ret_as}");
225                     break;
226             }
227         } else {
228             return 
false;
229         }
230     }
231
232     
/**
233      * Helper function to recursivly flatten an array. Shoud be used when exporting the
234      * cookies array (or parts of it)
235      *
236      * @param Zend_Http_Cookie|array $ptr
237      * @param int $ret_as What value to return
238      * @return array|string
239      */
240     
protected function _flattenCookiesArray($ptr$ret_as self::COOKIE_OBJECT) {
241         if (
is_array($ptr)) {
242             
$ret = ($ret_as == self::COOKIE_STRING_CONCAT '' : array());
243             foreach (
$ptr as $item) {
244                 if (
$ret_as == self::COOKIE_STRING_CONCAT) {
245                     
$ret .= $this->_flattenCookiesArray($item$ret_as);
246                 } else {
247                     
$ret array_merge($ret$this->_flattenCookiesArray($item$ret_as));
248                 }
249             }
250             return 
$ret;
251         } elseif (
$ptr instanceof Zend_Http_Cookie) {
252             switch (
$ret_as) {
253                 case 
self::COOKIE_STRING_ARRAY:
254                     return array(
$ptr->__toString());
255                     break;
256
257                 case 
self::COOKIE_STRING_CONCAT:
258                     return 
$ptr->__toString();
259                     break;
260
261                 case 
self::COOKIE_OBJECT:
262                 default:
263                     return array(
$ptr);
264                     break;
265             }
266         }
267
268         return 
null;
269     }
270
271     
/**
272      * Return a subset of the cookies array matching a specific domain
273      *
274      * @param string $domain
275      * @return array
276      */
277     
protected function _matchDomain($domain) {
278         
$ret = array();
279
280         foreach (
array_keys($this->cookies) as $cdom) {
281             if (
Zend_Http_Cookie::matchCookieDomain($cdom$domain)) {
282                 
$ret[$cdom] = $this->cookies[$cdom];
283             }
284         }
285
286         return 
$ret;
287     }
288
289     
/**
290      * Return a subset of a domain-matching cookies that also match a specified path
291      *
292      * @param array $dom_array
293      * @param string $path
294      * @return array
295      */
296     
protected function _matchPath($domains$path) {
297         
$ret = array();
298
299         foreach (
$domains as $dom => $paths_array) {
300             foreach (
array_keys($paths_array) as $cpath) {
301                 if (
Zend_Http_Cookie::matchCookiePath($cpath$path)) {
302                     if (! isset(
$ret[$dom])) {
303                         
$ret[$dom] = array();
304                     }
305
306                     
$ret[$dom][$cpath] = $paths_array[$cpath];
307                 }
308             }
309         }
310
311         return 
$ret;
312     }
313
314     
/**
315      * Create a new CookieJar object and automatically load into it all the
316      * cookies set in an Http_Response object. If $uri is set, it will be
317      * considered as the requested URI for setting default domain and path
318      * of the cookie.
319      *
320      * @param Zend_Http_Response $response HTTP Response object
321      * @param Zend_Uri_Http|string $uri The requested URI
322      * @return Zend_Http_CookieJar
323      * @todo Add the $uri functionality.
324      */
325     
public static function fromResponse(Zend_Http_Response $response$ref_uri) {
326         
$jar = new self();
327         
$jar->addCookiesFromResponse($response$ref_uri);
328         return 
$jar;
329     }
330
331     
/**
332      * Required by Countable interface
333      *
334      * @return int
335      */
336     
public function count() {
337         return 
count($this->_rawCookies);
338     }
339
340     
/**
341      * Required by IteratorAggregate interface
342      *
343      * @return ArrayIterator
344      */
345     
public function getIterator() {
346         return new 
ArrayIterator($this->_rawCookies);
347     }
348
349     
/**
350      * Tells if the jar is empty of any cookie
351      *
352      * @return bool
353      */
354     
public function isEmpty() {
355         return 
count($this) == 0;
356     }
357
358     
/**
359      * Empties the cookieJar of any cookie
360      *
361      * @return Zend_Http_CookieJar
362      */
363     
public function reset() {
364         
$this->cookies $this->_rawCookies = array();
365         return 
$this;
366     }
367 }
368
369
/**
370  * A testing-purposes adapter.
371  *
372  * Should be used to test all components that rely on Zend_Http_Client,
373  * without actually performing an HTTP request. You should instantiate this
374  * object manually, and then set it as the client's adapter. Then, you can
375  * set the expected response using the setResponse() method.
376  *
377  * @category   Zend
378  * @package    Zend_Http
379  * @subpackage Client_Adapter
380  * @version    $Id: Test.php 20096 2010-01-06 02:05:09Z bkarwin $
381  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
382  * @license    http://framework.zend.com/license/new-bsd     New BSD License
383  */
384
class Zend_Http_Client_Adapter_Test implements Zend_Http_Client_Adapter_Interface {
385     
/**
386      * Parameters array
387      *
388      * @var array
389      */
390     
protected $config = array();
391
392     
/**
393      * Buffer of responses to be returned by the read() method.  Can be
394      * set using setResponse() and addResponse().
395      *
396      * @var array
397      */
398     
protected $responses = array("HTTP/1.1 400 Bad Request\r\n\r\n");
399
400     
/**
401      * Current position in the response buffer
402      *
403      * @var integer
404      */
405     
protected $responseIndex 0;
406
407     
/**
408      * Wether or not the next request will fail with an exception
409      *
410      * @var boolean
411      */
412     
protected $_nextRequestWillFail false;
413
414     
/**
415      * Adapter constructor, currently empty. Config is set using setConfig()
416      *
417      */
418     
public function __construct() { }
419
420     
/**
421      * Set the nextRequestWillFail flag
422      *
423      * @param boolean $flag
424      * @return Zend_Http_Client_Adapter_Test
425      */
426     
public function setNextRequestWillFail($flag) {
427         
$this->_nextRequestWillFail = (bool) $flag;
428
429         return 
$this;
430     }
431
432     
/**
433      * Set the configuration array for the adapter
434      *
435      * @param Zend_Config | array $config
436      */
437     
public function setConfig($config = array()) {
438         if (
$config instanceof Zend_Config) {
439             
$config $config->toArray();
440
441         } elseif (! 
is_array($config)) {
442             if (!
class_exists('Zend_Exception')) require 'exception.php';
443             throw new 
Zend_Http_Client_Adapter_Exception(
444                 
'Array or Zend_Config object expected, got ' gettype($config)
445             );
446         }
447
448         foreach (
$config as $k => $v) {
449             
$this->config[strtolower($k)] = $v;
450         }
451     }
452
453     
/**
454      * Connect to the remote server
455      *
456      * @param string  $host
457      * @param int     $port
458      * @param boolean $secure
459      * @param int     $timeout
460      * @throws Zend_Http_Client_Adapter_Exception
461      */
462     
public function connect($host$port 80$secure false) {
463         if (
$this->_nextRequestWillFail) {
464             
$this->_nextRequestWillFail false;
465             if (!
class_exists('Zend_Exception')) require 'exception.php';
466             throw new 
Zend_Http_Client_Adapter_Exception('Request failed');
467         }
468     }
469
470     
/**
471      * Send request to the remote server
472      *
473      * @param string        $method
474      * @param Zend_Uri_Http $uri
475      * @param string        $http_ver
476      * @param array         $headers
477      * @param string        $body
478      * @return string Request as string
479      */
480     
public function write($method$uri$http_ver '1.1'$headers = array(), $body '') {
481         
$host $uri->getHost();
482             
$host = (strtolower($uri->getScheme()) == 'https' 'sslv2://' $host $host);
483
484         
// Build request headers
485         
$path $uri->getPath();
486         if (
$uri->getQuery()) $path .= '?' $uri->getQuery();
487         
$request "{$method} {$path} HTTP/{$http_ver}\r\n";
488         foreach (
$headers as $k => $v) {
489             if (
is_string($k)) $v ucfirst($k) . ": $v";
490             
$request .= "$v\r\n";
491         }
492
493         
// Add the request body
494         
$request .= "\r\n" $body;
495
496         
// Do nothing - just return the request as string
497         
return $request;
498     }
499
500     
/**
501      * Return the response set in $this->setResponse()
502      *
503      * @return string
504      */
505     
public function read() {
506         if (
$this->responseIndex >= count($this->responses)) {
507             
$this->responseIndex 0;
508         }
509         return 
$this->responses[$this->responseIndex++];
510     }
511
512     
/**
513      * Close the connection (dummy)
514      *
515      */
516     
public function close() { }
517
518     
/**
519      * Set the HTTP response(s) to be returned by this adapter
520      *
521      * @param Zend_Http_Response|array|string $response
522      */
523     
public function setResponse($response) {
524         if (
$response instanceof Zend_Http_Response) {
525             
$response $response->asString("\r\n");
526         }
527
528         
$this->responses = (array)$response;
529         
$this->responseIndex 0;
530     }
531
532     
/**
533      * Add another response to the response buffer.
534      *
535      * @param string Zend_Http_Response|$response
536      */
537     
public function addResponse($response) {
538          if (
$response instanceof Zend_Http_Response) {
539             
$response $response->asString("\r\n");
540         }
541
542         
$this->responses[] = $response;
543     }
544
545     
/**
546      * Sets the position of the response buffer.  Selects which
547      * response will be returned on the next call to read().
548      *
549      * @param integer $index
550      */
551     
public function setResponseIndex($index) {
552         if (
$index || $index >= count($this->responses)) {
553             if (!
class_exists('Zend_Exception')) require 'exception.php';
554             throw new 
Zend_Http_Client_Adapter_Exception(
555                 
'Index out of range of response buffer size');
556         }
557         
$this->responseIndex $index;
558     }
559 }
560
561
/**
562  * An adapter class for Zend_Http_Client based on the curl extension.
563  * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
564  *
565  * @category   Zend
566  * @package    Zend_Http
567  * @subpackage Client_Adapter
568  * @version    $Id: Curl.php 22221 2010-05-21 07:00:58Z dragonbe $
569  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
570  * @license    http://framework.zend.com/license/new-bsd     New BSD License
571  */
572
class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_InterfaceZend_Http_Client_Adapter_Stream
 
{
573     
/**
574      * Parameters array
575      *
576      * @var array
577      */
578     
protected $_config = array();
579
580     
/**
581      * What host/port are we connected to?
582      *
583      * @var array
584      */
585     
protected $_connected_to = array(nullnull);
586
587     
/**
588      * The curl session handle
589      *
590      * @var resource|null
591      */
592     
protected $_curl null;
593
594     
/**
595      * List of cURL options that should never be overwritten
596      *
597      * @var array
598      */
599     
protected $_invalidOverwritableCurlOptions;
600
601     
/**
602      * Response gotten from server
603      *
604      * @var string
605      */
606     
protected $_response null;
607
608     
/**
609      * Stream for storing output
610      *
611      * @var resource
612      */
613     
protected $out_stream;
614
615     
/**
616      * Adapter constructor
617      *
618      * Config is set using setConfig()
619      *
620      * @return void
621      * @throws Zend_Http_Client_Adapter_Exception
622      */
623     
public function __construct() {
624         if (!
extension_loaded('curl')) {
625             if (!
class_exists('Zend_Exception')) require 'exception.php';
626             throw new 
Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this
 Zend_Http_Client adapter.'
);
627         }
628         
$this->_invalidOverwritableCurlOptions = array(
629             
CURLOPT_HTTPGET,
630             
CURLOPT_POST,
631             
CURLOPT_PUT,
632             
CURLOPT_CUSTOMREQUEST,
633             
CURLOPT_HEADER,
634             
CURLOPT_RETURNTRANSFER,
635             
CURLOPT_HTTPHEADER,
636             
CURLOPT_POSTFIELDS,
637             
CURLOPT_INFILE,
638             
CURLOPT_INFILESIZE,
639             
CURLOPT_PORT,
640             
CURLOPT_MAXREDIRS,
641             
CURLOPT_CONNECTTIMEOUT,
642             
CURL_HTTP_VERSION_1_1,
643             
CURL_HTTP_VERSION_1_0,
644         );
645     }
646
647     
/**
648      * Set the configuration array for the adapter
649      *
650      * @throws Zend_Http_Client_Adapter_Exception
651      * @param  Zend_Config | array $config
652      * @return Zend_Http_Client_Adapter_Curl
653      */
654     
public function setConfig($config = array()) {
655         if (
$config instanceof Zend_Config) {
656             
$config $config->toArray();
657
658         } elseif (! 
is_array($config)) {
659             if (!
class_exists('Zend_Exception')) require 'exception.php';
660             throw new 
Zend_Http_Client_Adapter_Exception(
661                 
'Array or Zend_Config object expected, got ' gettype($config)
662             );
663         }
664
665         if(isset(
$config['proxy_user']) && isset($config['proxy_pass'])) {
666             
$this->setCurlOption(CURLOPT_PROXYUSERPWD$config['proxy_user'].":".$config['proxy_pass']);
667             unset(
$config['proxy_user'], $config['proxy_pass']);
668         }
669
670         foreach (
$config as $k => $v) {
671             
$option strtolower($k);
672             switch(
$option) {
673                 case 
'proxy_host':
674                     
$this->setCurlOption(CURLOPT_PROXY$v);
675                     break;
676                 case 
'proxy_port':
677                     
$this->setCurlOption(CURLOPT_PROXYPORT$v);
678                     break;
679                 default:
680                     
$this->_config[$option] = $v;
681                     break;
682             }
683         }
684
685         return 
$this;
686     }
687
688     
/**
689       * Retrieve the array of all configuration options
690       *
691       * @return array
692       */
693      
public function getConfig() {
694          return 
$this->_config;
695      }
696
697     
/**
698      * Direct setter for cURL adapter related options.
699      *
700      * @param  string|int $option
701      * @param  mixed $value
702      * @return Zend_Http_Adapter_Curl
703      */
704     
public function setCurlOption($option$value) {
705         if (!isset(
$this->_config['curloptions'])) {
706             
$this->_config['curloptions'] = array();
707         }
708         
$this->_config['curloptions'][$option] = $value;
709         return 
$this;
710     }
711
712     
/**
713      * Initialize curl
714      *
715      * @param  string  $host
716      * @param  int     $port
717      * @param  boolean $secure
718      * @return void
719      * @throws Zend_Http_Client_Adapter_Exception if unable to connect
720      */
721     
public function connect($host$port 80$secure false) {
722         
// If we're already connected, disconnect first
723         
if ($this->_curl) {
724             
$this->close();
725         }
726
727         
// If we are connected to a different server or port, disconnect first
728         
if ($this->_curl
729             
&& is_array($this->_connected_to)
730             && (
$this->_connected_to[0] != $host
731             
|| $this->_connected_to[1] != $port)
732         ) {
733             
$this->close();
734         }
735
736         
// Do the actual connection
737         
$this->_curl curl_init();
738         if (
$port != 80) {
739             
curl_setopt($this->_curlCURLOPT_PORTintval($port));
740         }
741
742         
// Set timeout
743         
curl_setopt($this->_curlCURLOPT_CONNECTTIMEOUT$this->_config['timeout']);
744
745         
// Set Max redirects
746         
curl_setopt($this->_curlCURLOPT_MAXREDIRS$this->_config['maxredirects']);
747
748         if (!
$this->_curl) {
749             
$this->close();
750
751             if (!
class_exists('Zend_Exception')) require 'exception.php';
752             throw new 
Zend_Http_Client_Adapter_Exception('Unable to Connect to ' .  $host ':' $port);
753         }
754
755         if (
$secure !== false) {
756             
// Behave the same like Zend_Http_Adapter_Socket on SSL options.
757             
if (isset($this->_config['sslcert'])) {
758                 
curl_setopt($this->_curlCURLOPT_SSLCERT$this->_config['sslcert']);
759             }
760             if (isset(
$this->_config['sslpassphrase'])) {
761                 
curl_setopt($this->_curlCURLOPT_SSLCERTPASSWD$this->_config['sslpassphrase']);
762             }
763         }
764
765         
// Update connected_to
766         
$this->_connected_to = array($host$port);
767     }
768
769     
/**
770      * Send request to the remote server
771      *
772      * @param  string        $method
773      * @param  Zend_Uri_Http $uri
774      * @param  float         $http_ver
775      * @param  array         $headers
776      * @param  string        $body
777      * @return string        $request
778      * @throws Zend_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file
 
defined,
779      *                                            
unsupported method, or unsupported cURL option
780      
*/
781     public function 
write($method$uri$httpVersion 1.1$headers = array(), $body '') {
782         
// Make sure we're properly connected
783         
if (!$this->_curl) {
784             if (!
class_exists('Zend_Exception')) require 'exception.php';
785             throw new 
Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
786         }
787
788         if (
$this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
789             if (!
class_exists('Zend_Exception')) require 'exception.php';
790             throw new 
Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong
 host"
);
791         }
792
793         
// set URL
794         
curl_setopt($this->_curlCURLOPT_URL$uri->__toString());
795
796         
// ensure correct curl call
797         
$curlValue true;
798         switch (
$method) {
799             case 
Zend_Http_Client::GET:
800                 
$curlMethod CURLOPT_HTTPGET;
801                 break;
802
803             case 
Zend_Http_Client::POST:
804                 
$curlMethod CURLOPT_POST;
805                 break;
806
807             case 
Zend_Http_Client::PUT:
808                 
// There are two different types of PUT request, either a Raw Data string has been set
809                 // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
810                 
if(is_resource($body)) {
811                     
$this->_config['curloptions'][CURLOPT_INFILE] = $body;
812                 }
813                 if (isset(
$this->_config['curloptions'][CURLOPT_INFILE])) {
814                     
// Now we will probably already have Content-Length set, so that we have to delete it
815                     // from $headers at this point:
816                     
foreach ($headers AS $k => $header) {
817                         if (
preg_match('/Content-Length:\s*(\d+)/i'$header$m)) {
818                             if(
is_resource($body)) {
819                                 
$this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1];
820                             }
821                             unset(
$headers[$k]);
822                         }
823                     }
824
825                     if (!isset(
$this->_config['curloptions'][CURLOPT_INFILESIZE])) {
826                         if (!
class_exists('Zend_Exception')) require 'exception.php';
827                         throw new 
Zend_Http_Client_Adapter_Exception("Cannot set a file-handle for cURL option "
828                                             
"CURLOPT_INFILE without also setting its size in
 CURLOPT_INFILESIZE."
);
829                     }
830
831                     if(
is_resource($body)) {
832                         
$body '';
833                     }
834
835                     
$curlMethod CURLOPT_PUT;
836                 } else {
837                     
$curlMethod CURLOPT_CUSTOMREQUEST;
838                     
$curlValue "PUT";
839                 }
840                 break;
841
842             case 
Zend_Http_Client::DELETE:
843                 
$curlMethod CURLOPT_CUSTOMREQUEST;
844                 
$curlValue "DELETE";
845                 break;
846
847             case 
Zend_Http_Client::OPTIONS:
848                 
$curlMethod CURLOPT_CUSTOMREQUEST;
849                 
$curlValue "OPTIONS";
850                 break;
851
852             case 
Zend_Http_Client::TRACE:
853                 
$curlMethod CURLOPT_CUSTOMREQUEST;
854                 
$curlValue "TRACE";
855                 break;
856
857             case 
Zend_Http_Client::HEAD:
858                 
$curlMethod CURLOPT_CUSTOMREQUEST;
859                 
$curlValue "HEAD";
860                 break;
861
862             default:
863                 
// For now, through an exception for unsupported request methods
864                 
if (!class_exists('Zend_Exception')) require 'exception.php';
865                 throw new 
Zend_Http_Client_Adapter_Exception("Method currently not supported");
866         }
867
868         if(
is_resource($body) && $curlMethod != CURLOPT_PUT) {
869             if (!
class_exists('Zend_Exception')) require 'exception.php';
870             throw new 
Zend_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT");
871         }
872
873         
// get http version to use
874         
$curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 CURL_HTTP_VERSION_1_0;
875
876         
// mark as HTTP request and set HTTP method
877         
curl_setopt($this->_curl$curlHttptrue);
878         
curl_setopt($this->_curl$curlMethod$curlValue);
879
880         if(
$this->out_stream) {
881             
// headers will be read into the response
882             
curl_setopt($this->_curlCURLOPT_HEADERfalse);
883             
curl_setopt($this->_curlCURLOPT_HEADERFUNCTION, array($this"readHeader"));
884             
// and data will be written into the file
885             
curl_setopt($this->_curlCURLOPT_FILE$this->out_stream);
886         } else {
887             
// ensure headers are also returned
888             
curl_setopt($this->_curlCURLOPT_HEADERtrue);
889
890             
// ensure actual response is returned
891             
curl_setopt($this->_curlCURLOPT_RETURNTRANSFERtrue);
892         }
893
894         
// set additional headers
895         
$headers['Accept'] = '';
896         
curl_setopt($this->_curlCURLOPT_HTTPHEADER$headers);
897
898         
/**
899          * Make sure POSTFIELDS is set after $curlMethod is set:
900          * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
901          */
902         
if ($method == Zend_Http_Client::POST) {
903             
curl_setopt($this->_curlCURLOPT_POSTFIELDS$body);
904         } elseif (
$curlMethod == CURLOPT_PUT) {
905             
// this covers a PUT by file-handle:
906             // Make the setting of this options explicit (rather than setting it through the loop following a bit
 lower)
907             // to group common functionality together.
908             
curl_setopt($this->_curlCURLOPT_INFILE$this->_config['curloptions'][CURLOPT_INFILE]);
909             
curl_setopt($this->_curlCURLOPT_INFILESIZE$this->_config['curloptions'][CURLOPT_INFILESIZE]);
910             unset(
$this->_config['curloptions'][CURLOPT_INFILE]);
911             unset(
$this->_config['curloptions'][CURLOPT_INFILESIZE]);
912         } elseif (
$method == Zend_Http_Client::PUT) {
913             
// This is a PUT by a setRawData string, not by file-handle
914             
curl_setopt($this->_curlCURLOPT_POSTFIELDS$body);
915         }
916
917         
// set additional curl options
918         
if (isset($this->_config['curloptions'])) {
919             foreach ((array)
$this->_config['curloptions'] as $k => $v) {
920                 if (!
in_array($k$this->_invalidOverwritableCurlOptions)) {
921                     if (
curl_setopt($this->_curl$k$v) == false) {
922                         if (!
class_exists('Zend_Exception')) require 'exception.php';
923                         throw new 
Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set",
 
$k));
924                     }
925                 }
926             }
927         }
928
929         
// send the request
930         
$response curl_exec($this->_curl);
931
932         
// if we used streaming, headers are already there
933         
if(!is_resource($this->out_stream)) {
934             
$this->_response $response;
935         }
936
937         
$request  curl_getinfo($this->_curlCURLINFO_HEADER_OUT);
938         
$request .= $body;
939
940         if (empty(
$this->_response)) {
941             if (!
class_exists('Zend_Exception')) require 'exception.php';
942             throw new 
Zend_Http_Client_Exception("Error in cURL request: " curl_error($this->_curl));
943         }
944
945         
// cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to
 do it again
946         
if (stripos($this->_response"Transfer-Encoding: chunked\r\n")) {
947             
$this->_response str_ireplace("Transfer-Encoding: chunked\r\n"''$this->_response);
948         }
949
950         
// Eliminate multiple HTTP responses.
951         
do {
952             
$parts  preg_split('|(?:\r?\n){2}|m'$this->_response2);
953             
$again  false;
954
955             if (isset(
$parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi"$parts[1])) {
956                 
$this->_response    $parts[1];
957                 
$again              true;
958             }
959         } while (
$again);
960
961         
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
962         
if (stripos($this->_response"HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
963             
$this->_response str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n"''$this->_response);
964         }
965
966         return 
$request;
967     }
968
969     
/**
970      * Return read response from server
971      *
972      * @return string
973      */
974     
public function read() {
975         return 
$this->_response;
976     }
977
978     
/**
979      * Close the connection to the server
980      *
981      */
982     
public function close() {
983         if(
is_resource($this->_curl)) {
984             
curl_close($this->_curl);
985         }
986         
$this->_curl         null;
987         
$this->_connected_to = array(nullnull);
988     }
989
990     
/**
991      * Get cUrl Handle
992      *
993      * @return resource
994      */
995     
public function getHandle() {
996         return 
$this->_curl;
997     }
998
999     
/**
1000      * Set output stream for the response
1001      *
1002      * @param resource $stream
1003      * @return Zend_Http_Client_Adapter_Socket
1004      */
1005     
public function setOutputStream($stream) {
1006         
$this->out_stream $stream;
1007         return 
$this;
1008     }
1009
1010     
/**
1011      * Header reader function for CURL
1012      *
1013      * @param resource $curl
1014      * @param string $header
1015      * @return int
1016      */
1017     
public function readHeader($curl$header) {
1018         
$this->_response .= $header;
1019         return 
strlen($header);
1020     }
1021 }
1022
1023
/**
1024  * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used
1025  * on almost every PHP environment, and does not require any special extensions.
1026  *
1027  * @category   Zend
1028  * @package    Zend_Http
1029  * @subpackage Client_Adapter
1030  * @version    $Id: Socket.php 21778 2010-04-06 11:19:35Z shahar $
1031  * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
1032  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
1033  * @license    http://framework.zend.com/license/new-bsd     New BSD License
1034  */
1035
class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface,
 
Zend_Http_Client_Adapter_Stream {
1036     
/**
1037      * The socket for server connection
1038      *
1039      * @var resource|null
1040      */
1041     
protected $socket null;
1042
1043     
/**
1044      * What host/port are we connected to?
1045      *
1046      * @var array
1047      */
1048     
protected $connected_to = array(nullnull);
1049
1050     
/**
1051      * Stream for storing output
1052      *
1053      * @var resource
1054      */
1055     
protected $out_stream null;
1056
1057     
/**
1058      * Parameters array
1059      *
1060      * @var array
1061      */
1062     
protected $config = array(
1063         
'persistent'    => false,
1064         
'ssltransport'  => 'ssl',
1065         
'sslcert'       => null,
1066         
'sslpassphrase' => null,
1067         
'sslusecontext' => false
1068     
);
1069
1070     
/**
1071      * Request method - will be set by write() and might be used by read()
1072      *
1073      * @var string
1074      */
1075     
protected $method null;
1076
1077     
/**
1078      * Stream context
1079      *
1080      * @var resource
1081      */
1082     
protected $_context null;
1083
1084     
/**
1085      * Adapter constructor, currently empty. Config is set using setConfig()
1086      *
1087      */
1088     
public function __construct() {
1089     }
1090
1091     
/**
1092      * Set the configuration array for the adapter
1093      *
1094      * @param Zend_Config | array $config
1095      */
1096     
public function setConfig($config = array()) {
1097         if (
$config instanceof Zend_Config) {
1098             
$config $config->toArray();
1099
1100         } elseif (! 
is_array($config)) {
1101             if (!
class_exists('Zend_Exception')) require 'exception.php';
1102             throw new 
Zend_Http_Client_Adapter_Exception(
1103                 
'Array or Zend_Config object expected, got ' gettype($config)
1104             );
1105         }
1106
1107         foreach (
$config as $k => $v) {
1108             
$this->config[strtolower($k)] = $v;
1109         }
1110     }
1111
1112     
/**
1113       * Retrieve the array of all configuration options
1114       *
1115       * @return array
1116       */
1117      
public function getConfig() {
1118          return 
$this->config;
1119      }
1120
1121      
/**
1122      * Set the stream context for the TCP connection to the server
1123      *
1124      * Can accept either a pre-existing stream context resource, or an array
1125      * of stream options, similar to the options array passed to the
1126      * stream_context_create() PHP function. In such case a new stream context
1127      * will be created using the passed options.
1128      *
1129      * @since  Zend Framework 1.9
1130      *
1131      * @param  mixed $context Stream context or array of context options
1132      * @return Zend_Http_Client_Adapter_Socket
1133      */
1134     
public function setStreamContext($context) {
1135         if (
is_resource($context) && get_resource_type($context) == 'stream-context') {
1136             
$this->_context $context;
1137
1138         } elseif (
is_array($context)) {
1139             
$this->_context stream_context_create($context);
1140
1141         } else {
1142             
// Invalid parameter
1143             
if (!class_exists('Zend_Exception')) require 'exception.php';
1144             throw new 
Zend_Http_Client_Adapter_Exception(
1145                 
"Expecting either a stream context resource or array, got " gettype($context)
1146             );
1147         }
1148
1149         return 
$this;
1150     }
1151
1152     
/**
1153      * Get the stream context for the TCP connection to the server.
1154      *
1155      * If no stream context is set, will create a default one.
1156      *
1157      * @return resource
1158      */
1159     
public function getStreamContext() {
1160         if (! 
$this->_context) {
1161             
$this->_context stream_context_create();
1162         }
1163
1164         return 
$this->_context;
1165     }
1166
1167     
/**
1168      * Connect to the remote server
1169      *
1170      * @param string  $host
1171      * @param int     $port
1172      * @param boolean $secure
1173      */
1174     
public function connect($host$port 80$secure false) {
1175         
// If the URI should be accessed via SSL, prepend the Hostname with ssl://
1176         
$host = ($secure $this->config['ssltransport'] : 'tcp') . '://' $host;
1177
1178         
// If we are connected to the wrong host, disconnect first
1179         
if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
1180             if (
is_resource($this->socket)) $this->close();
1181         }
1182
1183         
// Now, if we are not connected, connect
1184         
if (! is_resource($this->socket) || ! $this->config['keepalive']) {
1185             
$context $this->getStreamContext();
1186             if (
$secure || $this->config['sslusecontext']) {
1187                 if (
$this->config['sslcert'] !== null) {
1188                     if (! 
stream_context_set_option($context'ssl''local_cert',
1189                                                     
$this->config['sslcert'])) {
1190                         if (!
class_exists('Zend_Exception')) require 'exception.php';
1191                         throw new 
Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
1192                     }
1193                 }
1194                 if (
$this->config['sslpassphrase'] !== null) {
1195                     if (! 
stream_context_set_option($context'ssl''passphrase',
1196                                                     
$this->config['sslpassphrase'])) {
1197                         if (!
class_exists('Zend_Exception')) require 'exception.php';
1198                         throw new 
Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
1199                     }
1200                 }
1201             }
1202
1203             
$flags STREAM_CLIENT_CONNECT;
1204             if (
$this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT;
1205
1206             
$this->socket = @stream_socket_client($host ':' $port,
1207                                                   
$errno,
1208                                                   
$errstr,
1209                                                   (int) 
$this->config['timeout'],
1210                                                   
$flags,
1211                                                   
$context);
1212
1213             if (! 
$this->socket) {
1214                 
$this->close();
1215                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1216                 throw new 
Zend_Http_Client_Adapter_Exception(
1217                     
'Unable to Connect to ' $host ':' $port '. Error #' $errno ': ' $errstr);
1218             }
1219
1220             
// Set the stream timeout
1221             
if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
1222                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1223                 throw new 
Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
1224             }
1225
1226             
// Update connected_to
1227             
$this->connected_to = array($host$port);
1228         }
1229     }
1230
1231     
/**
1232      * Send request to the remote server
1233      *
1234      * @param string        $method
1235      * @param Zend_Uri_Http $uri
1236      * @param string        $http_ver
1237      * @param array         $headers
1238      * @param string        $body
1239      * @return string Request as string
1240      */
1241     
public function write($method$uri$http_ver '1.1'$headers = array(), $body '') {
1242         
// Make sure we're properly connected
1243         
if (! $this->socket) {
1244             if (!
class_exists('Zend_Exception')) require 'exception.php';
1245             throw new 
Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
1246         }
1247
1248         
$host $uri->getHost();
1249         
$host = (strtolower($uri->getScheme()) == 'https' $this->config['ssltransport'] : 'tcp') . '://' .
 
$host;
1250         if (
$this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
1251             if (!
class_exists('Zend_Exception')) require 'exception.php';
1252             throw new 
Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong
 host'
);
1253         }
1254
1255         
// Save request method for later
1256         
$this->method $method;
1257
1258         
// Build request headers
1259         
$path $uri->getPath();
1260         if (
$uri->getQuery()) $path .= '?' $uri->getQuery();
1261         
$request "{$method} {$path} HTTP/{$http_ver}\r\n";
1262         foreach (
$headers as $k => $v) {
1263             if (
is_string($k)) $v ucfirst($k) . ": $v";
1264             
$request .= "$v\r\n";
1265         }
1266
1267         if(
is_resource($body)) {
1268             
$request .= "\r\n";
1269         } else {
1270             
// Add the request body
1271             
$request .= "\r\n" $body;
1272         }
1273
1274         
// Send the request
1275         
if (! @fwrite($this->socket$request)) {
1276             if (!
class_exists('Zend_Exception')) require 'exception.php';
1277             throw new 
Zend_Http_Client_Adapter_Exception('Error writing request to server');
1278         }
1279
1280         if(
is_resource($body)) {
1281             if(
stream_copy_to_stream($body$this->socket) == 0) {
1282                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1283                 throw new 
Zend_Http_Client_Adapter_Exception('Error writing request to server');
1284             }
1285         }
1286
1287         return 
$request;
1288     }
1289
1290     
/**
1291      * Read response from server
1292      *
1293      * @return string
1294      */
1295     
public function read() {
1296         
// First, read headers only
1297         
$response '';
1298         
$gotStatus false;
1299         
$stream = !empty($this->config['stream']);
1300
1301         while ((
$line = @fgets($this->socket)) !== false) {
1302             
$gotStatus $gotStatus || (strpos($line'HTTP') !== false);
1303             if (
$gotStatus) {
1304                 
$response .= $line;
1305                 if (
rtrim($line) === '') break;
1306             }
1307         }
1308
1309         
$this->_checkSocketReadTimeout();
1310
1311         
$statusCode Zend_Http_Response::extractCode($response);
1312
1313         
// Handle 100 and 101 responses internally by restarting the read again
1314         
if ($statusCode == 100 || $statusCode == 101) return $this->read();
1315
1316         
// Check headers to see what kind of connection / transfer encoding we have
1317         
$headers Zend_Http_Response::extractHeaders($response);
1318
1319         
/**
1320          * Responses to HEAD requests and 204 or 304 responses are not expected
1321          * to have a body - stop reading here
1322          */
1323         
if ($statusCode == 304 || $statusCode == 204 ||
1324             
$this->method == Zend_Http_Client::HEAD) {
1325
1326             
// Close the connection if requested to do so by the server
1327             
if (isset($headers['connection']) && $headers['connection'] == 'close') {
1328                 
$this->close();
1329             }
1330             return 
$response;
1331         }
1332
1333         
// If we got a 'transfer-encoding: chunked' header
1334         
if (isset($headers['transfer-encoding'])) {
1335
1336             if (
strtolower($headers['transfer-encoding']) == 'chunked') {
1337
1338                 do {
1339                     
$line  = @fgets($this->socket);
1340                     
$this->_checkSocketReadTimeout();
1341
1342                     
$chunk $line;
1343
1344                     
// Figure out the next chunk size
1345                     
$chunksize trim($line);
1346                     if (! 
ctype_xdigit($chunksize)) {
1347                         
$this->close();
1348                         if (!
class_exists('Zend_Exception')) require 'exception.php';
1349                         throw new 
Zend_Http_Client_Adapter_Exception('Invalid chunk size "' .
1350                             
$chunksize '" unable to read chunked body');
1351                     }
1352
1353                     
// Convert the hexadecimal value to plain integer
1354                     
$chunksize hexdec($chunksize);
1355
1356                     
// Read next chunk
1357                     
$read_to ftell($this->socket) + $chunksize;
1358
1359                     do {
1360                         
$current_pos ftell($this->socket);
1361                         if (
$current_pos >= $read_to) break;
1362
1363                         if(
$this->out_stream) {
1364                             if(
stream_copy_to_stream($this->socket$this->out_stream$read_to $current_pos) ==
 
0) {
1365                               
$this->_checkSocketReadTimeout();
1366                               break;
1367                              }
1368                         } else {
1369                             
$line = @fread($this->socket$read_to $current_pos);
1370                             if (
$line === false || strlen($line) === 0) {
1371                                 
$this->_checkSocketReadTimeout();
1372                                 break;
1373                             }
1374                                     
$chunk .= $line;
1375                         }
1376                     } while (! 
feof($this->socket));
1377
1378                     
$chunk .= @fgets($this->socket);
1379                     
$this->_checkSocketReadTimeout();
1380
1381                     if(!
$this->out_stream) {
1382                         
$response .= $chunk;
1383                     }
1384                 } while (
$chunksize 0);
1385             } else {
1386                 
$this->close();
1387                 throw new 
Zend_Http_Client_Adapter_Exception('Cannot handle "' .
1388                     
$headers['transfer-encoding'] . '" transfer encoding');
1389             }
1390
1391             
// We automatically decode chunked-messages when writing to a stream
1392             // this means we have to disallow the Zend_Http_Response to do it again
1393             
if ($this->out_stream) {
1394                 
$response str_ireplace("Transfer-Encoding: chunked\r\n"''$response);
1395             }
1396         
// Else, if we got the content-length header, read this number of bytes
1397         
} elseif (isset($headers['content-length'])) {
1398
1399             
// If we got more than one Content-Length header (see ZF-9404) use
1400             // the last value sent
1401             
if (is_array($headers['content-length'])) {
1402                 
$contentLength $headers['content-length'][count($headers['content-length']) - 1];
1403             } else {
1404                 
$contentLength $headers['content-length'];
1405             }
1406
1407             
$current_pos ftell($this->socket);
1408             
$chunk '';
1409
1410             for (
$read_to $current_pos $contentLength;
1411                  
$read_to $current_pos;
1412                  
$current_pos ftell($this->socket)) {
1413
1414                  if(
$this->out_stream) {
1415                      if(@
stream_copy_to_stream($this->socket$this->out_stream$read_to $current_pos) == 0) {
1416                           
$this->_checkSocketReadTimeout();
1417                           break;
1418                      }
1419                  } else {
1420                     
$chunk = @fread($this->socket$read_to $current_pos);
1421                     if (
$chunk === false || strlen($chunk) === 0) {
1422                         
$this->_checkSocketReadTimeout();
1423                         break;
1424                     }
1425
1426                     
$response .= $chunk;
1427                 }
1428
1429                 
// Break if the connection ended prematurely
1430                 
if (feof($this->socket)) break;
1431             }
1432
1433         
// Fallback: just read the response until EOF
1434         
} else {
1435
1436             do {
1437                 if(
$this->out_stream) {
1438                     if(@
stream_copy_to_stream($this->socket$this->out_stream) == 0) {
1439                           
$this->_checkSocketReadTimeout();
1440                           break;
1441                      }
1442                 }  else {
1443                     
$buff = @fread($this->socket8192);
1444                     if (
$buff === false || strlen($buff) === 0) {
1445                         
$this->_checkSocketReadTimeout();
1446                         break;
1447                     } else {
1448                         
$response .= $buff;
1449                     }
1450                 }
1451
1452             } while (
feof($this->socket) === false);
1453
1454             
$this->close();
1455         }
1456
1457         
// Close the connection if requested to do so by the server
1458         
if (isset($headers['connection']) && $headers['connection'] == 'close') {
1459             
$this->close();
1460         }
1461
1462         return 
$response;
1463     }
1464
1465     
/**
1466      * Close the connection to the server
1467      *
1468      */
1469     
public function close() {
1470         if (
is_resource($this->socket)) @fclose($this->socket);
1471         
$this->socket null;
1472         
$this->connected_to = array(nullnull);
1473     }
1474
1475     
/**
1476      * Check if the socket has timed out - if so close connection and throw
1477      * an exception
1478      *
1479      * @throws Zend_Http_Client_Adapter_Exception with READ_TIMEOUT code
1480      */
1481     
protected function _checkSocketReadTimeout() {
1482         if (
$this->socket) {
1483             
$info stream_get_meta_data($this->socket);
1484             
$timedout $info['timed_out'];
1485             if (
$timedout) {
1486                 
$this->close();
1487                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1488                 throw new 
Zend_Http_Client_Adapter_Exception(
1489                     
"Read timed out after {$this->config['timeout']} seconds",
1490                     
Zend_Http_Client_Adapter_Exception::READ_TIMEOUT
1491                 
);
1492             }
1493         }
1494     }
1495
1496     
/**
1497      * Set output stream for the response
1498      *
1499      * @param resource $stream
1500      * @return Zend_Http_Client_Adapter_Socket
1501      */
1502     
public function setOutputStream($stream) {
1503         
$this->out_stream $stream;
1504         return 
$this;
1505     }
1506
1507     
/**
1508      * Destructor: make sure the socket is disconnected
1509      *
1510      * If we are in persistent TCP mode, will not close the connection
1511      *
1512      */
1513     
public function __destruct() {
1514         if (! 
$this->config['persistent']) {
1515             if (
$this->socket$this->close();
1516         }
1517     }
1518 }
1519
1520
/**
1521  * An interface description for Zend_Http_Client_Adapter_Stream classes.
1522  *
1523  * This interface decribes Zend_Http_Client_Adapter which supports streaming.
1524  *
1525  * @category   Zend
1526  * @package    Zend_Http
1527  * @subpackage Client_Adapter
1528  * @version    $Id: Interface.php 16214 2009-06-21 19:34:03Z thomas $
1529  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
1530  * @license    http://framework.zend.com/license/new-bsd     New BSD License
1531  */
1532
interface Zend_Http_Client_Adapter_Stream {
1533     
/**
1534      * Set output stream
1535      *
1536      * This function sets output stream where the result will be stored.
1537      *
1538      * @param resource $stream Stream to write the output to
1539      *
1540      */
1541     
function setOutputStream($stream);
1542 }
1543
1544
/**
1545  * HTTP Proxy-supporting Zend_Http_Client adapter class, based on the default
1546  * socket based adapter.
1547  *
1548  * Should be used if proxy HTTP access is required. If no proxy is set, will
1549  * fall back to Zend_Http_Client_Adapter_Socket behavior. Just like the
1550  * default Socket adapter, this adapter does not require any special extensions
1551  * installed.
1552  *
1553  * @category   Zend
1554  * @package    Zend_Http
1555  * @subpackage Client_Adapter
1556  * @version    $Id: Proxy.php 22445 2010-06-16 09:09:12Z bate $
1557  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
1558  * @license    http://framework.zend.com/license/new-bsd     New BSD License
1559  */
1560
class Zend_Http_Client_Adapter_Proxy extends Zend_Http_Client_Adapter_Socket {
1561     
/**
1562      * Parameters array
1563      *
1564      * @var array
1565      */
1566     
protected $config = array(
1567         
'ssltransport'  => 'ssl',
1568         
'sslcert'       => null,
1569         
'sslpassphrase' => null,
1570         
'sslusecontext' => false,
1571         
'proxy_host'    => '',
1572         
'proxy_port'    => 8080,
1573         
'proxy_user'    => '',
1574         
'proxy_pass'    => '',
1575         
'proxy_auth'    => Zend_Http_Client::AUTH_BASIC,
1576         
'persistent'    => false
1577     
);
1578
1579     
/**
1580      * Whether HTTPS CONNECT was already negotiated with the proxy or not
1581      *
1582      * @var boolean
1583      */
1584     
protected $negotiated false;
1585
1586     
/**
1587      * Connect to the remote server
1588      *
1589      * Will try to connect to the proxy server. If no proxy was set, will
1590      * fall back to the target server (behave like regular Socket adapter)
1591      *
1592      * @param string  $host
1593      * @param int     $port
1594      * @param boolean $secure
1595      */
1596     
public function connect($host$port 80$secure false) {
1597         
// If no proxy is set, fall back to Socket adapter
1598         
if (! $this->config['proxy_host']) {
1599             return 
parent::connect($host$port$secure);
1600         }
1601
1602         
/* Url might require stream context even if proxy connection doesn't */
1603         
if ($secure) {
1604             
$this->config['sslusecontext'] = true;
1605         }
1606
1607         
// Connect (a non-secure connection) to the proxy server
1608         
return parent::connect(
1609             
$this->config['proxy_host'],
1610             
$this->config['proxy_port'],
1611             
false
1612         
);
1613     }
1614
1615     
/**
1616      * Send request to the proxy server
1617      *
1618      * @param string        $method
1619      * @param Zend_Uri_Http $uri
1620      * @param string        $http_ver
1621      * @param array         $headers
1622      * @param string        $body
1623      * @return string Request as string
1624      */
1625     
public function write($method$uri$http_ver '1.1'$headers = array(), $body '') {
1626         
// If no proxy is set, fall back to default Socket adapter
1627         
if (! $this->config['proxy_host']) return parent::write($method$uri$http_ver$headers$body);
1628
1629         
// Make sure we're properly connected
1630         
if (! $this->socket) {
1631             if (!
class_exists('Zend_Exception')) require 'exception.php';
1632             throw new 
Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
1633         }
1634
1635         
$host $this->config['proxy_host'];
1636         
$port $this->config['proxy_port'];
1637
1638         if (
$this->connected_to[0] != "tcp://$host|| $this->connected_to[1] != $port) {
1639             if (!
class_exists('Zend_Exception')) require 'exception.php';
1640             throw new 
Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy
 server"
);
1641         }
1642
1643         
// Add Proxy-Authorization header
1644         
if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
1645             
$headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader(
1646                 
$this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
1647             );
1648         }
1649
1650         
// if we are proxying HTTPS, preform CONNECT handshake with the proxy
1651         
if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
1652             
$this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver$headers);
1653             
$this->negotiated true;
1654         }
1655
1656         
// Save request method for later
1657         
$this->method $method;
1658
1659         
// Build request headers
1660         
if ($this->negotiated) {
1661             
$path $uri->getPath();
1662             if (
$uri->getQuery()) {
1663                 
$path .= '?' $uri->getQuery();
1664             }
1665             
$request "$method $path HTTP/$http_ver\r\n";
1666         } else {
1667             
$request "$method $uri HTTP/$http_ver\r\n";
1668         }
1669
1670         
// Add all headers to the request string
1671         
foreach ($headers as $k => $v) {
1672             if (
is_string($k)) $v "$k$v";
1673             
$request .= "$v\r\n";
1674         }
1675
1676         if(
is_resource($body)) {
1677             
$request .= "\r\n";
1678         } else {
1679             
// Add the request body
1680             
$request .= "\r\n" $body;
1681         }
1682
1683         
// Send the request
1684         
if (! @fwrite($this->socket$request)) {
1685             if (!
class_exists('Zend_Exception')) require 'exception.php';
1686             throw new 
Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
1687         }
1688
1689         if(
is_resource($body)) {
1690             if(
stream_copy_to_stream($body$this->socket) == 0) {
1691                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1692                 throw new 
Zend_Http_Client_Adapter_Exception('Error writing request to server');
1693             }
1694         }
1695
1696         return 
$request;
1697     }
1698
1699     
/**
1700      * Preform handshaking with HTTPS proxy using CONNECT method
1701      *
1702      * @param string  $host
1703      * @param integer $port
1704      * @param string  $http_ver
1705      * @param array   $headers
1706      */
1707     
protected function connectHandshake($host$port 443$http_ver '1.1', array &$headers = array()) {
1708         
$request "CONNECT $host:$port HTTP/$http_ver\r\n" .
1709                    
"Host: " $this->config['proxy_host'] . "\r\n";
1710
1711         
// Add the user-agent header
1712         
if (isset($this->config['useragent'])) {
1713             
$request .= "User-agent: " $this->config['useragent'] . "\r\n";
1714         }
1715
1716         
// If the proxy-authorization header is set, send it to proxy but remove
1717         // it from headers sent to target host
1718         
if (isset($headers['proxy-authorization'])) {
1719             
$request .= "Proxy-authorization: " $headers['proxy-authorization'] . "\r\n";
1720             unset(
$headers['proxy-authorization']);
1721         }
1722
1723         
$request .= "\r\n";
1724
1725         
// Send the request
1726         
if (! @fwrite($this->socket$request)) {
1727             if (!
class_exists('Zend_Exception')) require 'exception.php';
1728             throw new 
Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
1729         }
1730
1731         
// Read response headers only
1732         
$response '';
1733         
$gotStatus false;
1734         while (
$line = @fgets($this->socket)) {
1735             
$gotStatus $gotStatus || (strpos($line'HTTP') !== false);
1736             if (
$gotStatus) {
1737                 
$response .= $line;
1738                 if (!
chop($line)) break;
1739             }
1740         }
1741
1742         
// Check that the response from the proxy is 200
1743         
if (Zend_Http_Response::extractCode($response) != 200) {
1744             if (!
class_exists('Zend_Exception')) require 'exception.php';
1745             throw new 
Zend_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " .
 
$response);
1746         }
1747
1748         
// If all is good, switch socket to secure mode. We have to fall back
1749         // through the different modes
1750         
$modes = array(
1751             
STREAM_CRYPTO_METHOD_TLS_CLIENT,
1752             
STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
1753             
STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
1754             
STREAM_CRYPTO_METHOD_SSLv2_CLIENT
1755         
);
1756
1757         
$success false;
1758         foreach(
$modes as $mode) {
1759             
$success stream_socket_enable_crypto($this->sockettrue$mode);
1760             if (
$success) break;
1761         }
1762
1763         if (! 
$success) {
1764                 if (!
class_exists('Zend_Exception')) require 'exception.php';
1765                 throw new 
Zend_Http_Client_Adapter_Exception("Unable to connect to" .
1766                     
" HTTPS server through proxy: could not negotiate secure connection.");
1767         }
1768     }
1769
1770     
/**
1771      * Close the connection to the server
1772      *
1773      */
1774     
public function close() {
1775         
parent::close();
1776         
$this->negotiated false;
1777     }
1778
1779     
/**
1780      * Destructor: make sure the socket is disconnected
1781      *
1782      */
1783     
public function __destruct() {
1784         if (
$this->socket$this->close();
1785     }
1786 }
1787
1788
/**
1789  * An interface description for Zend_Http_Client_Adapter classes.
1790  *
1791  * These classes are used as connectors for Zend_Http_Client, performing the
1792  * tasks of connecting, writing, reading and closing connection to the server.
1793  *
1794  * @category   Zend
1795  * @package    Zend_Http
1796  * @subpackage Client_Adapter
1797  * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
1798  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
1799  * @license    http://framework.zend.com/license/new-bsd     New BSD License
1800  */
1801
interface Zend_Http_Client_Adapter_Interface {
1802     
/**
1803      * Set the configuration array for the adapter
1804      *
1805      * @param array $config
1806      */
1807     
public function setConfig($config = array());
1808
1809     
/**
1810      * Connect to the remote server
1811      *
1812      * @param string  $host
1813      * @param int     $port
1814      * @param boolean $secure
1815      */
1816     
public function connect($host$port 80$secure false);
1817
1818     
/**
1819      * Send request to the remote server
1820      *
1821      * @param string        $method
1822      * @param Zend_Uri_Http $url
1823      * @param string        $http_ver
1824      * @param array         $headers
1825      * @param string        $body
1826      * @return string Request as text
1827      */
1828     
public function write($method$url$http_ver '1.1'$headers = array(), $body '');
1829
1830     
/**
1831      * Read response from server
1832      *
1833      * @return string
1834      */
1835     
public function read();
1836
1837     
/**
1838      * Close the connection to the server
1839      *
1840      */
1841     
public function close();
1842 }
1843
1844
/**
1845  * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It
1846  * includes easy access to all the response's different elemts, as well as some
1847  * convenience methods for parsing and validating HTTP responses.
1848  *
1849  * @package    Zend_Http
1850  * @subpackage Response
1851  * @version    $Id: Response.php 20096 2010-01-06 02:05:09Z bkarwin $
1852  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
1853  * @license    http://framework.zend.com/license/new-bsd     New BSD License
1854  */
1855
class Zend_Http_Response {
1856     
/**
1857      * List of all known HTTP response codes - used by responseCodeAsText() to
1858      * translate numeric codes to messages.
1859      *
1860      * @var array
1861      */
1862     
protected static $messages = array(
1863         
// Informational 1xx
1864         
100 => 'Continue',
1865         
101 => 'Switching Protocols',
1866
1867         
// Success 2xx
1868         
200 => 'OK',
1869         
201 => 'Created',
1870         
202 => 'Accepted',
1871         
203 => 'Non-Authoritative Information',
1872         
204 => 'No Content',
1873         
205 => 'Reset Content',
1874         
206 => 'Partial Content',
1875
1876         
// Redirection 3xx
1877         
300 => 'Multiple Choices',
1878         
301 => 'Moved Permanently',
1879         
302 => 'Found',  // 1.1
1880         
303 => 'See Other',
1881         
304 => 'Not Modified',
1882         
305 => 'Use Proxy',
1883         
// 306 is deprecated but reserved
1884         
307 => 'Temporary Redirect',
1885
1886         
// Client Error 4xx
1887         
400 => 'Bad Request',
1888         
401 => 'Unauthorized',
1889         
402 => 'Payment Required',
1890         
403 => 'Forbidden',
1891         
404 => 'Not Found',
1892         
405 => 'Method Not Allowed',
1893         
406 => 'Not Acceptable',
1894         
407 => 'Proxy Authentication Required',
1895         
408 => 'Request Timeout',
1896         
409 => 'Conflict',
1897         
410 => 'Gone',
1898         
411 => 'Length Required',
1899         
412 => 'Precondition Failed',
1900         
413 => 'Request Entity Too Large',
1901         
414 => 'Request-URI Too Long',
1902         
415 => 'Unsupported Media Type',
1903         
416 => 'Requested Range Not Satisfiable',
1904         
417 => 'Expectation Failed',
1905
1906         
// Server Error 5xx
1907         
500 => 'Internal Server Error',
1908         
501 => 'Not Implemented',
1909         
502 => 'Bad Gateway',
1910         
503 => 'Service Unavailable',
1911         
504 => 'Gateway Timeout',
1912         
505 => 'HTTP Version Not Supported',
1913         
509 => 'Bandwidth Limit Exceeded'
1914     
);
1915
1916     
/**
1917      * The HTTP version (1.0, 1.1)
1918      *
1919      * @var string
1920      */
1921     
protected $version;
1922
1923     
/**
1924      * The HTTP response code
1925      *
1926      * @var int
1927      */
1928     
protected $code;
1929
1930     
/**
1931      * The HTTP response code as string
1932      * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
1933      *
1934      * @var string
1935      */
1936     
protected $message;
1937
1938     
/**
1939      * The HTTP response headers array
1940      *
1941      * @var array
1942      */
1943     
protected $headers = array();
1944
1945     
/**
1946      * The HTTP response body
1947      *
1948      * @var string
1949      */
1950     
protected $body;
1951
1952     
/**
1953      * HTTP response constructor
1954      *
1955      * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP
1956      * response string and create a new Zend_Http_Response object.
1957      *
1958      * NOTE: The constructor no longer accepts nulls or empty values for the code and
1959      * headers and will throw an exception if the passed values do not form a valid HTTP
1960      * responses.
1961      *
1962      * If no message is passed, the message will be guessed according to the response code.
1963      *
1964      * @param int $code Response code (200, 404, ...)
1965      * @param array $headers Headers array
1966      * @param string $body Response body
1967      * @param string $version HTTP version
1968      * @param string $message Response code as text
1969      * @throws Zend_Http_Exception
1970      */
1971     
public function __construct($code$headers$body null$version '1.1'$message null) {
1972         
// Make sure the response code is valid and set it
1973         
if (self::responseCodeAsText($code) === null) {
1974             if (!
class_exists('Zend_Exception')) require 'exception.php';
1975             throw new 
Zend_Http_Exception("{$code} is not a valid HTTP response code");
1976         }
1977
1978         
$this->code $code;
1979
1980         
// Make sure we got valid headers and set them
1981         
if (! is_array($headers)) {
1982             if (!
class_exists('Zend_Exception')) require 'exception.php';
1983             throw new 
Zend_Http_Exception('No valid headers were passed');
1984     }
1985
1986         foreach (
$headers as $name => $value) {
1987             if (
is_int($name))
1988                 list(
$name$value) = explode(": "$value1);
1989
1990             
$this->headers[ucwords(strtolower($name))] = $value;
1991         }
1992
1993         
// Set the body
1994         
$this->body $body;
1995
1996         
// Set the HTTP version
1997         
if (! preg_match('|^\d\.\d$|'$version)) {
1998             if (!
class_exists('Zend_Exception')) require 'exception.php';
1999             throw new 
Zend_Http_Exception("Invalid HTTP response version: $version");
2000         }
2001
2002         
$this->version $version;
2003
2004         
// If we got the response message, set it. Else, set it according to
2005         // the response code
2006         
if (is_string($message)) {
2007             
$this->message $message;
2008         } else {
2009             
$this->message self::responseCodeAsText($code);
2010         }
2011     }
2012
2013     
/**
2014      * Check whether the response is an error
2015      *
2016      * @return boolean
2017      */
2018     
public function isError() {
2019         
$restype floor($this->code 100);
2020         if (
$restype == || $restype == 5) {
2021             return 
true;
2022         }
2023
2024         return 
false;
2025     }
2026
2027     
/**
2028      * Check whether the response in successful
2029      *
2030      * @return boolean
2031      */
2032     
public function isSuccessful() {
2033         
$restype floor($this->code 100);
2034         if (
$restype == || $restype == 1) { // Shouldn't 3xx count as success as well ???
2035             
return true;
2036         }
2037
2038         return 
false;
2039     }
2040
2041     
/**
2042      * Check whether the response is a redirection
2043      *
2044      * @return boolean
2045      */
2046     
public function isRedirect() {
2047         
$restype floor($this->code 100);
2048         if (
$restype == 3) {
2049             return 
true;
2050         }
2051
2052         return 
false;
2053     }
2054
2055     
/**
2056      * Get the response body as string
2057      *
2058      * This method returns the body of the HTTP response (the content), as it
2059      * should be in it's readable version - that is, after decoding it (if it
2060      * was decoded), deflating it (if it was gzip compressed), etc.
2061      *
2062      * If you want to get the raw body (as transfered on wire) use
2063      * $this->getRawBody() instead.
2064      *
2065      * @return string
2066      */
2067     
public function getBody() {
2068         
$body '';
2069
2070         
// Decode the body if it was transfer-encoded
2071         
switch (strtolower($this->getHeader('transfer-encoding'))) {
2072
2073             
// Handle chunked body
2074             
case 'chunked':
2075                 
$body self::decodeChunkedBody($this->body);
2076                 break;
2077
2078             
// No transfer encoding, or unknown encoding extension:
2079             // return body as is
2080             
default:
2081                 
$body $this->body;
2082                 break;
2083         }
2084
2085         
// Decode any content-encoding (gzip or deflate) if needed
2086         
switch (strtolower($this->getHeader('content-encoding'))) {
2087
2088             
// Handle gzip encoding
2089             
case 'gzip':
2090                 
$body self::decodeGzip($body);
2091                 break;
2092
2093             
// Handle deflate encoding
2094             
case 'deflate':
2095                 
$body self::decodeDeflate($body);
2096                 break;
2097
2098             default:
2099                 break;
2100         }
2101
2102         return 
$body;
2103     }
2104
2105     
/**
2106      * Get the raw response body (as transfered "on wire") as string
2107      *
2108      * If the body is encoded (with Transfer-Encoding, not content-encoding -
2109      * IE "chunked" body), gzip compressed, etc. it will not be decoded.
2110      *
2111      * @return string
2112      */
2113     
public function getRawBody() {
2114         return 
$this->body;
2115     }
2116
2117     
/**
2118      * Get the HTTP version of the response
2119      *
2120      * @return string
2121      */
2122     
public function getVersion() {
2123         return 
$this->version;
2124     }
2125
2126     
/**
2127      * Get the HTTP response status code
2128      *
2129      * @return int
2130      */
2131     
public function getStatus() {
2132         return 
$this->code;
2133     }
2134
2135     
/**
2136      * Return a message describing the HTTP response code
2137      * (Eg. "OK", "Not Found", "Moved Permanently")
2138      *
2139      * @return string
2140      */
2141     
public function getMessage() {
2142         return 
$this->message;
2143     }
2144
2145     
/**
2146      * Get the response headers
2147      *
2148      * @return array
2149      */
2150     
public function getHeaders() {
2151         return 
$this->headers;
2152     }
2153
2154     
/**
2155      * Get a specific header as string, or null if it is not set
2156      *
2157      * @param string$header
2158      * @return string|array|null
2159      */
2160     
public function getHeader($header) {
2161         
$header ucwords(strtolower($header));
2162         if (! 
is_string($header) || ! isset($this->headers[$header])) return null;
2163
2164         return 
$this->headers[$header];
2165     }
2166
2167     
/**
2168      * Get all headers as string
2169      *
2170      * @param boolean $status_line Whether to return the first status line (IE "HTTP 200 OK")
2171      * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
2172      * @return string
2173      */
2174     
public function getHeadersAsString($status_line true$br "\n") {
2175         
$str '';
2176
2177         if (
$status_line) {
2178             
$str "HTTP/{$this->version} {$this->code} {$this->message}{$br}";
2179         }
2180
2181         
// Iterate over the headers and stringify them
2182         
foreach ($this->headers as $name => $value) {
2183             if (
is_string($value))
2184                 
$str .= "{$name}{$value}{$br}";
2185
2186             elseif (
is_array($value)) {
2187                 foreach (
$value as $subval) {
2188                     
$str .= "{$name}{$subval}{$br}";
2189                 }
2190             }
2191         }
2192
2193         return 
$str;
2194     }
2195
2196     
/**
2197      * Get the entire response as string
2198      *
2199      * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
2200      * @return string
2201      */
2202     
public function asString($br "\n") {
2203         return 
$this->getHeadersAsString(true$br) . $br $this->getRawBody();
2204     }
2205
2206     
/**
2207      * Implements magic __toString()
2208      *
2209      * @return string
2210      */
2211     
public function __toString() {
2212         return 
$this->asString();
2213     }
2214
2215     
/**
2216      * A convenience function that returns a text representation of
2217      * HTTP response codes. Returns 'Unknown' for unknown codes.
2218      * Returns array of all codes, if $code is not specified.
2219      *
2220      * Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown')
2221      * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference
2222      *
2223      * @param int $code HTTP response code
2224      * @param boolean $http11 Use HTTP version 1.1
2225      * @return string
2226      */
2227     
public static function responseCodeAsText($code null$http11 true) {
2228         
$messages self::$messages;
2229         if (! 
$http11$messages[302] = 'Moved Temporarily';
2230
2231         if (
$code === null) {
2232             return 
$messages;
2233         } elseif (isset(
$messages[$code])) {
2234             return 
$messages[$code];
2235         } else {
2236             return 
'Unknown';
2237         }
2238     }
2239
2240     
/**
2241      * Extract the response code from a response string
2242      *
2243      * @param string $response_str
2244      * @return int
2245      */
2246     
public static function extractCode($response_str) {
2247         
preg_match("|^HTTP/[\d\.x]+ (\d+)|"$response_str$m);
2248
2249         if (isset(
$m[1])) {
2250             return (int) 
$m[1];
2251         } else {
2252             return 
false;
2253         }
2254     }
2255
2256     
/**
2257      * Extract the HTTP message from a response
2258      *
2259      * @param string $response_str
2260      * @return string
2261      */
2262     
public static function extractMessage($response_str) {
2263         
preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|"$response_str$m);
2264
2265         if (isset(
$m[1])) {
2266             return 
$m[1];
2267         } else {
2268             return 
false;
2269         }
2270     }
2271
2272     
/**
2273      * Extract the HTTP version from a response
2274      *
2275      * @param string $response_str
2276      * @return string
2277      */
2278     
public static function extractVersion($response_str) {
2279         
preg_match("|^HTTP/([\d\.x]+) \d+|"$response_str$m);
2280
2281         if (isset(
$m[1])) {
2282             return 
$m[1];
2283         } else {
2284             return 
false;
2285         }
2286     }
2287
2288     
/**
2289      * Extract the headers from a response string
2290      *
2291      * @param string $response_str
2292      * @return array
2293      */
2294     
public static function extractHeaders($response_str) {
2295         
$headers = array();
2296
2297         
// First, split body and headers
2298         
$parts preg_split('|(?:\r?\n){2}|m'$response_str2);
2299         if (! 
$parts[0]) return $headers;
2300
2301         
// Split headers part to lines
2302         
$lines explode("\n"$parts[0]);
2303         unset(
$parts);
2304         
$last_header null;
2305
2306         foreach(
$lines as $line) {
2307             
$line trim($line"\r\n");
2308             if (
$line == "") break;
2309
2310             if (
preg_match("|^([\w-]+):\s+(.+)|"$line$m)) {
2311                 unset(
$last_header);
2312                 
$h_name strtolower($m[1]);
2313                 
$h_value $m[2];
2314
2315                 if (isset(
$headers[$h_name])) {
2316                     if (! 
is_array($headers[$h_name])) {
2317                         
$headers[$h_name] = array($headers[$h_name]);
2318                     }
2319
2320                     
$headers[$h_name][] = $h_value;
2321                 } else {
2322                     
$headers[$h_name] = $h_value;
2323                 }
2324                 
$last_header $h_name;
2325             } elseif (
preg_match("|^\s+(.+)$|"$line$m) && $last_header !== null) {
2326                 if (
is_array($headers[$last_header])) {
2327                     
end($headers[$last_header]);
2328                     
$last_header_key key($headers[$last_header]);
2329                     
$headers[$last_header][$last_header_key] .= $m[1];
2330                 } else {
2331                     
$headers[$last_header] .= $m[1];
2332                 }
2333             }
2334         }
2335
2336         return 
$headers;
2337     }
2338
2339     
/**
2340      * Extract the body from a response string
2341      *
2342      * @param string $response_str
2343      * @return string
2344      */
2345     
public static function extractBody($response_str) {
2346         
$parts preg_split('|(?:\r?\n){2}|m'$response_str2);
2347         if (isset(
$parts[1])) {
2348             return 
$parts[1];
2349         }
2350         return 
'';
2351     }
2352
2353     
/**
2354      * Decode a "chunked" transfer-encoded body and return the decoded text
2355      *
2356      * @param string $body
2357      * @return string
2358      */
2359     
public static function decodeChunkedBody($body) {
2360         
$decBody '';
2361
2362         
// If mbstring overloads substr and strlen functions, we have to
2363         // override it's internal encoding
2364         
if (function_exists('mb_internal_encoding') &&
2365            ((int) 
ini_get('mbstring.func_overload')) & 2) {
2366
2367             
$mbIntEnc mb_internal_encoding();
2368             
mb_internal_encoding('ASCII');
2369         }
2370
2371         while (
trim($body)) {
2372             if (! 
preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm"$body$m)) {
2373                 if (!
class_exists('Zend_Exception')) require 'exception.php';
2374                 throw new 
Zend_Http_Exception("Error parsing body - doesn't seem to be a chunked message");
2375             }
2376
2377             
$length hexdec(trim($m[1]));
2378             
$cut strlen($m[0]);
2379             
$decBody .= substr($body$cut$length);
2380             
$body substr($body$cut $length 2);
2381         }
2382
2383         if (isset(
$mbIntEnc)) {
2384             
mb_internal_encoding($mbIntEnc);
2385         }
2386
2387         return 
$decBody;
2388     }
2389
2390     
/**
2391      * Decode a gzip encoded message (when Content-encoding = gzip)
2392      *
2393      * Currently requires PHP with zlib support
2394      *
2395      * @param string $body
2396      * @return string
2397      */
2398     
public static function decodeGzip($body) {
2399         if (! 
function_exists('gzinflate')) {
2400             if (!
class_exists('Zend_Exception')) require 'exception.php';
2401             throw new 
Zend_Http_Exception(
2402                 
'zlib extension is required in order to decode "gzip" encoding'
2403             
);
2404         }
2405
2406         return 
gzinflate(substr($body10));
2407     }
2408
2409     
/**
2410      * Decode a zlib deflated message (when Content-encoding = deflate)
2411      *
2412      * Currently requires PHP with zlib support
2413      *
2414      * @param string $body
2415      * @return string
2416      */
2417     
public static function decodeDeflate($body) {
2418         if (! 
function_exists('gzuncompress')) {
2419             if (!
class_exists('Zend_Exception')) require 'exception.php';
2420             throw new 
Zend_Http_Exception(
2421                 
'zlib extension is required in order to decode "deflate" encoding'
2422             
);
2423         }
2424
2425         
/**
2426          * Some servers (IIS ?) send a broken deflate response, without the
2427          * RFC-required zlib header.
2428          *
2429          * We try to detect the zlib header, and if it does not exsit we
2430          * teat the body is plain DEFLATE content.
2431          *
2432          * This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov
2433          *
2434          * @link http://framework.zend.com/issues/browse/ZF-6040
2435          */
2436         
$zlibHeader unpack('n'substr($body02));
2437         if (
$zlibHeader[1] % 31 == 0) {
2438             return 
gzuncompress($body);
2439         } else {
2440             return 
gzinflate($body);
2441         }
2442     }
2443
2444     
/**
2445      * Create a new Zend_Http_Response object from a string
2446      *
2447      * @param string $response_str
2448      * @return Zend_Http_Response
2449      */
2450     
public static function fromString($response_str) {
2451         
$code    self::extractCode($response_str);
2452         
$headers self::extractHeaders($response_str);
2453         
$body    self::extractBody($response_str);
2454         
$version self::extractVersion($response_str);
2455         
$message self::extractMessage($response_str);
2456
2457         return new 
Zend_Http_Response($code$headers$body$version$message);
2458     }
2459 }
2460
2461
/**
2462  * Zend_Http_Client is an implemetation of an HTTP client in PHP. The client
2463  * supports basic features like sending different HTTP requests and handling
2464  * redirections, as well as more advanced features like proxy settings, HTTP
2465  * authentication and cookie persistance (using a Zend_Http_CookieJar object)
2466  *
2467  * @todo Implement proxy settings
2468  * @category   Zend
2469  * @package    Zend_Http
2470  * @subpackage Client
2471  * @throws     Zend_Http_Client_Exception
2472  * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
2473  * @version    $Id: Client.php 21952 2010-04-19 18:44:26Z shahar $
2474  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
2475  * @license    http://framework.zend.com/license/new-bsd     New BSD License
2476  */
2477
class Zend_Http_Client {
2478     
/**
2479      * HTTP request methods
2480      */
2481     
const GET     'GET';
2482     const 
POST    'POST';
2483     const 
PUT     'PUT';
2484     const 
HEAD    'HEAD';
2485     const 
DELETE  'DELETE';
2486     const 
TRACE   'TRACE';
2487     const 
OPTIONS 'OPTIONS';
2488     const 
CONNECT 'CONNECT';
2489     const 
MERGE   'MERGE';
2490
2491     
/**
2492      * Supported HTTP Authentication methods
2493      */
2494     
const AUTH_BASIC 'basic';
2495     
//const AUTH_DIGEST = 'digest'; <-- not implemented yet
2496
2497     /**
2498      * HTTP protocol versions
2499      */
2500     
const HTTP_1 '1.1';
2501     const 
HTTP_0 '1.0';
2502
2503     
/**
2504      * Content attributes
2505      */
2506     
const CONTENT_TYPE   'Content-Type';
2507     const 
CONTENT_LENGTH 'Content-Length';
2508
2509     
/**
2510      * POST data encoding methods
2511      */
2512     
const ENC_URLENCODED 'application/x-www-form-urlencoded';
2513     const 
ENC_FORMDATA   'multipart/form-data';
2514
2515     
/**
2516      * Configuration array, set using the constructor or using ::setConfig()
2517      *
2518      * @var array
2519      */
2520     
protected $config = array(
2521         
'maxredirects'    => 5,
2522         
'strictredirects' => false,
2523         
'useragent'       => 'Zend_Http_Client',
2524         
'timeout'         => 10,
2525         
'adapter'         => 'Zend_Http_Client_Adapter_Socket',
2526         
'httpversion'     => self::HTTP_1,
2527         
'keepalive'       => false,
2528         
'storeresponse'   => true,
2529         
'strict'          => true,
2530         
'output_stream'   => false,
2531         
'encodecookies'   => true,
2532     );
2533
2534     
/**
2535      * The adapter used to preform the actual connection to the server
2536      *
2537      * @var Zend_Http_Client_Adapter_Interface
2538      */
2539     
protected $adapter null;
2540
2541     
/**
2542      * Request URI
2543      *
2544      * @var Zend_Uri_Http
2545      */
2546     
protected $uri null;
2547
2548     
/**
2549      * Associative array of request headers
2550      *
2551      * @var array
2552      */
2553     
protected $headers = array();
2554
2555     
/**
2556      * HTTP request method
2557      *
2558      * @var string
2559      */
2560     
protected $method self::GET;
2561
2562     
/**
2563      * Associative array of GET parameters
2564      *
2565      * @var array
2566      */
2567     
protected $paramsGet = array();
2568
2569     
/**
2570      * Assiciative array of POST parameters
2571      *
2572      * @var array
2573      */
2574     
protected $paramsPost = array();
2575
2576     
/**
2577      * Request body content type (for POST requests)
2578      *
2579      * @var string
2580      */
2581     
protected $enctype null;
2582
2583     
/**
2584      * The raw post data to send. Could be set by setRawData($data, $enctype).
2585      *
2586      * @var string
2587      */
2588     
protected $raw_post_data null;
2589
2590     
/**
2591      * HTTP Authentication settings
2592      *
2593      * Expected to be an associative array with this structure:
2594      * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
2595      * Where 'type' should be one of the supported authentication types (see the AUTH_*
2596      * constants), for example 'basic' or 'digest'.
2597      *
2598      * If null, no authentication will be used.
2599      *
2600      * @var array|null
2601      */
2602     
protected $auth;
2603
2604     
/**
2605      * File upload arrays (used in POST requests)
2606      *
2607      * An associative array, where each element is of the format:
2608      *   'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
2609      *
2610      * @var array
2611      */
2612     
protected $files = array();
2613
2614     
/**
2615      * The client's cookie jar
2616      *
2617      * @var Zend_Http_CookieJar
2618      */
2619     
protected $cookiejar null;
2620
2621     
/**
2622      * The last HTTP request sent by the client, as string
2623      *
2624      * @var string
2625      */
2626     
protected $last_request null;
2627
2628     
/**
2629      * The last HTTP response received by the client
2630      *
2631      * @var Zend_Http_Response
2632      */
2633     
protected $last_response null;
2634
2635     
/**
2636      * Redirection counter
2637      *
2638      * @var int
2639      */
2640     
protected $redirectCounter 0;
2641
2642     
/**
2643      * Fileinfo magic database resource
2644      *
2645      * This varaiable is populated the first time _detectFileMimeType is called
2646      * and is then reused on every call to this method
2647      *
2648      * @var resource
2649      */
2650     
static protected $_fileInfoDb null;
2651
2652     
/**
2653      * Contructor method. Will create a new HTTP client. Accepts the target
2654      * URL and optionally configuration array.
2655      *
2656      * @param Zend_Uri_Http|string $uri
2657      * @param array $config Configuration key-value pairs.
2658      */
2659     
public function __construct($uri null$config null) {
2660         if (
$uri !== null) {
2661             
$this->setUri($uri);
2662         }
2663         if (
$config !== null) {
2664             
$this->setConfig($config);
2665         }
2666     }
2667
2668     
/**
2669      * Set the URI for the next request
2670      *
2671      * @param  Zend_Uri_Http|string $uri
2672      * @return Zend_Http_Client
2673      * @throws Zend_Http_Client_Exception
2674      */
2675     
public function setUri($uri) {
2676         if (
is_string($uri)) {
2677             
$uri Zend_Uri::factory($uri);
2678         }
2679
2680         if (!
$uri instanceof Zend_Uri_Http) {
2681             
/** @see Zend_Http_Client_Exception */
2682             
if (!class_exists('Zend_Exception')) require 'exception.php';
2683             throw new 
Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
2684         }
2685
2686         
// Set auth if username and password has been specified in the uri
2687         
if ($uri->getUsername() && $uri->getPassword()) {
2688             
$this->setAuth($uri->getUsername(), $uri->getPassword());
2689         }
2690
2691         
// We have no ports, set the defaults
2692         
if (! $uri->getPort()) {
2693             
$uri->setPort(($uri->getScheme() == 'https' 443 80));
2694         }
2695
2696         
$this->uri $uri;
2697
2698         return 
$this;
2699     }
2700
2701     
/**
2702      * Get the URI for the next request
2703      *
2704      * @param boolean $as_string If true, will return the URI as a string
2705      * @return Zend_Uri_Http|string
2706      */
2707     
public function getUri($as_string false) {
2708         if (
$as_string && $this->uri instanceof Zend_Uri_Http) {
2709             return 
$this->uri->__toString();
2710         } else {
2711             return 
$this->uri;
2712         }
2713     }
2714
2715     
/**
2716      * Set configuration parameters for this HTTP client
2717      *
2718      * @param  Zend_Config | array $config
2719      * @return Zend_Http_Client
2720      * @throws Zend_Http_Client_Exception
2721      */
2722     
public function setConfig($config = array()) {
2723         if (
$config instanceof Zend_Config) {
2724             
$config $config->toArray();
2725
2726         } elseif (! 
is_array($config)) {
2727             
/** @see Zend_Http_Client_Exception */
2728             
if (!class_exists('Zend_Exception')) require 'exception.php';
2729             throw new 
Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' .
 
gettype($config));
2730         }
2731
2732         foreach (
$config as $k => $v) {
2733             
$this->config[strtolower($k)] = $v;
2734         }
2735
2736         
// Pass configuration options to the adapter if it exists
2737         
if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
2738             
$this->adapter->setConfig($config);
2739         }
2740
2741         return 
$this;
2742     }
2743
2744     
/**
2745      * Set the next request's method
2746      *
2747      * Validated the passed method and sets it. If we have files set for
2748      * POST requests, and the new method is not POST, the files are silently
2749      * dropped.
2750      *
2751      * @param string $method
2752      * @return Zend_Http_Client
2753      * @throws Zend_Http_Client_Exception
2754      */
2755     
public function setMethod($method self::GET) {
2756         if (! 
preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/'$method)) {
2757             
/** @see Zend_Http_Client_Exception */
2758             
if (!class_exists('Zend_Exception')) require 'exception.php';
2759             throw new 
Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
2760         }
2761
2762         if (
$method == self::POST && $this->enctype === null) {
2763             
$this->setEncType(self::ENC_URLENCODED);
2764         }
2765
2766         
$this->method $method;
2767
2768         return 
$this;
2769     }
2770
2771     
/**
2772      * Set one or more request headers
2773      *
2774      * This function can be used in several ways to set the client's request
2775      * headers:
2776      * 1. By providing two parameters: $name as the header to set (eg. 'Host')
2777      *    and $value as it's value (eg. 'www.example.com').
2778      * 2. By providing a single header string as the only parameter
2779      *    eg. 'Host: www.example.com'
2780      * 3. By providing an array of headers as the first parameter
2781      *    eg. array('host' => 'www.example.com', 'x-foo: bar'). In This case
2782      *    the function will call itself recursively for each array item.
2783      *
2784      * @param string|array $name Header name, full header string ('Header: value')
2785      *     or an array of headers
2786      * @param mixed $value Header value or null
2787      * @return Zend_Http_Client
2788      * @throws Zend_Http_Client_Exception
2789      */
2790     
public function setHeaders($name$value null) {
2791         
// If we got an array, go recusive!
2792         
if (is_array($name)) {
2793             foreach (
$name as $k => $v) {
2794                 if (
is_string($k)) {
2795                     
$this->setHeaders($k$v);
2796                 } else {
2797                     
$this->setHeaders($vnull);
2798                 }
2799             }
2800         } else {
2801             
// Check if $name needs to be split
2802             
if ($value === null && (strpos($name':') > 0)) {
2803                 list(
$name$value) = explode(':'$name2);
2804             }
2805
2806             
// Make sure the name is valid if we are in strict mode
2807             
if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/'$name))) {
2808                 
/** @see Zend_Http_Client_Exception */
2809                 
if (!class_exists('Zend_Exception')) require 'exception.php';
2810                 throw new 
Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
2811             }
2812
2813             
$normalized_name strtolower($name);
2814
2815             
// If $value is null or false, unset the header
2816             
if ($value === null || $value === false) {
2817                 unset(
$this->headers[$normalized_name]);
2818
2819             
// Else, set the header
2820             
} else {
2821                 
// Header names are stored lowercase internally.
2822                 
if (is_string($value)) {
2823                     
$value trim($value);
2824                 }
2825                 
$this->headers[$normalized_name] = array($name$value);
2826             }
2827         }
2828
2829         return 
$this;
2830     }
2831
2832     
/**
2833      * Get the value of a specific header
2834      *
2835      * Note that if the header has more than one value, an array
2836      * will be returned.
2837      *
2838      * @param string $key
2839      * @return string|array|null The header value or null if it is not set
2840      */
2841     
public function getHeader($key) {
2842         
$key strtolower($key);
2843         if (isset(
$this->headers[$key])) {
2844             return 
$this->headers[$key][1];
2845         } else {
2846             return 
null;
2847         }
2848     }
2849
2850     
/**
2851      * Set a GET parameter for the request. Wrapper around _setParameter
2852      *
2853      * @param string|array $name
2854      * @param string $value
2855      * @return Zend_Http_Client
2856      */
2857     
public function setParameterGet($name$value null) {
2858         if (
is_array($name)) {
2859             foreach (
$name as $k => $v)
2860                 
$this->_setParameter('GET'$k$v);
2861         } else {
2862             
$this->_setParameter('GET'$name$value);
2863         }
2864
2865         return 
$this;
2866     }
2867
2868     
/**
2869      * Set a POST parameter for the request. Wrapper around _setParameter
2870      *
2871      * @param string|array $name
2872      * @param string $value
2873      * @return Zend_Http_Client
2874      */
2875     
public function setParameterPost($name$value null) {
2876         if (
is_array($name)) {
2877             foreach (
$name as $k => $v)
2878                 
$this->_setParameter('POST'$k$v);
2879         } else {
2880             
$this->_setParameter('POST'$name$value);
2881         }
2882
2883         return 
$this;
2884     }
2885
2886     
/**
2887      * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
2888      *
2889      * @param string $type GET or POST
2890      * @param string $name
2891      * @param string $value
2892      * @return null
2893      */
2894     
protected function _setParameter($type$name$value) {
2895         
$parray = array();
2896         
$type strtolower($type);
2897         switch (
$type) {
2898             case 
'get':
2899                 
$parray = &$this->paramsGet;
2900                 break;
2901             case 
'post':
2902                 
$parray = &$this->paramsPost;
2903                 break;
2904         }
2905
2906         if (
$value === null) {
2907             if (isset(
$parray[$name])) unset($parray[$name]);
2908         } else {
2909             
$parray[$name] = $value;
2910         }
2911     }
2912
2913     
/**
2914      * Get the number of redirections done on the last request
2915      *
2916      * @return int
2917      */
2918     
public function getRedirectionsCount() {
2919         return 
$this->redirectCounter;
2920     }
2921
2922     
/**
2923      * Set HTTP authentication parameters
2924      *
2925      * $type should be one of the supported types - see the self::AUTH_*
2926      * constants.
2927      *
2928      * To enable authentication:
2929      * <code>
2930      * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
2931      * </code>
2932      *
2933      * To disable authentication:
2934      * <code>
2935      * $this->setAuth(false);
2936      * </code>
2937      *
2938      * @see http://www.faqs.org/rfcs/rfc2617.html
2939      * @param string|false $user User name or false disable authentication
2940      * @param string $password Password
2941      * @param string $type Authentication type
2942      * @return Zend_Http_Client
2943      * @throws Zend_Http_Client_Exception
2944      */
2945     
public function setAuth($user$password ''$type self::AUTH_BASIC) {
2946         
// If we got false or null, disable authentication
2947         
if ($user === false || $user === null) {
2948             
$this->auth null;
2949
2950             
// Clear the auth information in the uri instance as well
2951             
if ($this->uri instanceof Zend_Uri_Http) {
2952                 
$this->getUri()->setUsername('');
2953                 
$this->getUri()->setPassword('');
2954             }
2955         
// Else, set up authentication
2956         
} else {
2957             
// Check we got a proper authentication type
2958             
if (! defined('self::AUTH_' strtoupper($type))) {
2959                 
/** @see Zend_Http_Client_Exception */
2960                 
if (!class_exists('Zend_Exception')) require 'exception.php';
2961                 throw new 
Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
2962             }
2963
2964             
$this->auth = array(
2965                 
'user' => (string) $user,
2966                 
'password' => (string) $password,
2967                 
'type' => $type
2968             
);
2969         }
2970
2971         return 
$this;
2972     }
2973
2974     
/**
2975      * Return the current cookie jar or null if none.
2976      *
2977      * @return Zend_Http_CookieJar|null
2978      */
2979     
public function getCookieJar() {
2980         return 
$this->cookiejar;
2981     }
2982
2983     
/**
2984      * Set a file to upload (using a POST request)
2985      *
2986      * Can be used in two ways:
2987      *
2988      * 1. $data is null (default): $filename is treated as the name if a local file which
2989      *    will be read and sent. Will try to guess the content type using mime_content_type().
2990      * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
2991      *    contents and no file is read from the file system. In this case, you need to
2992      *    manually set the Content-Type ($ctype) or it will default to
2993      *    application/octet-stream.
2994      *
2995      * @param string $filename Name of file to upload, or name to save as
2996      * @param string $formname Name of form element to send as
2997      * @param string $data Data to send (if null, $filename is read and sent)
2998      * @param string $ctype Content type to use (if $data is set and $ctype is
2999      *     null, will be application/octet-stream)
3000      * @return Zend_Http_Client
3001      * @throws Zend_Http_Client_Exception
3002      */
3003     
public function setFileUpload($filename$formname$data null$ctype null) {
3004         if (
$data === null) {
3005             if ((
$data = @file_get_contents($filename)) === false) {
3006                 
/** @see Zend_Http_Client_Exception */
3007                 
if (!class_exists('Zend_Exception')) require 'exception.php';
3008                 throw new 
Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
3009             }
3010
3011             if (! 
$ctype) {
3012                 
$ctype $this->_detectFileMimeType($filename);
3013             }
3014         }
3015
3016         
// Force enctype to multipart/form-data
3017         
$this->setEncType(self::ENC_FORMDATA);
3018
3019         
$this->files[] = array(
3020             
'formname' => $formname,
3021             
'filename' => basename($filename),
3022             
'ctype'    => $ctype,
3023             
'data'     => $data
3024         
);
3025
3026         return 
$this;
3027     }
3028
3029     
/**
3030      * Set the encoding type for POST data
3031      *
3032      * @param string $enctype
3033      * @return Zend_Http_Client
3034      */
3035     
public function setEncType($enctype self::ENC_URLENCODED) {
3036         
$this->enctype $enctype;
3037
3038         return 
$this;
3039     }
3040
3041     
/**
3042      * Set the raw (already encoded) POST data.
3043      *
3044      * This function is here for two reasons:
3045      * 1. For advanced user who would like to set their own data, already encoded
3046      * 2. For backwards compatibilty: If someone uses the old post($data) method.
3047      *    this method will be used to set the encoded data.
3048      *
3049      * $data can also be stream (such as file) from which the data will be read.
3050      *
3051      * @param string|resource $data
3052      * @param string $enctype
3053      * @return Zend_Http_Client
3054      */
3055     
public function setRawData($data$enctype null) {
3056         
$this->raw_post_data $data;
3057         
$this->setEncType($enctype);
3058         if (
is_resource($data)) {
3059             
// We've got stream data
3060             
$stat = @fstat($data);
3061             if(
$stat) {
3062                 
$this->setHeaders(self::CONTENT_LENGTH$stat['size']);
3063             }
3064         }
3065         return 
$this;
3066     }
3067
3068     
/**
3069      * Clear all GET and POST parameters
3070      *
3071      * Should be used to reset the request parameters if the client is
3072      * used for several concurrent requests.
3073      *
3074      * clearAll parameter controls if we clean just parameters or also
3075      * headers and last_*
3076      *
3077      * @param bool $clearAll Should all data be cleared?
3078      * @return Zend_Http_Client
3079      */
3080     
public function resetParameters($clearAll false) {
3081         
// Reset parameter data
3082         
$this->paramsGet     = array();
3083         
$this->paramsPost    = array();
3084         
$this->files         = array();
3085         
$this->raw_post_data null;
3086
3087         if(
$clearAll) {
3088             
$this->headers = array();
3089             
$this->last_request null;
3090             
$this->last_response null;
3091         } else {
3092             
// Clear outdated headers
3093             
if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) {
3094                 unset(
$this->headers[strtolower(self::CONTENT_TYPE)]);
3095             }
3096             if (isset(
$this->headers[strtolower(self::CONTENT_LENGTH)])) {
3097                 unset(
$this->headers[strtolower(self::CONTENT_LENGTH)]);
3098             }
3099         }
3100
3101         return 
$this;
3102     }
3103
3104     
/**
3105      * Get the last HTTP request as string
3106      *
3107      * @return string
3108      */
3109     
public function getLastRequest() {
3110         return 
$this->last_request;
3111     }
3112
3113     
/**
3114      * Get the last HTTP response received by this client
3115      *
3116      * If $config['storeresponse'] is set to false, or no response was
3117      * stored yet, will return null
3118      *
3119      * @return Zend_Http_Response or null if none
3120      */
3121     
public function getLastResponse() {
3122         return 
$this->last_response;
3123     }
3124
3125     
/**
3126      * Load the connection adapter
3127      *
3128      * While this method is not called more than one for a client, it is
3129      * seperated from ->request() to preserve logic and readability
3130      *
3131      * @param Zend_Http_Client_Adapter_Interface|string $adapter
3132      * @return null
3133      * @throws Zend_Http_Client_Exception
3134      */
3135     
public function setAdapter($adapter) {
3136         if (
is_string($adapter)) {
3137             if (!
class_exists($adapter)) {
3138                 try {
3139                     require_once 
'Zend/Loader.php';
3140                     
Zend_Loader::loadClass($adapter);
3141                 } catch (
Zend_Exception $e) {
3142                     
/** @see Zend_Http_Client_Exception */
3143                     
if (!class_exists('Zend_Exception')) require 'exception.php';
3144                     throw new 
Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}",
 
0$e);
3145                 }
3146             }
3147
3148             
$adapter = new $adapter;
3149         }
3150
3151         if (! 
$adapter instanceof Zend_Http_Client_Adapter_Interface) {
3152             
/** @see Zend_Http_Client_Exception */
3153             
if (!class_exists('Zend_Exception')) require 'exception.php';
3154             throw new 
Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
3155         }
3156
3157         
$this->adapter $adapter;
3158         
$config $this->config;
3159         unset(
$config['adapter']);
3160         
$this->adapter->setConfig($config);
3161     }
3162
3163     
/**
3164      * Load the connection adapter
3165      *
3166      * @return Zend_Http_Client_Adapter_Interface $adapter
3167      */
3168     
public function getAdapter() {
3169         return 
$this->adapter;
3170     }
3171
3172     
/**
3173      * Set streaming for received data
3174      *
3175      * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming
3176      * @return Zend_Http_Client
3177      */
3178     
public function setStream($streamfile true) {
3179         
$this->setConfig(array("output_stream" => $streamfile));
3180         return 
$this;
3181     }
3182
3183     
/**
3184      * Get status of streaming for received data
3185      * @return boolean|string
3186      */
3187     
public function getStream() {
3188         return 
$this->config["output_stream"];
3189     }
3190
3191     
/**
3192      * Create temporary stream
3193      *
3194      * @return resource
3195      */
3196     
protected function _openTempStream() {
3197         
$this->_stream_name $this->config['output_stream'];
3198         if(!
is_string($this->_stream_name)) {
3199             
// If name is not given, create temp name
3200             
$this->_stream_name tempnam(isset($this->config['stream_tmp_dir']) ?
 
$this->config['stream_tmp_dir']
3201                                                                                  : 
sys_get_temp_dir(),
3202                                           
'Zend_Http_Client');
3203         }
3204
3205         if (
false === ($fp = @fopen($this->_stream_name"w+b"))) {
3206                 if (
$this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
3207                     
$this->adapter->close();
3208                 }
3209                 if (!
class_exists('Zend_Exception')) require 'exception.php';
3210                 throw new 
Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}");
3211         }
3212
3213         return 
$fp;
3214     }
3215
3216     
/**
3217      * Send the HTTP request and return an HTTP response object
3218      *
3219      * @param string $method
3220      * @return Zend_Http_Response
3221      * @throws Zend_Http_Client_Exception
3222      */
3223     
public function request($method null) {
3224         if (! 
$this->uri instanceof Zend_Uri_Http) {
3225             
/** @see Zend_Http_Client_Exception */
3226             
if (!class_exists('Zend_Exception')) require 'exception.php';
3227             throw new 
Zend_Http_Client_Exception('No valid URI has been passed to the client');
3228         }
3229
3230         if (
$method) {
3231             
$this->setMethod($method);
3232         }
3233         
$this->redirectCounter 0;
3234         
$response null;
3235
3236         
// Make sure the adapter is loaded
3237         
if ($this->adapter == null) {
3238             
$this->setAdapter($this->config['adapter']);
3239         }
3240
3241         
// Send the first request. If redirected, continue.
3242         
do {
3243             
// Clone the URI and add the additional GET parameters to it
3244             
$uri = clone $this->uri;
3245             if (! empty(
$this->paramsGet)) {
3246                 
$query $uri->getQuery();
3247                    if (! empty(
$query)) {
3248                        
$query .= '&';
3249                    }
3250                 
$query .= http_build_query($this->paramsGetnull'&');
3251
3252                 
$uri->setQuery($query);
3253             }
3254
3255             
$body $this->_prepareBody();
3256             
$headers $this->_prepareHeaders();
3257
3258             
// check that adapter supports streaming before using it
3259             
if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) {
3260                 
/** @see Zend_Http_Client_Exception */
3261                 
if (!class_exists('Zend_Exception')) require 'exception.php';
3262                 throw new 
Zend_Http_Client_Exception('Adapter does not support streaming');
3263             }
3264
3265             
// Open the connection, send the request and read the response
3266             
$this->adapter->connect($uri->getHost(), $uri->getPort(),
3267                 (
$uri->getScheme() == 'https' true false));
3268
3269             if(
$this->config['output_stream']) {
3270                 if(
$this->adapter instanceof Zend_Http_Client_Adapter_Stream) {
3271                     
$stream $this->_openTempStream();
3272                     
$this->adapter->setOutputStream($stream);
3273                 } else {
3274                     
/** @see Zend_Http_Client_Exception */
3275                     
if (!class_exists('Zend_Exception')) require 'exception.php';
3276                     throw new 
Zend_Http_Client_Exception('Adapter does not support streaming');
3277                 }
3278             }
3279
3280             
$this->last_request $this->adapter->write($this->method,
3281                 
$uri$this->config['httpversion'], $headers$body);
3282
3283             
$response $this->adapter->read();
3284             if (! 
$response) {
3285                 
/** @see Zend_Http_Client_Exception */
3286                 
if (!class_exists('Zend_Exception')) require 'exception.php';
3287                 throw new 
Zend_Http_Client_Exception('Unable to read response, or response is empty');
3288             }
3289
3290             if(
$this->config['output_stream']) {
3291                 
rewind($stream);
3292                 
// cleanup the adapter
3293                 
$this->adapter->setOutputStream(null);
3294                 
$response Zend_Http_Response_Stream::fromStream($response$stream);
3295                 
$response->setStreamName($this->_stream_name);
3296                 if(!
is_string($this->config['output_stream'])) {
3297                     
// we used temp name, will need to clean up
3298                     
$response->setCleanup(true);
3299                 }
3300             } else {
3301                 
$response Zend_Http_Response::fromString($response);
3302             }
3303
3304             if (
$this->config['storeresponse']) {
3305                 
$this->last_response $response;
3306             }
3307
3308             
// Load cookies into cookie jar
3309             
if (isset($this->cookiejar)) {
3310                 
$this->cookiejar->addCookiesFromResponse($response$uri);
3311             }
3312
3313             
// If we got redirected, look for the Location header
3314             
if ($response->isRedirect() && ($location $response->getHeader('location'))) {
3315
3316                 
// Check whether we send the exact same request again, or drop the parameters
3317                 // and send a GET request
3318                 
if ($response->getStatus() == 303 ||
3319                    ((! 
$this->config['strictredirects']) && ($response->getStatus() == 302 ||
3320                        
$response->getStatus() == 301))) {
3321
3322                     
$this->resetParameters();
3323                     
$this->setMethod(self::GET);
3324                 }
3325
3326                 
// If we got a well formed absolute URI
3327                 
if (Zend_Uri_Http::check($location)) {
3328                     
$this->setHeaders('host'null);
3329                     
$this->setUri($location);
3330
3331                 } else {
3332
3333                     
// Split into path and query and set the query
3334                     
if (strpos($location'?') !== false) {
3335                         list(
$location$query) = explode('?'$location2);
3336                     } else {
3337                         
$query '';
3338                     }
3339                     
$this->uri->setQuery($query);
3340
3341                     
// Else, if we got just an absolute path, set it
3342                     
if(strpos($location'/') === 0) {
3343                         
$this->uri->setPath($location);
3344
3345                         
// Else, assume we have a relative path
3346                     
} else {
3347                         
// Get the current path directory, removing any trailing slashes
3348                         
$path $this->uri->getPath();
3349                         
$path rtrim(substr($path0strrpos($path'/')), "/");
3350                         
$this->uri->setPath($path '/' $location);
3351                     }
3352                 }
3353                 ++
$this->redirectCounter;
3354
3355             } else {
3356                 
// If we didn't get any location, stop redirecting
3357                 
break;
3358             }
3359
3360         } while (
$this->redirectCounter $this->config['maxredirects']);
3361
3362         return 
$response;
3363     }
3364
3365     
/**
3366      * Prepare the request headers
3367      *
3368      * @return array
3369      */
3370     
protected function _prepareHeaders() {
3371         
$headers = array();
3372
3373         
// Set the host header
3374         
if (! isset($this->headers['host'])) {
3375             
$host $this->uri->getHost();
3376
3377             
// If the port is not default, add it
3378             
if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
3379                   (
$this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
3380                 
$host .= ':' $this->uri->getPort();
3381             }
3382
3383             
$headers[] = "Host: {$host}";
3384         }
3385
3386         
// Set the connection header
3387         
if (! isset($this->headers['connection'])) {
3388             if (! 
$this->config['keepalive']) {
3389                 
$headers[] = "Connection: close";
3390             }
3391         }
3392
3393         
// Set the Accept-encoding header if not set - depending on whether
3394         // zlib is available or not.
3395         
if (! isset($this->headers['accept-encoding'])) {
3396             if (
function_exists('gzinflate')) {
3397                 
$headers[] = 'Accept-encoding: gzip, deflate';
3398             } else {
3399                 
$headers[] = 'Accept-encoding: identity';
3400             }
3401         }
3402
3403         
// Set the Content-Type header
3404         
if ($this->method == self::POST &&
3405            (! isset(
$this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) {
3406
3407             
$headers[] = self::CONTENT_TYPE ': ' $this->enctype;
3408         }
3409
3410         
// Set the user agent header
3411         
if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
3412             
$headers[] = "User-Agent: {$this->config['useragent']}";
3413         }
3414
3415         
// Set HTTP authentication if needed
3416         
if (is_array($this->auth)) {
3417             
$auth self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
3418             
$headers[] = "Authorization: {$auth}";
3419         }
3420
3421         
// Load cookies from cookie jar
3422         
if (isset($this->cookiejar)) {
3423             
$cookstr $this->cookiejar->getMatchingCookies($this->uri,
3424                 
trueZend_Http_CookieJar::COOKIE_STRING_CONCAT);
3425
3426             if (
$cookstr) {
3427                 
$headers[] = "Cookie: {$cookstr}";
3428             }
3429         }
3430
3431         
// Add all other user defined headers
3432         
foreach ($this->headers as $header) {
3433             list(
$name$value) = $header;
3434             if (
is_array($value)) {
3435                 
$value implode(', '$value);
3436             }
3437
3438             
$headers[] = "$name$value";
3439         }
3440
3441         return 
$headers;
3442     }
3443
3444     
/**
3445      * Prepare the request body (for POST and PUT requests)
3446      *
3447      * @return string
3448      * @throws Zend_Http_Client_Exception
3449      */
3450     
protected function _prepareBody() {
3451         
// According to RFC2616, a TRACE request should not have a body.
3452         
if ($this->method == self::TRACE) {
3453             return 
'';
3454         }
3455
3456         if (isset(
$this->raw_post_data) && is_resource($this->raw_post_data)) {
3457             return 
$this->raw_post_data;
3458         }
3459         
// If mbstring overloads substr and strlen functions, we have to
3460         // override it's internal encoding
3461         
if (function_exists('mb_internal_encoding') &&
3462            ((int) 
ini_get('mbstring.func_overload')) & 2) {
3463
3464             
$mbIntEnc mb_internal_encoding();
3465             
mb_internal_encoding('ASCII');
3466         }
3467
3468         
// If we have raw_post_data set, just use it as the body.
3469         
if (isset($this->raw_post_data)) {
3470             
$this->setHeaders(self::CONTENT_LENGTHstrlen($this->raw_post_data));
3471             if (isset(
$mbIntEnc)) {
3472                 
mb_internal_encoding($mbIntEnc);
3473             }
3474
3475             return 
$this->raw_post_data;
3476         }
3477
3478         
$body '';
3479
3480         
// If we have files to upload, force enctype to multipart/form-data
3481         
if (count ($this->files) > 0) {
3482             
$this->setEncType(self::ENC_FORMDATA);
3483         }
3484
3485         
// If we have POST parameters or files, encode and add them to the body
3486         
if (count($this->paramsPost) > || count($this->files) > 0) {
3487             switch(
$this->enctype) {
3488                 case 
self::ENC_FORMDATA:
3489                     
// Encode body as multipart/form-data
3490                     
$boundary '---ZENDHTTPCLIENT-' md5(microtime());
3491                     
$this->setHeaders(self::CONTENT_TYPEself::ENC_FORMDATA "; boundary={$boundary}");
3492
3493                     
// Get POST parameters and encode them
3494                     
$params self::_flattenParametersArray($this->paramsPost);
3495                     foreach (
$params as $pp) {
3496                         
$body .= self::encodeFormData($boundary$pp[0], $pp[1]);
3497                     }
3498
3499                     
// Encode files
3500                     
foreach ($this->files as $file) {
3501                         
$fhead = array(self::CONTENT_TYPE => $file['ctype']);
3502                         
$body .= self::encodeFormData($boundary$file['formname'], $file['data'],
 
$file['filename'], $fhead);
3503                     }
3504
3505                     
$body .= "--{$boundary}--\r\n";
3506                     break;
3507
3508                 case 
self::ENC_URLENCODED:
3509                     
// Encode body as application/x-www-form-urlencoded
3510                     
$this->setHeaders(self::CONTENT_TYPEself::ENC_URLENCODED);
3511                     
$body http_build_query($this->paramsPost'''&');
3512                     break;
3513
3514                 default:
3515                     if (isset(
$mbIntEnc)) {
3516                         
mb_internal_encoding($mbIntEnc);
3517                     }
3518
3519                     
/** @see Zend_Http_Client_Exception */
3520                     
if (!class_exists('Zend_Exception')) require 'exception.php';
3521                     throw new 
Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}'
 automatically."
3522                         
" Please use Zend_Http_Client::setRawData to send this kind of content.");
3523                     break;
3524             }
3525         }
3526
3527         
// Set the Content-Length if we have a body or if request is POST/PUT
3528         
if ($body || $this->method == self::POST || $this->method == self::PUT) {
3529             
$this->setHeaders(self::CONTENT_LENGTHstrlen($body));
3530         }
3531
3532         if (isset(
$mbIntEnc)) {
3533             
mb_internal_encoding($mbIntEnc);
3534         }
3535
3536         return 
$body;
3537     }
3538
3539     
/**
3540      * Helper method that gets a possibly multi-level parameters array (get or
3541      * post) and flattens it.
3542      *
3543      * The method returns an array of (key, value) pairs (because keys are not
3544      * necessarily unique. If one of the parameters in as array, it will also
3545      * add a [] suffix to the key.
3546      *
3547      * This method is deprecated since Zend Framework 1.9 in favour of
3548      * self::_flattenParametersArray() and will be dropped in 2.0
3549      *
3550      * @deprecated since 1.9
3551      *
3552      * @param  array $parray    The parameters array
3553      * @param  bool  $urlencode Whether to urlencode the name and value
3554      * @return array
3555      */
3556     
protected function _getParametersRecursive($parray$urlencode false) {
3557         
// Issue a deprecated notice
3558         
trigger_error("The " .  __METHOD__ " method is deprecated and will be dropped in 2.0.",
3559             
E_USER_NOTICE);
3560
3561         if (! 
is_array($parray)) {
3562             return 
$parray;
3563         }
3564         
$parameters = array();
3565
3566         foreach (
$parray as $name => $value) {
3567             if (
$urlencode) {
3568                 
$name urlencode($name);
3569             }
3570
3571             
// If $value is an array, iterate over it
3572             
if (is_array($value)) {
3573                 
$name .= ($urlencode '%5B%5D' '[]');
3574                 foreach (
$value as $subval) {
3575                     if (
$urlencode) {
3576                         
$subval urlencode($subval);
3577                     }
3578                     
$parameters[] = array($name$subval);
3579                 }
3580             } else {
3581                 if (
$urlencode) {
3582                     
$value urlencode($value);
3583                 }
3584                 
$parameters[] = array($name$value);
3585             }
3586         }
3587
3588         return 
$parameters;
3589     }
3590
3591     
/**
3592      * Attempt to detect the MIME type of a file using available extensions
3593      *
3594      * This method will try to detect the MIME type of a file. If the fileinfo
3595      * extension is available, it will be used. If not, the mime_magic
3596      * extension which is deprected but is still available in many PHP setups
3597      * will be tried.
3598      *
3599      * If neither extension is available, the default application/octet-stream
3600      * MIME type will be returned
3601      *
3602      * @param  string $file File path
3603      * @return string       MIME type
3604      */
3605     
protected function _detectFileMimeType($file) {
3606         
$type null;
3607
3608         
// First try with fileinfo functions
3609         
if (function_exists('finfo_open')) {
3610             if (
self::$_fileInfoDb === null) {
3611                 
self::$_fileInfoDb = @finfo_open(FILEINFO_MIME);
3612             }
3613
3614             if (
self::$_fileInfoDb) {
3615                 
$type finfo_file(self::$_fileInfoDb$file);
3616             }
3617
3618         } elseif (
function_exists('mime_content_type')) {
3619             
$type mime_content_type($file);
3620         }
3621
3622         
// Fallback to the default application/octet-stream
3623         
if (! $type) {
3624             
$type 'application/octet-stream';
3625         }
3626
3627         return 
$type;
3628     }
3629
3630     
/**
3631      * Encode data to a multipart/form-data part suitable for a POST request.
3632      *
3633      * @param string $boundary
3634      * @param string $name
3635      * @param mixed $value
3636      * @param string $filename
3637      * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" =>
 
"binary")
3638      * @return 
string
3639      
*/
3640     public static function 
encodeFormData($boundary$name$value$filename null$headers = array()) {
3641         
$ret "--{$boundary}\r\n" .
3642             
'Content-Disposition: form-data; name="' $name .'"';
3643
3644         if (
$filename) {
3645             
$ret .= '; filename="' $filename '"';
3646         }
3647         
$ret .= "\r\n";
3648
3649         foreach (
$headers as $hname => $hvalue) {
3650             
$ret .= "{$hname}{$hvalue}\r\n";
3651         }
3652         
$ret .= "\r\n";
3653
3654         
$ret .= "{$value}\r\n";
3655
3656         return 
$ret;
3657     }
3658
3659     
/**
3660      * Create a HTTP authentication "Authorization:" header according to the
3661      * specified user, password and authentication method.
3662      *
3663      * @see http://www.faqs.org/rfcs/rfc2617.html
3664      * @param string $user
3665      * @param string $password
3666      * @param string $type
3667      * @return string
3668      * @throws Zend_Http_Client_Exception
3669      */
3670     
public static function encodeAuthHeader($user$password$type self::AUTH_BASIC) {
3671         
$authHeader null;
3672
3673         switch (
$type) {
3674             case 
self::AUTH_BASIC:
3675                 
// In basic authentication, the user name cannot contain ":"
3676                 
if (strpos($user':') !== false) {
3677                     
/** @see Zend_Http_Client_Exception */
3678                     
if (!class_exists('Zend_Exception')) require 'exception.php';
3679                     throw new 
Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP
 authentication"
);
3680                 }
3681
3682                 
$authHeader 'Basic ' base64_encode($user ':' $password);
3683                 break;
3684
3685             
//case self::AUTH_DIGEST:
3686                 /**
3687                  * @todo Implement digest authentication
3688                  */
3689             //    break;
3690
3691             
default:
3692                 
/** @see Zend_Http_Client_Exception */
3693                 
if (!class_exists('Zend_Exception')) require 'exception.php';
3694                 throw new 
Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
3695         }
3696
3697         return 
$authHeader;
3698     }
3699
3700     
/**
3701      * Convert an array of parameters into a flat array of (key, value) pairs
3702      *
3703      * Will flatten a potentially multi-dimentional array of parameters (such
3704      * as POST parameters) into a flat array of (key, value) paris. In case
3705      * of multi-dimentional arrays, square brackets ([]) will be added to the
3706      * key to indicate an array.
3707      *
3708      * @since  1.9
3709      *
3710      * @param  array  $parray
3711      * @param  string $prefix
3712      * @return array
3713      */
3714     
static protected function _flattenParametersArray($parray$prefix null) {
3715         if (! 
is_array($parray)) {
3716             return 
$parray;
3717         }
3718
3719         
$parameters = array();
3720
3721         foreach(
$parray as $name => $value) {
3722
3723             
// Calculate array key
3724             
if ($prefix) {
3725                 if (
is_int($name)) {
3726                     
$key $prefix '[]';
3727                 } else {
3728                     
$key $prefix "[$name]";
3729                 }
3730             } else {
3731                 
$key $name;
3732             }
3733
3734             if (
is_array($value)) {
3735                 
$parameters array_merge($parametersself::_flattenParametersArray($value$key));
3736
3737             } else {
3738                 
$parameters[] = array($key$value);
3739             }
3740         }
3741
3742         return 
$parameters;
3743     }
3744
3745 }
3746
3747
/**
3748  * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters.
3749  *
3750  * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters. The
3751  * class also enables validating whether the cookie should be sent to the server in
3752  * a specified scenario according to the request URI, the expiry time and whether
3753  * session cookies should be used or not. Generally speaking cookies should be
3754  * contained in a Cookiejar object, or instantiated manually and added to an HTTP
3755  * request.
3756  *
3757  * See http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
3758  *
3759  * @category   Zend
3760  * @package    Zend_Http
3761  * @subpackage Cookie
3762  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
3763  * @version    $Id: Cookie.php 21020 2010-02-11 17:27:23Z shahar $
3764  * @license    http://framework.zend.com/license/new-bsd     New BSD License
3765  */
3766
class Zend_Http_Cookie {
3767     
/**
3768      * Cookie name
3769      *
3770      * @var string
3771      */
3772     
protected $name;
3773
3774     
/**
3775      * Cookie value
3776      *
3777      * @var string
3778      */
3779     
protected $value;
3780
3781     
/**
3782      * Cookie expiry date
3783      *
3784      * @var int
3785      */
3786     
protected $expires;
3787
3788     
/**
3789      * Cookie domain
3790      *
3791      * @var string
3792      */
3793     
protected $domain;
3794
3795     
/**
3796      * Cookie path
3797      *
3798      * @var string
3799      */
3800     
protected $path;
3801
3802     
/**
3803      * Whether the cookie is secure or not
3804      *
3805      * @var boolean
3806      */
3807     
protected $secure;
3808
3809     
/**
3810      * Whether the cookie value has been encoded/decoded
3811      *
3812      * @var boolean
3813      */
3814     
protected $encodeValue;
3815
3816     
/**
3817      * Cookie object constructor
3818      *
3819      * @todo Add validation of each one of the parameters (legal domain, etc.)
3820      *
3821      * @param string $name
3822      * @param string $value
3823      * @param string $domain
3824      * @param int $expires
3825      * @param string $path
3826      * @param bool $secure
3827      */
3828     
public function __construct($name$value$domain$expires null$path null$secure false) {
3829         if (
preg_match("/[=,; \t\r\n\013\014]/"$name)) {
3830             if (!
class_exists('Zend_Exception')) require 'exception.php';
3831             throw new 
Zend_Http_Exception("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014
 (
{$name})");
3832         }
3833
3834         if (! 
$this->name = (string) $name) {
3835             if (!
class_exists('Zend_Exception')) require 'exception.php';
3836             throw new 
Zend_Http_Exception('Cookies must have a name');
3837         }
3838
3839         if (! 
$this->domain = (string) $domain) {
3840             if (!
class_exists('Zend_Exception')) require 'exception.php';
3841             throw new 
Zend_Http_Exception('Cookies must have a domain');
3842         }
3843
3844         
$this->value = (string) $value;
3845         
$this->expires = ($expires === null null : (int) $expires);
3846         
$this->path = ($path $path '/');
3847         
$this->secure $secure;
3848     }
3849
3850     
/**
3851      * Get Cookie name
3852      *
3853      * @return string
3854      */
3855     
public function getName() {
3856         return 
$this->name;
3857     }
3858
3859     
/**
3860      * Get cookie value
3861      *
3862      * @return string
3863      */
3864     
public function getValue() {
3865         return 
$this->value;
3866     }
3867
3868     
/**
3869      * Get cookie domain
3870      *
3871      * @return string
3872      */
3873     
public function getDomain() {
3874         return 
$this->domain;
3875     }
3876
3877     
/**
3878      * Get the cookie path
3879      *
3880      * @return string
3881      */
3882     
public function getPath() {
3883         return 
$this->path;
3884     }
3885
3886     
/**
3887      * Get the expiry time of the cookie, or null if no expiry time is set
3888      *
3889      * @return int|null
3890      */
3891     
public function getExpiryTime() {
3892         return 
$this->expires;
3893     }
3894
3895     
/**
3896      * Check whether the cookie should only be sent over secure connections
3897      *
3898      * @return boolean
3899      */
3900     
public function isSecure() {
3901         return 
$this->secure;
3902     }
3903
3904     
/**
3905      * Check whether the cookie has expired
3906      *
3907      * Always returns false if the cookie is a session cookie (has no expiry time)
3908      *
3909      * @param int $now Timestamp to consider as "now"
3910      * @return boolean
3911      */
3912     
public function isExpired($now null) {
3913         if (
$now === null$now time();
3914         if (
is_int($this->expires) && $this->expires $now) {
3915             return 
true;
3916         } else {
3917             return 
false;
3918         }
3919     }
3920
3921     
/**
3922      * Check whether the cookie is a session cookie (has no expiry time set)
3923      *
3924      * @return boolean
3925      */
3926     
public function isSessionCookie() {
3927         return (
$this->expires === null);
3928     }
3929
3930     
/**
3931      * Checks whether the cookie should be sent or not in a specific scenario
3932      *
3933      * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
3934      * @param boolean $matchSessionCookies Whether to send session cookies
3935      * @param int $now Override the current time when checking for expiry time
3936      * @return boolean
3937      */
3938     
public function match($uri$matchSessionCookies true$now null) {
3939         if (
is_string ($uri)) {
3940             
$uri Zend_Uri_Http::factory($uri);
3941         }
3942
3943         
// Make sure we have a valid Zend_Uri_Http object
3944         
if (! ($uri->valid() && ($uri->getScheme() == 'http' || $uri->getScheme() == 'https'))) {
3945             if (!
class_exists('Zend_Exception')) require 'exception.php';
3946             throw new 
Zend_Http_Exception('Passed URI is not a valid HTTP or HTTPS URI');
3947         }
3948
3949         
// Check that the cookie is secure (if required) and not expired
3950         
if ($this->secure && $uri->getScheme() != 'https') return false;
3951         if (
$this->isExpired($now)) return false;
3952         if (
$this->isSessionCookie() && ! $matchSessionCookies) return false;
3953
3954         
// Check if the domain matches
3955         
if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
3956             return 
false;
3957         }
3958
3959         
// Check that path matches using prefix match
3960         
if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
3961             return 
false;
3962         }
3963
3964         
// If we didn't die until now, return true.
3965         
return true;
3966     }
3967
3968     
/**
3969      * Get the cookie as a string, suitable for sending as a "Cookie" header in an
3970      * HTTP request
3971      *
3972      * @return string
3973      */
3974     
public function __toString() {
3975         if (
$this->encodeValue) {
3976             return 
$this->name '=' urlencode($this->value) . ';';
3977         }
3978         return 
$this->name '=' $this->value ';';
3979     }
3980
3981     
/**
3982      * Generate a new Cookie object from a cookie string
3983      * (for example the value of the Set-Cookie HTTP header)
3984      *
3985      * @param string $cookieStr
3986      * @param Zend_Uri_Http|string $refUri Reference URI for default values (domain, path)
3987      * @param boolean $encodeValue Weither or not the cookie's value should be
3988      *                             passed through urlencode/urldecode
3989      * @return Zend_Http_Cookie A new Zend_Http_Cookie object or false on failure.
3990      */
3991     
public static function fromString($cookieStr$refUri null$encodeValue true) {
3992         
// Set default values
3993         
if (is_string($refUri)) {
3994             
$refUri Zend_Uri_Http::factory($refUri);
3995         }
3996
3997         
$name    '';
3998         
$value   '';
3999         
$domain  '';
4000         
$path    '';
4001         
$expires null;
4002         
$secure  false;
4003         
$parts   explode(';'$cookieStr);
4004
4005         
// If first part does not include '=', fail
4006         
if (strpos($parts[0], '=') === false) return false;
4007
4008         
// Get the name and value of the cookie
4009         
list($name$value) = explode('='trim(array_shift($parts)), 2);
4010         
$name  trim($name);
4011         if (
$encodeValue) {
4012             
$value urldecode(trim($value));
4013         }
4014
4015         
// Set default domain and path
4016         
if ($refUri instanceof Zend_Uri_Http) {
4017             
$domain $refUri->getHost();
4018             
$path $refUri->getPath();
4019             
$path substr($path0strrpos($path'/'));
4020         }
4021
4022         
// Set other cookie parameters
4023         
foreach ($parts as $part) {
4024             
$part trim($part);
4025             if (
strtolower($part) == 'secure') {
4026                 
$secure true;
4027                 continue;
4028             }
4029
4030             
$keyValue explode('='$part2);
4031             if (
count($keyValue) == 2) {
4032                 list(
$k$v) = $keyValue;
4033                 switch (
strtolower($k))    {
4034                     case 
'expires':
4035                         if((
$expires strtotime($v)) === false) {
4036                             
/**
4037                              * The expiration is past Tue, 19 Jan 2038 03:14:07 UTC
4038                              * the maximum for 32-bit signed integer. Zend_Date
4039                              * can get around that limit.
4040                              *
4041                              * @see Zend_Date
4042                              */
4043                             
require_once 'Zend/Date.php';
4044
4045                             
$expireDate = new Zend_Date($v);
4046                             
$expires $expireDate->getTimestamp();
4047                         }
4048                         break;
4049
4050                     case 
'path':
4051                         
$path $v;
4052                         break;
4053
4054                     case 
'domain':
4055                         
$domain $v;
4056                         break;
4057
4058                     default:
4059                         break;
4060                 }
4061             }
4062         }
4063
4064         if (
$name !== '') {
4065             
$ret = new self($name$value$domain$expires$path$secure);
4066             
$ret->encodeValue = ($encodeValue) ? true false;
4067             return 
$ret;
4068         } else {
4069             return 
false;
4070         }
4071     }
4072
4073     
/**
4074      * Check if a cookie's domain matches a host name.
4075      *
4076      * Used by Zend_Http_Cookie and Zend_Http_CookieJar for cookie matching
4077      *
4078      * @param  string $cookieDomain
4079      * @param  string $host
4080      *
4081      * @return boolean
4082      */
4083     
public static function matchCookieDomain($cookieDomain$host) {
4084         if (! 
$cookieDomain) {
4085             if (!
class_exists('Zend_Exception')) require 'exception.php';
4086             throw new 
Zend_Http_Exception("\$cookieDomain is expected to be a cookie domain");
4087         }
4088
4089         if (! 
$host) {
4090             if (!
class_exists('Zend_Exception')) require 'exception.php';
4091             throw new 
Zend_Http_Exception("\$host is expected to be a host name");
4092         }
4093
4094         
$cookieDomain strtolower($cookieDomain);
4095         
$host strtolower($host);
4096
4097         if (
$cookieDomain[0] == '.') {
4098             
$cookieDomain substr($cookieDomain1);
4099         }
4100
4101         
// Check for either exact match or suffix match
4102         
return ($cookieDomain == $host ||
4103                 
preg_match("/\.$cookieDomain$/"$host));
4104     }
4105
4106     
/**
4107      * Check if a cookie's path matches a URL path
4108      *
4109      * Used by Zend_Http_Cookie and Zend_Http_CookieJar for cookie matching
4110      *
4111      * @param  string $cookiePath
4112      * @param  string $path
4113      * @return boolean
4114      */
4115     
public static function matchCookiePath($cookiePath$path) {
4116         if (! 
$cookiePath) {
4117             if (!
class_exists('Zend_Exception')) require 'exception.php';
4118             throw new 
Zend_Http_Exception("\$cookiePath is expected to be a cookie path");
4119         }
4120
4121         if (! 
$path) {
4122             if (!
class_exists('Zend_Exception')) require 'exception.php';
4123             throw new 
Zend_Http_Exception("\$path is expected to be a host name");
4124         }
4125
4126         return (
strpos($path$cookiePath) === 0);
4127     }
4128 }
4129
4130
/**
4131  * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It
4132  * includes easy access to all the response's different elemts, as well as some
4133  * convenience methods for parsing and validating HTTP responses.
4134  *
4135  * @package    Zend_Http
4136  * @subpackage Response
4137  * @version    $Id: Response.php 17131 2009-07-26 10:03:39Z shahar $
4138  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
4139  * @license    http://framework.zend.com/license/new-bsd     New BSD License
4140  */
4141
class Zend_Http_Response_Stream extends Zend_Http_Response {
4142     
/**
4143      * Response as stream
4144      *
4145      * @var resource
4146      */
4147     
protected $stream;
4148
4149     
/**
4150      * The name of the file containing the stream
4151      *
4152      * Will be empty if stream is not file-based.
4153      *
4154      * @var string
4155      */
4156     
protected $stream_name;
4157
4158     
/**
4159      * Should we clean up the stream file when this response is closed?
4160      *
4161      * @var boolean
4162      */
4163     
protected $_cleanup;
4164
4165     
/**
4166      * Get the response as stream
4167      *
4168      * @return resourse
4169      */
4170     
public function getStream() {
4171         return 
$this->stream;
4172     }
4173
4174     
/**
4175      * Set the response stream
4176      *
4177      * @param resourse $stream
4178      * @return Zend_Http_Response_Stream
4179      */
4180     
public function setStream($stream) {
4181         
$this->stream $stream;
4182         return 
$this;
4183     }
4184
4185     
/**
4186      * Get the cleanup trigger
4187      *
4188      * @return boolean
4189      */
4190     
public function getCleanup() {
4191         return 
$this->_cleanup;
4192     }
4193
4194     
/**
4195      * Set the cleanup trigger
4196      *
4197      * @param $cleanup Set cleanup trigger
4198      */
4199     
public function setCleanup($cleanup true) {
4200         
$this->_cleanup $cleanup;
4201     }
4202
4203     
/**
4204      * Get file name associated with the stream
4205      *
4206      * @return string
4207      */
4208     
public function getStreamName() {
4209         return 
$this->stream_name;
4210     }
4211
4212     
/**
4213      * Set file name associated with the stream
4214      *
4215      * @param string $stream_name Name to set
4216      * @return Zend_Http_Response_Stream
4217      */
4218     
public function setStreamName($stream_name) {
4219         
$this->stream_name $stream_name;
4220         return 
$this;
4221     }
4222
4223
4224     
/**
4225      * HTTP response constructor
4226      *
4227      * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP
4228      * response string and create a new Zend_Http_Response object.
4229      *
4230      * NOTE: The constructor no longer accepts nulls or empty values for the code and
4231      * headers and will throw an exception if the passed values do not form a valid HTTP
4232      * responses.
4233      *
4234      * If no message is passed, the message will be guessed according to the response code.
4235      *
4236      * @param int $code Response code (200, 404, ...)
4237      * @param array $headers Headers array
4238      * @param string $body Response body
4239      * @param string $version HTTP version
4240      * @param string $message Response code as text
4241      * @throws Zend_Http_Exception
4242      */
4243     
public function __construct($code$headers$body null$version '1.1'$message null) {
4244
4245         if(
is_resource($body)) {
4246             
$this->setStream($body);
4247             
$body '';
4248         }
4249         
parent::__construct($code$headers$body$version$message);
4250     }
4251
4252     
/**
4253      * Create a new Zend_Http_Response_Stream object from a string
4254      *
4255      * @param string $response_str
4256      * @param resource $stream
4257      * @return Zend_Http_Response_Stream
4258      */
4259     
public static function fromStream($response_str$stream) {
4260         
$code    self::extractCode($response_str);
4261         
$headers self::extractHeaders($response_str);
4262         
$version self::extractVersion($response_str);
4263         
$message self::extractMessage($response_str);
4264
4265         return new 
self($code$headers$stream$version$message);
4266     }
4267
4268     
/**
4269      * Get the response body as string
4270      *
4271      * This method returns the body of the HTTP response (the content), as it
4272      * should be in it's readable version - that is, after decoding it (if it
4273      * was decoded), deflating it (if it was gzip compressed), etc.
4274      *
4275      * If you want to get the raw body (as transfered on wire) use
4276      * $this->getRawBody() instead.
4277      *
4278      * @return string
4279      */
4280     
public function getBody() {
4281         if(
$this->stream != null) {
4282             
$this->readStream();
4283         }
4284         return 
parent::getBody();
4285     }
4286
4287     
/**
4288      * Get the raw response body (as transfered "on wire") as string
4289      *
4290      * If the body is encoded (with Transfer-Encoding, not content-encoding -
4291      * IE "chunked" body), gzip compressed, etc. it will not be decoded.
4292      *
4293      * @return string
4294      */
4295     
public function getRawBody() {
4296         if(
$this->stream) {
4297             
$this->readStream();
4298         }
4299         return 
$this->body;
4300     }
4301
4302     
/**
4303      * Read stream content and return it as string
4304      *
4305      * Function reads the remainder of the body from the stream and closes the stream.
4306      *
4307      * @return string
4308      */
4309     
protected function readStream() {
4310         if(!
is_resource($this->stream)) {
4311             return 
'';
4312         }
4313
4314         if(isset(
$headers['content-length'])) {
4315             
$this->body stream_get_contents($this->stream$headers['content-length']);
4316         } else {
4317             
$this->body stream_get_contents($this->stream);
4318         }
4319         
fclose($this->stream);
4320         
$this->stream null;
4321     }
4322
4323     public function 
__destruct() {
4324         if(
is_resource($this->stream)) {
4325             
fclose($this->stream);
4326             
$this->stream null;
4327         }
4328         if(
$this->_cleanup) {
4329             @
unlink($this->stream_name);
4330         }
4331     }
4332 }