258 lines
7 KiB
PHP
258 lines
7 KiB
PHP
<?php
|
|
/**
|
|
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
|
* Atom feeds for websites that don't have one.
|
|
*
|
|
* For the full license information, please view the UNLICENSE file distributed
|
|
* with this source code.
|
|
*
|
|
* @package Core
|
|
* @license http://unlicense.org/ UNLICENSE
|
|
* @link https://github.com/rss-bridge/rss-bridge
|
|
*/
|
|
|
|
class DisplayAction extends ActionAbstract {
|
|
private function get_return_code($error) {
|
|
$returnCode = $error->getCode();
|
|
if ($returnCode === 301 || $returnCode === 302) {
|
|
# Don't pass redirect codes to the exterior
|
|
$returnCode = 508;
|
|
}
|
|
return $returnCode;
|
|
}
|
|
|
|
public function execute() {
|
|
$bridge = array_key_exists('bridge', $this->userData) ? $this->userData['bridge'] : null;
|
|
|
|
$format = $this->userData['format']
|
|
or returnClientError('You must specify a format!');
|
|
|
|
$bridgeFac = new \BridgeFactory();
|
|
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
|
|
|
|
// whitelist control
|
|
if(!$bridgeFac->isWhitelisted($bridge)) {
|
|
throw new \Exception('This bridge is not whitelisted', 401);
|
|
die;
|
|
}
|
|
|
|
// Data retrieval
|
|
$bridge = $bridgeFac->create($bridge);
|
|
|
|
$noproxy = array_key_exists('_noproxy', $this->userData)
|
|
&& filter_var($this->userData['_noproxy'], FILTER_VALIDATE_BOOLEAN);
|
|
|
|
if(defined('PROXY_URL') && PROXY_BYBRIDGE && $noproxy) {
|
|
define('NOPROXY', true);
|
|
}
|
|
|
|
// Cache timeout
|
|
$cache_timeout = -1;
|
|
if(array_key_exists('_cache_timeout', $this->userData)) {
|
|
|
|
if(!CUSTOM_CACHE_TIMEOUT) {
|
|
unset($this->userData['_cache_timeout']);
|
|
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '?' . http_build_query($this->userData);
|
|
header('Location: ' . $uri, true, 301);
|
|
die();
|
|
}
|
|
|
|
$cache_timeout = filter_var($this->userData['_cache_timeout'], FILTER_VALIDATE_INT);
|
|
|
|
} else {
|
|
$cache_timeout = $bridge->getCacheTimeout();
|
|
}
|
|
|
|
// Remove parameters that don't concern bridges
|
|
$bridge_params = array_diff_key(
|
|
$this->userData,
|
|
array_fill_keys(
|
|
array(
|
|
'action',
|
|
'bridge',
|
|
'format',
|
|
'_noproxy',
|
|
'_cache_timeout',
|
|
'_error_time'
|
|
), '')
|
|
);
|
|
|
|
// Remove parameters that don't concern caches
|
|
$cache_params = array_diff_key(
|
|
$this->userData,
|
|
array_fill_keys(
|
|
array(
|
|
'action',
|
|
'format',
|
|
'_noproxy',
|
|
'_cache_timeout',
|
|
'_error_time'
|
|
), '')
|
|
);
|
|
|
|
// Initialize cache
|
|
$cacheFac = new CacheFactory();
|
|
$cacheFac->setWorkingDir(PATH_LIB_CACHES);
|
|
$cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
|
|
$cache->setScope('');
|
|
$cache->purgeCache(86400); // 24 hours
|
|
$cache->setKey($cache_params);
|
|
|
|
$items = array();
|
|
$infos = array();
|
|
$mtime = $cache->getTime();
|
|
|
|
if($mtime !== false
|
|
&& (time() - $cache_timeout < $mtime)
|
|
&& !Debug::isEnabled()) { // Load cached data
|
|
|
|
// Send "Not Modified" response if client supports it
|
|
// Implementation based on https://stackoverflow.com/a/10847262
|
|
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
|
$stime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
|
|
|
|
if($mtime <= $stime) { // Cached data is older or same
|
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $mtime) . 'GMT', true, 304);
|
|
die();
|
|
}
|
|
}
|
|
|
|
$cached = $cache->loadData();
|
|
|
|
if(isset($cached['items']) && isset($cached['extraInfos'])) {
|
|
foreach($cached['items'] as $item) {
|
|
$items[] = new \FeedItem($item);
|
|
}
|
|
|
|
$infos = $cached['extraInfos'];
|
|
}
|
|
|
|
} else { // Collect new data
|
|
|
|
try {
|
|
$bridge->setDatas($bridge_params);
|
|
$bridge->collectData();
|
|
|
|
$items = $bridge->getItems();
|
|
|
|
// Transform "legacy" items to FeedItems if necessary.
|
|
// Remove this code when support for "legacy" items ends!
|
|
if(isset($items[0]) && is_array($items[0])) {
|
|
$feedItems = array();
|
|
|
|
foreach($items as $item) {
|
|
$feedItems[] = new \FeedItem($item);
|
|
}
|
|
|
|
$items = $feedItems;
|
|
}
|
|
|
|
$infos = array(
|
|
'name' => $bridge->getName(),
|
|
'uri' => $bridge->getURI(),
|
|
'icon' => $bridge->getIcon()
|
|
);
|
|
} catch(Error $e) {
|
|
error_log($e);
|
|
|
|
if(logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) {
|
|
if(Configuration::getConfig('error', 'output') === 'feed') {
|
|
$item = new \FeedItem();
|
|
|
|
// Create "new" error message every 24 hours
|
|
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
|
|
|
// Error 0 is a special case (i.e. "trying to get property of non-object")
|
|
if($e->getCode() === 0) {
|
|
$item->setTitle(
|
|
'Bridge encountered an unexpected situation! ('
|
|
. $this->userData['_error_time']
|
|
. ')'
|
|
);
|
|
} else {
|
|
$item->setTitle(
|
|
'Bridge returned error '
|
|
. $e->getCode()
|
|
. '! ('
|
|
. $this->userData['_error_time']
|
|
. ')'
|
|
);
|
|
}
|
|
|
|
$item->setURI(
|
|
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
|
. '?'
|
|
. http_build_query($this->userData)
|
|
);
|
|
|
|
$item->setTimestamp(time());
|
|
$item->setContent(buildBridgeException($e, $bridge));
|
|
|
|
$items[] = $item;
|
|
} elseif(Configuration::getConfig('error', 'output') === 'http') {
|
|
header('Content-Type: text/html', true, $this->get_return_code($e));
|
|
die(buildTransformException($e, $bridge));
|
|
}
|
|
}
|
|
} catch(Exception $e) {
|
|
error_log($e);
|
|
|
|
if(logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) {
|
|
if(Configuration::getConfig('error', 'output') === 'feed') {
|
|
$item = new \FeedItem();
|
|
|
|
// Create "new" error message every 24 hours
|
|
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
|
|
|
$item->setURI(
|
|
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
|
. '?'
|
|
. http_build_query($this->userData)
|
|
);
|
|
|
|
$item->setTitle(
|
|
'Bridge returned error '
|
|
. $e->getCode()
|
|
. '! ('
|
|
. $this->userData['_error_time']
|
|
. ')'
|
|
);
|
|
$item->setTimestamp(time());
|
|
$item->setContent(buildBridgeException($e, $bridge));
|
|
|
|
$items[] = $item;
|
|
} elseif(Configuration::getConfig('error', 'output') === 'http') {
|
|
header('Content-Type: text/html', true, $this->get_return_code($e));
|
|
die(buildTransformException($e, $bridge));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store data in cache
|
|
$cache->saveData(array(
|
|
'items' => array_map(function($i){ return $i->toArray(); }, $items),
|
|
'extraInfos' => $infos
|
|
));
|
|
|
|
}
|
|
|
|
// Data transformation
|
|
try {
|
|
$formatFac = new FormatFactory();
|
|
$formatFac->setWorkingDir(PATH_LIB_FORMATS);
|
|
$format = $formatFac->create($format);
|
|
$format->setItems($items);
|
|
$format->setExtraInfos($infos);
|
|
$format->setLastModified($cache->getTime());
|
|
$format->display();
|
|
} catch(Error $e) {
|
|
error_log($e);
|
|
header('Content-Type: text/html', true, $e->getCode());
|
|
die(buildTransformException($e, $bridge));
|
|
} catch(Exception $e) {
|
|
error_log($e);
|
|
header('Content-Type: text/html', true, $e->getCode());
|
|
die(buildTransformException($e, $bridge));
|
|
}
|
|
}
|
|
}
|