PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/common/libraries/plugin/pear/HTTP/Request.php

https://bitbucket.org/cbenelug/chamilo
PHP | 1661 lines | 884 code | 151 blank | 626 comment | 174 complexity | 941a3616223f0e07cb66c369e8e00ede MD5 | raw file
Possible License(s): GPL-3.0, MIT, GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file