Rss-Bridge/lib/Bridge.php
logmanoriginal 007ee4d858 [Bridge] Fix broken bridge initialization
Commit e26d61e introduced a bug that causes the error message "The
bridge you [sic!] looking for does not exist." if the bridge name
specified in the query ends on "Bridge"
(i.e. '&bridge=SoundcloudBridge'), while other queries work fine
(i.e. '&bridge=Soundcloud').

This commit fixes that issue by sanitizing the bridge name before
creating the class.

References #922
2018-11-13 17:36:06 +01:00

167 lines
4.1 KiB
PHP

<?php
class Bridge {
static protected $dirBridge;
/**
* Holds the active whitelist.
* Use Bridge::getWhitelist() instead of accessing this parameter directly!
*/
private static $whitelist = array();
public function __construct(){
throw new \LogicException('Please use ' . __CLASS__ . '::create for new object.');
}
/**
* Create a new bridge object
* @param string $nameBridge Defined bridge name you want use
* @return Bridge object dedicated
*/
static public function create($nameBridge){
if(!preg_match('@^[A-Z][a-zA-Z0-9-]*$@', $nameBridge)) {
$message = <<<EOD
'nameBridge' must start with one uppercase character followed or not by
alphanumeric or dash characters!
EOD;
throw new \InvalidArgumentException($message);
}
$nameBridge = Bridge::sanitizeBridgeName($nameBridge) . 'Bridge';
$pathBridge = self::getDir() . $nameBridge . '.php';
if(!file_exists($pathBridge)) {
throw new \Exception('The bridge you are looking for does not exist. It should be at path '
. $pathBridge);
}
require_once $pathBridge;
if((new ReflectionClass($nameBridge))->isInstantiable()) {
return new $nameBridge();
}
return false;
}
static public function setDir($dirBridge){
if(!is_string($dirBridge)) {
throw new \InvalidArgumentException('Dir bridge must be a string.');
}
if(!file_exists($dirBridge)) {
throw new \Exception('Dir bridge does not exist.');
}
self::$dirBridge = $dirBridge;
}
static public function getDir(){
if(is_null(self::$dirBridge)) {
throw new \LogicException(__CLASS__ . ' class need to know bridge path !');
}
return self::$dirBridge;
}
/**
* Lists the available bridges.
* @return array List of the bridges
*/
static public function listBridges(){
static $listBridge = array(); // Initialized on first call
if(empty($listBridge)) {
$dirFiles = scandir(self::getDir());
if($dirFiles !== false) {
foreach($dirFiles as $fileName) {
if(preg_match('@^([^.]+)Bridge\.php$@U', $fileName, $out)) {
$listBridge[] = $out[1];
}
}
}
}
return $listBridge;
}
/**
* @return bool Returns true if the given bridge is whitelisted.
*/
static public function isWhitelisted($name){
return in_array(Bridge::sanitizeBridgeName($name), Bridge::getWhitelist());
}
/**
* On first call reads the whitelist from WHITELIST. Each line in the file
* specifies one bridge that will be placed on the whitelist. An empty file
* disables all bridges. '*' enables all bridges.
*
* @return array Returns a list of whitelisted bridges
*/
public static function getWhitelist() {
static $firstCall = true; // Initialized on first call
if($firstCall) {
// Create initial whitelist or load from disk
if (!file_exists(WHITELIST) && !empty(Bridge::$whitelist)) {
file_put_contents(WHITELIST, implode("\n", Bridge::$whitelist));
} else {
$contents = trim(file_get_contents(WHITELIST));
if($contents === '*') { // Whitelist all bridges
Bridge::$whitelist = Bridge::listBridges();
} else {
Bridge::$whitelist = array_map('Bridge::sanitizeBridgeName', explode("\n", $contents));
}
}
}
return Bridge::$whitelist;
}
public static function setWhitelist($default = array()) {
Bridge::$whitelist = array_map('Bridge::sanitizeBridgeName', $default);
}
/**
* @return string Returns a sanitized bridge name if the given name has been
* found valid, null otherwise.
*/
private static function sanitizeBridgeName($name) {
if(is_string($name)) {
// Trim trailing '.php' if exists
if(preg_match('/(.+)(?:\.php)/', $name, $matches)) {
$name = $matches[1];
}
// Trim trailing 'Bridge' if exists
if(preg_match('/(.+)(?:Bridge)/i', $name, $matches)) {
$name = $matches[1];
}
// The name is valid if a corresponding bridge file is found on disk
if(in_array(strtolower($name), array_map('strtolower', Bridge::listBridges()))) {
$index = array_search(strtolower($name), array_map('strtolower', Bridge::listBridges()));
return Bridge::listBridges()[$index];
}
Debug::log('Invalid bridge name specified: "' . $name . '"!');
}
return null; // Bad parameter
}
}