PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/blog/www/system/library/PEAR/PEAR/REST.php

https://bitbucket.org/vmihailenco/vladimirwebdev
PHP | 397 lines | 299 code | 12 blank | 86 comment | 67 complexity | b30e9eae6cfd5474cb618d4477d19814 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * PEAR_REST
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE: This source file is subject to version 3.0 of the PHP license
  8. * that is available through the world-wide-web at the following URI:
  9. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  10. * the PHP License and are unable to obtain it through the web, please
  11. * send a note to license@php.net so we can mail you a copy immediately.
  12. *
  13. * @category pear
  14. * @package PEAR
  15. * @author Greg Beaver <cellog@php.net>
  16. * @copyright 1997-2008 The PHP Group
  17. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  18. * @version CVS: $Id: REST.php 11 2008-09-16 18:31:09Z root $
  19. * @link http://pear.php.net/package/PEAR
  20. * @since File available since Release 1.4.0a1
  21. */
  22. /**
  23. * For downloading xml files
  24. */
  25. require_once 'PEAR.php';
  26. require_once 'PEAR/XMLParser.php';
  27. /**
  28. * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
  29. * as well
  30. * @category pear
  31. * @package PEAR
  32. * @author Greg Beaver <cellog@php.net>
  33. * @copyright 1997-2008 The PHP Group
  34. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  35. * @version Release: 1.7.2
  36. * @link http://pear.php.net/package/PEAR
  37. * @since Class available since Release 1.4.0a1
  38. */
  39. class PEAR_REST
  40. {
  41. var $config;
  42. var $_options;
  43. function PEAR_REST(&$config, $options = array())
  44. {
  45. $this->config = &$config;
  46. $this->_options = $options;
  47. }
  48. /**
  49. * Retrieve REST data, but always retrieve the local cache if it is available.
  50. *
  51. * This is useful for elements that should never change, such as information on a particular
  52. * release
  53. * @param string full URL to this resource
  54. * @param array|false contents of the accept-encoding header
  55. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  56. * parsed using PEAR_XMLParser
  57. * @return string|array
  58. */
  59. function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
  60. {
  61. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  62. md5($url) . 'rest.cachefile';
  63. if (file_exists($cachefile)) {
  64. return unserialize(implode('', file($cachefile)));
  65. }
  66. return $this->retrieveData($url, $accept, $forcestring, $channel);
  67. }
  68. /**
  69. * Retrieve a remote REST resource
  70. * @param string full URL to this resource
  71. * @param array|false contents of the accept-encoding header
  72. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  73. * parsed using PEAR_XMLParser
  74. * @return string|array
  75. */
  76. function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
  77. {
  78. $cacheId = $this->getCacheId($url);
  79. if ($ret = $this->useLocalCache($url, $cacheId)) {
  80. return $ret;
  81. }
  82. if (!isset($this->_options['offline'])) {
  83. $trieddownload = true;
  84. $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
  85. } else {
  86. $trieddownload = false;
  87. $file = false;
  88. }
  89. if (PEAR::isError($file)) {
  90. if ($file->getCode() == -9276) {
  91. $trieddownload = false;
  92. $file = false; // use local copy if available on socket connect error
  93. } else {
  94. return $file;
  95. }
  96. }
  97. if (!$file) {
  98. $ret = $this->getCache($url);
  99. if (!PEAR::isError($ret) && $trieddownload) {
  100. // reset the age of the cache if the server says it was unmodified
  101. $this->saveCache($url, $ret, null, true, $cacheId);
  102. }
  103. return $ret;
  104. }
  105. if (is_array($file)) {
  106. $headers = $file[2];
  107. $lastmodified = $file[1];
  108. $content = $file[0];
  109. } else {
  110. $content = $file;
  111. $lastmodified = false;
  112. $headers = array();
  113. }
  114. if ($forcestring) {
  115. $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  116. return $content;
  117. }
  118. if (isset($headers['content-type'])) {
  119. switch ($headers['content-type']) {
  120. case 'text/xml' :
  121. case 'application/xml' :
  122. $parser = new PEAR_XMLParser;
  123. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  124. $err = $parser->parse($content);
  125. PEAR::popErrorHandling();
  126. if (PEAR::isError($err)) {
  127. return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
  128. $err->getMessage());
  129. }
  130. $content = $parser->getData();
  131. case 'text/html' :
  132. default :
  133. // use it as a string
  134. }
  135. } else {
  136. // assume XML
  137. $parser = new PEAR_XMLParser;
  138. $parser->parse($content);
  139. $content = $parser->getData();
  140. }
  141. $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  142. return $content;
  143. }
  144. function useLocalCache($url, $cacheid = null)
  145. {
  146. if ($cacheid === null) {
  147. $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  148. md5($url) . 'rest.cacheid';
  149. if (file_exists($cacheidfile)) {
  150. $cacheid = unserialize(implode('', file($cacheidfile)));
  151. } else {
  152. return false;
  153. }
  154. }
  155. $cachettl = $this->config->get('cache_ttl');
  156. // If cache is newer than $cachettl seconds, we use the cache!
  157. if (time() - $cacheid['age'] < $cachettl) {
  158. return $this->getCache($url);
  159. }
  160. return false;
  161. }
  162. function getCacheId($url)
  163. {
  164. $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  165. md5($url) . 'rest.cacheid';
  166. if (file_exists($cacheidfile)) {
  167. $ret = unserialize(implode('', file($cacheidfile)));
  168. return $ret;
  169. } else {
  170. return false;
  171. }
  172. }
  173. function getCache($url)
  174. {
  175. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  176. md5($url) . 'rest.cachefile';
  177. if (file_exists($cachefile)) {
  178. return unserialize(implode('', file($cachefile)));
  179. } else {
  180. return PEAR::raiseError('No cached content available for "' . $url . '"');
  181. }
  182. }
  183. /**
  184. * @param string full URL to REST resource
  185. * @param string original contents of the REST resource
  186. * @param array HTTP Last-Modified and ETag headers
  187. * @param bool if true, then the cache id file should be regenerated to
  188. * trigger a new time-to-live value
  189. */
  190. function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
  191. {
  192. $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  193. md5($url) . 'rest.cacheid';
  194. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  195. md5($url) . 'rest.cachefile';
  196. if ($cacheid === null && $nochange) {
  197. $cacheid = unserialize(implode('', file($cacheidfile)));
  198. }
  199. $fp = @fopen($cacheidfile, 'wb');
  200. if (!$fp) {
  201. $cache_dir = $this->config->get('cache_dir');
  202. if (!is_dir($cache_dir)) {
  203. System::mkdir(array('-p', $cache_dir));
  204. $fp = @fopen($cacheidfile, 'wb');
  205. if (!$fp) {
  206. return false;
  207. }
  208. } else {
  209. return false;
  210. }
  211. }
  212. if ($nochange) {
  213. fwrite($fp, serialize(array(
  214. 'age' => time(),
  215. 'lastChange' => $cacheid['lastChange'],
  216. )));
  217. fclose($fp);
  218. return true;
  219. } else {
  220. fwrite($fp, serialize(array(
  221. 'age' => time(),
  222. 'lastChange' => $lastmodified,
  223. )));
  224. }
  225. fclose($fp);
  226. $fp = @fopen($cachefile, 'wb');
  227. if (!$fp) {
  228. if (file_exists($cacheidfile)) {
  229. @unlink($cacheidfile);
  230. }
  231. return false;
  232. }
  233. fwrite($fp, serialize($contents));
  234. fclose($fp);
  235. return true;
  236. }
  237. /**
  238. * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory
  239. * This is best used for small files
  240. *
  241. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  242. * setting), the proxy will be used.
  243. *
  244. * @param string $url the URL to download
  245. * @param string $save_dir directory to save file in
  246. * @param false|string|array $lastmodified header values to check against for caching
  247. * use false to return the header values from this download
  248. * @param false|array $accept Accept headers to send
  249. * @return string|array Returns the contents of the downloaded file or a PEAR
  250. * error on failure. If the error is caused by
  251. * socket-related errors, the error object will
  252. * have the fsockopen error code available through
  253. * getCode(). If caching is requested, then return the header
  254. * values.
  255. *
  256. * @access public
  257. */
  258. function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
  259. {
  260. $info = parse_url($url);
  261. if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  262. return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  263. }
  264. if (!isset($info['host'])) {
  265. return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  266. } else {
  267. $host = $info['host'];
  268. if (!array_key_exists('port', $info)) {
  269. $info['port'] = null;
  270. }
  271. if (!array_key_exists('path', $info)) {
  272. $info['path'] = null;
  273. }
  274. $port = $info['port'];
  275. $path = $info['path'];
  276. }
  277. $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
  278. if ($this->config->get('http_proxy')&&
  279. $proxy = parse_url($this->config->get('http_proxy'))) {
  280. $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
  281. if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
  282. $proxy_host = 'ssl://' . $proxy_host;
  283. }
  284. $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
  285. $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
  286. $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
  287. }
  288. if (empty($port)) {
  289. if (isset($info['scheme']) && $info['scheme'] == 'https') {
  290. $port = 443;
  291. } else {
  292. $port = 80;
  293. }
  294. }
  295. If (isset($proxy['host'])) {
  296. $request = "GET $url HTTP/1.1\r\n";
  297. } else {
  298. $request = "GET $path HTTP/1.1\r\n";
  299. }
  300. $request .= "Host: $host:$port\r\n";
  301. $ifmodifiedsince = '';
  302. if (is_array($lastmodified)) {
  303. if (isset($lastmodified['Last-Modified'])) {
  304. $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  305. }
  306. if (isset($lastmodified['ETag'])) {
  307. $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  308. }
  309. } else {
  310. $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  311. }
  312. $request .= $ifmodifiedsince .
  313. "User-Agent: PEAR/1.7.2/PHP/" . PHP_VERSION . "\r\n";
  314. $username = $this->config->get('username', null, $channel);
  315. $password = $this->config->get('password', null, $channel);
  316. if ($username && $password) {
  317. $tmp = base64_encode("$username:$password");
  318. $request .= "Authorization: Basic $tmp\r\n";
  319. }
  320. if ($proxy_host != '' && $proxy_user != '') {
  321. $request .= 'Proxy-Authorization: Basic ' .
  322. base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
  323. }
  324. if ($accept) {
  325. $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  326. }
  327. $request .= "Accept-Encoding:\r\n";
  328. $request .= "Connection: close\r\n";
  329. $request .= "\r\n";
  330. if ($proxy_host != '') {
  331. $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
  332. if (!$fp) {
  333. return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",
  334. -9276);
  335. }
  336. } else {
  337. if (isset($info['scheme']) && $info['scheme'] == 'https') {
  338. $host = 'ssl://' . $host;
  339. }
  340. $fp = @fsockopen($host, $port, $errno, $errstr);
  341. if (!$fp) {
  342. return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
  343. }
  344. }
  345. fwrite($fp, $request);
  346. $headers = array();
  347. while (trim($line = fgets($fp, 1024))) {
  348. if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
  349. $headers[strtolower($matches[1])] = trim($matches[2]);
  350. } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  351. if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {
  352. return false;
  353. }
  354. if ($matches[1] != 200) {
  355. return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]);
  356. }
  357. }
  358. }
  359. if (isset($headers['content-length'])) {
  360. $length = $headers['content-length'];
  361. } else {
  362. $length = -1;
  363. }
  364. $data = '';
  365. while ($chunk = @fread($fp, 8192)) {
  366. $data .= $chunk;
  367. }
  368. fclose($fp);
  369. if ($lastmodified === false || $lastmodified) {
  370. if (isset($headers['etag'])) {
  371. $lastmodified = array('ETag' => $headers['etag']);
  372. }
  373. if (isset($headers['last-modified'])) {
  374. if (is_array($lastmodified)) {
  375. $lastmodified['Last-Modified'] = $headers['last-modified'];
  376. } else {
  377. $lastmodified = $headers['last-modified'];
  378. }
  379. }
  380. return array($data, $lastmodified, $headers);
  381. }
  382. return $data;
  383. }
  384. }
  385. ?>