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