diff options
Diffstat (limited to 'actions/DisplayAction.php')
-rw-r--r-- | actions/DisplayAction.php | 421 |
1 files changed, 213 insertions, 208 deletions
diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php index e7031dab..721e9446 100644 --- a/actions/DisplayAction.php +++ b/actions/DisplayAction.php @@ -1,4 +1,5 @@ <?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. @@ -6,216 +7,220 @@ * 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 + * @package Core + * @license http://unlicense.org/ UNLICENSE + * @link https://github.com/rss-bridge/rss-bridge */ class DisplayAction implements ActionInterface { - public $userData = []; - - private function getReturnCode($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(); - - // whitelist control - if(!$bridgeFac->isWhitelisted($bridge)) { - throw new \Exception('This bridge is not whitelisted', 401); - die; - } - - // Data retrieval - $bridge = $bridgeFac->create($bridge); - $bridge->loadConfiguration(); - - $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(); - - $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(), - 'donationUri' => $bridge->getDonationURI(), - 'icon' => $bridge->getIcon() - ); - } catch(\Throwable $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)); - - $message = sprintf( - 'Bridge returned error %s! (%s)', - $e->getCode(), - $this->userData['_error_time'] - ); - $item->setTitle($message); - - $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->getReturnCode($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(); - $format = $formatFac->create($format); - $format->setItems($items); - $format->setExtraInfos($infos); - $lastModified = $cache->getTime(); - $format->setLastModified($lastModified); - if ($lastModified) { - header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT'); - } - header('Content-Type: ' . $format->getMimeType() . '; charset=' . $format->getCharset()); - - echo $format->stringify(); - } catch(\Throwable $e) { - error_log($e); - header('Content-Type: text/html', true, $e->getCode()); - die(buildTransformException($e, $bridge)); - } - } + public $userData = []; + + private function getReturnCode($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(); + + // whitelist control + if (!$bridgeFac->isWhitelisted($bridge)) { + throw new \Exception('This bridge is not whitelisted', 401); + die; + } + + // Data retrieval + $bridge = $bridgeFac->create($bridge); + $bridge->loadConfiguration(); + + $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( + [ + '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( + [ + 'action', + 'format', + '_noproxy', + '_cache_timeout', + '_error_time' + ], + '' + ) + ); + + // Initialize cache + $cacheFac = new CacheFactory(); + + $cache = $cacheFac->create(Configuration::getConfig('cache', 'type')); + $cache->setScope(''); + $cache->purgeCache(86400); // 24 hours + $cache->setKey($cache_params); + + $items = []; + $infos = []; + $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 = []; + + foreach ($items as $item) { + $feedItems[] = new \FeedItem($item); + } + + $items = $feedItems; + } + + $infos = [ + 'name' => $bridge->getName(), + 'uri' => $bridge->getURI(), + 'donationUri' => $bridge->getDonationURI(), + 'icon' => $bridge->getIcon() + ]; + } catch (\Throwable $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)); + + $message = sprintf( + 'Bridge returned error %s! (%s)', + $e->getCode(), + $this->userData['_error_time'] + ); + $item->setTitle($message); + + $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->getReturnCode($e)); + die(buildTransformException($e, $bridge)); + } + } + } + + // Store data in cache + $cache->saveData([ + 'items' => array_map(function ($i) { + return $i->toArray(); + }, $items), + 'extraInfos' => $infos + ]); + } + + // Data transformation + try { + $formatFac = new FormatFactory(); + $format = $formatFac->create($format); + $format->setItems($items); + $format->setExtraInfos($infos); + $lastModified = $cache->getTime(); + $format->setLastModified($lastModified); + if ($lastModified) { + header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT'); + } + header('Content-Type: ' . $format->getMimeType() . '; charset=' . $format->getCharset()); + + echo $format->stringify(); + } catch (\Throwable $e) { + error_log($e); + header('Content-Type: text/html', true, $e->getCode()); + die(buildTransformException($e, $bridge)); + } + } } |