4,18 → 4,11 |
* |
* PHP versions 4 and 5 |
* |
* LICENSE: This source file is subject to version 3.0 of the PHP license |
* that is available through the world-wide-web at the following URI: |
* http://www.php.net/license/3_0.txt. If you did not receive a copy of |
* the PHP License and are unable to obtain it through the web, please |
* send a note to license@php.net so we can mail you a copy immediately. |
* |
* @category pear |
* @package PEAR |
* @author Greg Beaver <cellog@php.net> |
* @copyright 1997-2006 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: REST.php,v 1.21 2006/03/27 04:33:11 cellog Exp $ |
* @copyright 1997-2009 The Authors |
* @license http://opensource.org/licenses/bsd-license.php New BSD License |
* @link http://pear.php.net/package/PEAR |
* @since File available since Release 1.4.0a1 |
*/ |
32,9 → 25,9 |
* @category pear |
* @package PEAR |
* @author Greg Beaver <cellog@php.net> |
* @copyright 1997-2006 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version Release: 1.5.1 |
* @copyright 1997-2009 The Authors |
* @license http://opensource.org/licenses/bsd-license.php New BSD License |
* @version Release: 1.10.1 |
* @link http://pear.php.net/package/PEAR |
* @since Class available since Release 1.4.0a1 |
*/ |
42,9 → 35,10 |
{ |
var $config; |
var $_options; |
function PEAR_REST(&$config, $options = array()) |
|
function __construct(&$config, $options = array()) |
{ |
$this->config = &$config; |
$this->config = &$config; |
$this->_options = $options; |
} |
|
59,14 → 53,16 |
* parsed using PEAR_XMLParser |
* @return string|array |
*/ |
function retrieveCacheFirst($url, $accept = false, $forcestring = false) |
function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false) |
{ |
$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cachefile'; |
|
if (file_exists($cachefile)) { |
return unserialize(implode('', file($cachefile))); |
} |
return $this->retrieveData($url, $accept, $forcestring); |
|
return $this->retrieveData($url, $accept, $forcestring, $channel); |
} |
|
/** |
77,52 → 73,74 |
* parsed using PEAR_XMLParser |
* @return string|array |
*/ |
function retrieveData($url, $accept = false, $forcestring = false) |
function retrieveData($url, $accept = false, $forcestring = false, $channel = false) |
{ |
$cacheId = $this->getCacheId($url); |
if ($ret = $this->useLocalCache($url, $cacheId)) { |
return $ret; |
} |
|
$file = $trieddownload = false; |
if (!isset($this->_options['offline'])) { |
$trieddownload = true; |
$file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept); |
} else { |
$trieddownload = false; |
$file = false; |
$file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel); |
} |
|
if (PEAR::isError($file)) { |
if ($file->getCode() == -9276) { |
$trieddownload = false; |
$file = false; // use local copy if available on socket connect error |
} else { |
if ($file->getCode() !== -9276) { |
return $file; |
} |
|
$trieddownload = false; |
$file = false; // use local copy if available on socket connect error |
} |
|
if (!$file) { |
$ret = $this->getCache($url); |
if (!PEAR::isError($ret) && $trieddownload) { |
// reset the age of the cache if the server says it was unmodified |
$this->saveCache($url, $ret, null, true, $cacheId); |
$result = $this->saveCache($url, $ret, null, true, $cacheId); |
if (PEAR::isError($result)) { |
return PEAR::raiseError($result->getMessage()); |
} |
} |
|
return $ret; |
} |
|
if (is_array($file)) { |
$headers = $file[2]; |
$headers = $file[2]; |
$lastmodified = $file[1]; |
$content = $file[0]; |
$content = $file[0]; |
} else { |
$content = $file; |
$headers = array(); |
$lastmodified = false; |
$headers = array(); |
$content = $file; |
} |
|
if ($forcestring) { |
$this->saveCache($url, $content, $lastmodified, false, $cacheId); |
$result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); |
if (PEAR::isError($result)) { |
return PEAR::raiseError($result->getMessage()); |
} |
|
return $content; |
} |
|
if (isset($headers['content-type'])) { |
switch ($headers['content-type']) { |
$content_type = explode(";", $headers['content-type']); |
$content_type = $content_type[0]; |
switch ($content_type) { |
case 'text/xml' : |
case 'application/xml' : |
case 'text/plain' : |
if ($content_type === 'text/plain') { |
$check = substr($content, 0, 5); |
if ($check !== '<?xml') { |
break; |
} |
} |
|
$parser = new PEAR_XMLParser; |
PEAR::pushErrorHandling(PEAR_ERROR_RETURN); |
$err = $parser->parse($content); |
142,7 → 160,12 |
$parser->parse($content); |
$content = $parser->getData(); |
} |
$this->saveCache($url, $content, $lastmodified, false, $cacheId); |
|
$result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); |
if (PEAR::isError($result)) { |
return PEAR::raiseError($result->getMessage()); |
} |
|
return $content; |
} |
|
151,17 → 174,19 |
if ($cacheid === null) { |
$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cacheid'; |
if (file_exists($cacheidfile)) { |
$cacheid = unserialize(implode('', file($cacheidfile))); |
} else { |
if (!file_exists($cacheidfile)) { |
return false; |
} |
|
$cacheid = unserialize(implode('', file($cacheidfile))); |
} |
|
$cachettl = $this->config->get('cache_ttl'); |
// If cache is newer than $cachettl seconds, we use the cache! |
if (time() - $cacheid['age'] < $cachettl) { |
return $this->getCache($url); |
} |
|
return false; |
} |
|
169,12 → 194,13 |
{ |
$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cacheid'; |
if (file_exists($cacheidfile)) { |
$ret = unserialize(implode('', file($cacheidfile))); |
return $ret; |
} else { |
|
if (!file_exists($cacheidfile)) { |
return false; |
} |
|
$ret = unserialize(implode('', file($cacheidfile))); |
return $ret; |
} |
|
function getCache($url) |
181,11 → 207,12 |
{ |
$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cachefile'; |
if (file_exists($cachefile)) { |
return unserialize(implode('', file($cachefile))); |
} else { |
|
if (!file_exists($cachefile)) { |
return PEAR::raiseError('No cached content available for "' . $url . '"'); |
} |
|
return unserialize(implode('', file($cachefile))); |
} |
|
/** |
197,54 → 224,96 |
*/ |
function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null) |
{ |
$cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cacheid'; |
$cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . |
md5($url) . 'rest.cachefile'; |
$cache_dir = $this->config->get('cache_dir'); |
$d = $cache_dir . DIRECTORY_SEPARATOR . md5($url); |
$cacheidfile = $d . 'rest.cacheid'; |
$cachefile = $d . 'rest.cachefile'; |
|
if (!is_dir($cache_dir)) { |
if (System::mkdir(array('-p', $cache_dir)) === false) { |
return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed."); |
} |
} |
|
if (!is_writeable($cache_dir)) { |
// If writing to the cache dir is not going to work, silently do nothing. |
// An ugly hack, but retains compat with PEAR 1.9.1 where many commands |
// work fine as non-root user (w/out write access to default cache dir). |
return true; |
} |
|
if ($cacheid === null && $nochange) { |
$cacheid = unserialize(implode('', file($cacheidfile))); |
} |
|
$fp = @fopen($cacheidfile, 'wb'); |
if (!$fp) { |
$cache_dir = $this->config->get('cache_dir'); |
if (!is_dir($cache_dir)) { |
System::mkdir(array('-p', $cache_dir)); |
$fp = @fopen($cacheidfile, 'wb'); |
if (!$fp) { |
return false; |
} |
} else { |
return false; |
} |
} |
$idData = serialize(array( |
'age' => time(), |
'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified), |
)); |
|
if ($nochange) { |
fwrite($fp, serialize(array( |
'age' => time(), |
'lastChange' => $cacheid['lastChange'], |
))); |
fclose($fp); |
$result = $this->saveCacheFile($cacheidfile, $idData); |
if (PEAR::isError($result)) { |
return $result; |
} elseif ($nochange) { |
return true; |
} else { |
fwrite($fp, serialize(array( |
'age' => time(), |
'lastChange' => $lastmodified, |
))); |
} |
fclose($fp); |
$fp = @fopen($cachefile, 'wb'); |
if (!$fp) { |
|
$result = $this->saveCacheFile($cachefile, serialize($contents)); |
if (PEAR::isError($result)) { |
if (file_exists($cacheidfile)) { |
@unlink($cacheidfile); |
@unlink($cacheidfile); |
} |
return false; |
|
return $result; |
} |
fwrite($fp, serialize($contents)); |
fclose($fp); |
|
return true; |
} |
|
function saveCacheFile($file, $contents) |
{ |
$len = strlen($contents); |
|
$cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode |
if ($cachefile_fp !== false) { // create file |
if (fwrite($cachefile_fp, $contents, $len) < $len) { |
fclose($cachefile_fp); |
return PEAR::raiseError("Could not write $file."); |
} |
} else { // update file |
$cachefile_fp = @fopen($file, 'r+b'); // do not truncate file |
if (!$cachefile_fp) { |
return PEAR::raiseError("Could not open $file for writing."); |
} |
|
if (OS_WINDOWS) { |
$not_symlink = !is_link($file); // see bug #18834 |
} else { |
$cachefile_lstat = lstat($file); |
$cachefile_fstat = fstat($cachefile_fp); |
$not_symlink = $cachefile_lstat['mode'] == $cachefile_fstat['mode'] |
&& $cachefile_lstat['ino'] == $cachefile_fstat['ino'] |
&& $cachefile_lstat['dev'] == $cachefile_fstat['dev'] |
&& $cachefile_fstat['nlink'] === 1; |
} |
|
if ($not_symlink) { |
ftruncate($cachefile_fp, 0); // NOW truncate |
if (fwrite($cachefile_fp, $contents, $len) < $len) { |
fclose($cachefile_fp); |
return PEAR::raiseError("Could not write $file."); |
} |
} else { |
fclose($cachefile_fp); |
$link = function_exists('readlink') ? readlink($file) : $file; |
return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack'); |
} |
} |
|
fclose($cachefile_fp); |
return true; |
} |
|
/** |
* Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory |
* This is best used for small files |
266,54 → 335,59 |
* |
* @access public |
*/ |
function downloadHttp($url, $lastmodified = null, $accept = false) |
function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false) |
{ |
static $redirect = 0; |
// always reset , so we are clean case of error |
$wasredirect = $redirect; |
$redirect = 0; |
|
$info = parse_url($url); |
if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { |
return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); |
} |
|
if (!isset($info['host'])) { |
return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); |
} else { |
$host = $info['host']; |
if (!array_key_exists('port', $info)) { |
$info['port'] = null; |
} |
if (!array_key_exists('path', $info)) { |
$info['path'] = null; |
} |
$port = $info['port']; |
$path = $info['path']; |
} |
|
$host = isset($info['host']) ? $info['host'] : null; |
$port = isset($info['port']) ? $info['port'] : null; |
$path = isset($info['path']) ? $info['path'] : null; |
$schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; |
|
$proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
if ($this->config->get('http_proxy')&& |
$proxy = parse_url($this->config->get('http_proxy'))) { |
if ($this->config->get('http_proxy')&& |
$proxy = parse_url($this->config->get('http_proxy')) |
) { |
$proxy_host = isset($proxy['host']) ? $proxy['host'] : null; |
if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { |
if ($schema === 'https') { |
$proxy_host = 'ssl://' . $proxy_host; |
} |
$proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; |
$proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
$proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
|
$proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; |
$proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
$proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
$proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http'; |
} |
|
if (empty($port)) { |
if (isset($info['scheme']) && $info['scheme'] == 'https') { |
$port = 443; |
} else { |
$port = 80; |
} |
$port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; |
} |
If (isset($proxy['host'])) { |
|
if (isset($proxy['host'])) { |
$request = "GET $url HTTP/1.1\r\n"; |
} else { |
$request = "GET $path HTTP/1.1\r\n"; |
} |
|
$request .= "Host: $host\r\n"; |
$ifmodifiedsince = ''; |
if (is_array($lastmodified)) { |
if (isset($lastmodified['Last-Modified'])) { |
$ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; |
} |
|
if (isset($lastmodified['ETag'])) { |
$ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; |
} |
320,66 → 394,92 |
} else { |
$ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); |
} |
$request .= "Host: $host:$port\r\n" . $ifmodifiedsince . |
"User-Agent: PEAR/1.5.1/PHP/" . PHP_VERSION . "\r\n"; |
$username = $this->config->get('username'); |
$password = $this->config->get('password'); |
|
$request .= $ifmodifiedsince . |
"User-Agent: PEAR/1.10.1/PHP/" . PHP_VERSION . "\r\n"; |
|
$username = $this->config->get('username', null, $channel); |
$password = $this->config->get('password', null, $channel); |
|
if ($username && $password) { |
$tmp = base64_encode("$username:$password"); |
$request .= "Authorization: Basic $tmp\r\n"; |
} |
|
if ($proxy_host != '' && $proxy_user != '') { |
$request .= 'Proxy-Authorization: Basic ' . |
base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; |
} |
|
if ($accept) { |
$request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; |
} |
|
$request .= "Accept-Encoding:\r\n"; |
$request .= "Connection: close\r\n"; |
$request .= "\r\n"; |
|
if ($proxy_host != '') { |
$fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15); |
if (!$fp) { |
return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", |
-9276); |
return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276); |
} |
} else { |
if (isset($info['scheme']) && $info['scheme'] == 'https') { |
if ($schema === 'https') { |
$host = 'ssl://' . $host; |
} |
|
$fp = @fsockopen($host, $port, $errno, $errstr); |
if (!$fp) { |
return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); |
} |
} |
|
fwrite($fp, $request); |
|
$headers = array(); |
while (trim($line = fgets($fp, 1024))) { |
if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) { |
$reply = 0; |
while ($line = trim(fgets($fp, 1024))) { |
if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { |
$headers[strtolower($matches[1])] = trim($matches[2]); |
} elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { |
if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) { |
$reply = (int)$matches[1]; |
if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { |
return false; |
} |
if ($matches[1] != 200) { |
return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]); |
|
if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { |
return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)"); |
} |
} |
} |
if (isset($headers['content-length'])) { |
$length = $headers['content-length']; |
} else { |
$length = -1; |
|
if ($reply != 200) { |
if (!isset($headers['location'])) { |
return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)"); |
} |
|
if ($wasredirect > 4) { |
return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)"); |
} |
|
$redirect = $wasredirect + 1; |
return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel); |
} |
|
$length = isset($headers['content-length']) ? $headers['content-length'] : -1; |
|
$data = ''; |
while ($chunk = @fread($fp, 8192)) { |
$data .= $chunk; |
} |
fclose($fp); |
|
if ($lastmodified === false || $lastmodified) { |
if (isset($headers['etag'])) { |
$lastmodified = array('ETag' => $headers['etag']); |
} |
|
if (isset($headers['last-modified'])) { |
if (is_array($lastmodified)) { |
$lastmodified['Last-Modified'] = $headers['last-modified']; |
387,9 → 487,10 |
$lastmodified = $headers['last-modified']; |
} |
} |
|
return array($data, $lastmodified, $headers); |
} |
|
return $data; |
} |
} |
?> |