|
|
|
@@ -19,27 +19,9 @@
|
|
|
|
|
*/
|
|
|
|
|
final class Configuration
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Holds the current release version of RSS-Bridge.
|
|
|
|
|
*
|
|
|
|
|
* Do not access this property directly!
|
|
|
|
|
* Use {@see Configuration::getVersion()} instead.
|
|
|
|
|
*
|
|
|
|
|
* @var string
|
|
|
|
|
*
|
|
|
|
|
* @todo Replace this property by a constant.
|
|
|
|
|
*/
|
|
|
|
|
public static $VERSION = 'dev.2022-06-14';
|
|
|
|
|
private const VERSION = 'dev.2022-06-14';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Holds the configuration data.
|
|
|
|
|
*
|
|
|
|
|
* Do not access this property directly!
|
|
|
|
|
* Use {@see Configuration::getConfig()} instead.
|
|
|
|
|
*
|
|
|
|
|
* @var array|null
|
|
|
|
|
*/
|
|
|
|
|
private static $config = null;
|
|
|
|
|
private static array $config = [];
|
|
|
|
|
|
|
|
|
|
private function __construct()
|
|
|
|
|
{
|
|
|
|
@@ -56,7 +38,7 @@ final class Configuration
|
|
|
|
|
public static function verifyInstallation()
|
|
|
|
|
{
|
|
|
|
|
if (version_compare(\PHP_VERSION, '7.4.0') === -1) {
|
|
|
|
|
self::reportError('RSS-Bridge requires at least PHP version 7.4.0!');
|
|
|
|
|
throw new \Exception('RSS-Bridge requires at least PHP version 7.4.0!');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$errors = [];
|
|
|
|
@@ -97,158 +79,114 @@ final class Configuration
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Loads the configuration from disk and checks if the parameters are valid.
|
|
|
|
|
*
|
|
|
|
|
* Returns an error message and aborts execution if the configuration is invalid.
|
|
|
|
|
*
|
|
|
|
|
* The RSS-Bridge configuration is split into two files:
|
|
|
|
|
* - {@see FILE_CONFIG_DEFAULT} The default configuration file that ships
|
|
|
|
|
* with every release of RSS-Bridge (do not modify this file!).
|
|
|
|
|
* - {@see FILE_CONFIG} The local configuration file that can be modified
|
|
|
|
|
* by server administrators.
|
|
|
|
|
*
|
|
|
|
|
* RSS-Bridge will first load {@see FILE_CONFIG_DEFAULT} into memory and then
|
|
|
|
|
* replace parameters with the contents of {@see FILE_CONFIG}. That way new
|
|
|
|
|
* parameters are automatically initialized with default values and custom
|
|
|
|
|
* configurations can be reduced to the minimum set of parametes necessary
|
|
|
|
|
* (only the ones that changed).
|
|
|
|
|
*
|
|
|
|
|
* The configuration files must be placed in the root folder of RSS-Bridge
|
|
|
|
|
* (next to `index.php`).
|
|
|
|
|
*
|
|
|
|
|
* _Notice_: The configuration is stored in {@see Configuration::$config}.
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public static function loadConfiguration()
|
|
|
|
|
public static function loadConfiguration(array $customConfig = [], array $env = [])
|
|
|
|
|
{
|
|
|
|
|
if (!file_exists(FILE_CONFIG_DEFAULT)) {
|
|
|
|
|
self::reportError('The default configuration file is missing at ' . FILE_CONFIG_DEFAULT);
|
|
|
|
|
if (!file_exists(__DIR__ . '/../config.default.ini.php')) {
|
|
|
|
|
throw new \Exception('The default configuration file is missing');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$config = parse_ini_file(FILE_CONFIG_DEFAULT, true, INI_SCANNER_TYPED);
|
|
|
|
|
$config = parse_ini_file(__DIR__ . '/../config.default.ini.php', true, INI_SCANNER_TYPED);
|
|
|
|
|
if (!$config) {
|
|
|
|
|
self::reportError('Error parsing ' . FILE_CONFIG_DEFAULT);
|
|
|
|
|
throw new \Exception('Error parsing config');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file_exists(FILE_CONFIG)) {
|
|
|
|
|
// Replace default configuration with custom settings
|
|
|
|
|
foreach (parse_ini_file(FILE_CONFIG, true, INI_SCANNER_TYPED) as $header => $section) {
|
|
|
|
|
foreach ($section as $key => $value) {
|
|
|
|
|
$config[$header][$key] = $value;
|
|
|
|
|
}
|
|
|
|
|
foreach ($config as $header => $section) {
|
|
|
|
|
foreach ($section as $key => $value) {
|
|
|
|
|
self::setConfig($header, $key, $value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (getenv() as $envName => $envValue) {
|
|
|
|
|
// Replace all settings with their respective environment variable if available
|
|
|
|
|
$keyArray = explode('_', $envName);
|
|
|
|
|
if ($keyArray[0] === 'RSSBRIDGE') {
|
|
|
|
|
$header = strtolower($keyArray[1]);
|
|
|
|
|
$key = strtolower($keyArray[2]);
|
|
|
|
|
foreach ($customConfig as $header => $section) {
|
|
|
|
|
foreach ($section as $key => $value) {
|
|
|
|
|
self::setConfig($header, $key, $value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach ($env as $envName => $envValue) {
|
|
|
|
|
$nameParts = explode('_', $envName);
|
|
|
|
|
if ($nameParts[0] === 'RSSBRIDGE') {
|
|
|
|
|
$header = $nameParts[1];
|
|
|
|
|
$key = $nameParts[2];
|
|
|
|
|
if ($envValue === 'true' || $envValue === 'false') {
|
|
|
|
|
$envValue = filter_var($envValue, FILTER_VALIDATE_BOOLEAN);
|
|
|
|
|
}
|
|
|
|
|
$config[$header][$key] = $envValue;
|
|
|
|
|
self::setConfig($header, $key, $envValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self::$config = $config;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!is_string(self::getConfig('system', 'timezone'))
|
|
|
|
|
|| !in_array(self::getConfig('system', 'timezone'), timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))
|
|
|
|
|
) {
|
|
|
|
|
self::reportConfigurationError('system', 'timezone');
|
|
|
|
|
self::throwConfigError('system', 'timezone');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_string(self::getConfig('proxy', 'url'))) {
|
|
|
|
|
self::reportConfigurationError('proxy', 'url', 'Is not a valid string');
|
|
|
|
|
self::throwConfigError('proxy', 'url', 'Is not a valid string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_bool(self::getConfig('proxy', 'by_bridge'))) {
|
|
|
|
|
self::reportConfigurationError('proxy', 'by_bridge', 'Is not a valid Boolean');
|
|
|
|
|
self::throwConfigError('proxy', 'by_bridge', 'Is not a valid Boolean');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_string(self::getConfig('proxy', 'name'))) {
|
|
|
|
|
/** Name of the proxy server */
|
|
|
|
|
self::reportConfigurationError('proxy', 'name', 'Is not a valid string');
|
|
|
|
|
self::throwConfigError('proxy', 'name', 'Is not a valid string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_string(self::getConfig('cache', 'type'))) {
|
|
|
|
|
self::reportConfigurationError('cache', 'type', 'Is not a valid string');
|
|
|
|
|
self::throwConfigError('cache', 'type', 'Is not a valid string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_bool(self::getConfig('cache', 'custom_timeout'))) {
|
|
|
|
|
self::reportConfigurationError('cache', 'custom_timeout', 'Is not a valid Boolean');
|
|
|
|
|
self::throwConfigError('cache', 'custom_timeout', 'Is not a valid Boolean');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_bool(self::getConfig('authentication', 'enable'))) {
|
|
|
|
|
self::reportConfigurationError('authentication', 'enable', 'Is not a valid Boolean');
|
|
|
|
|
self::throwConfigError('authentication', 'enable', 'Is not a valid Boolean');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!self::getConfig('authentication', 'username')) {
|
|
|
|
|
self::reportConfigurationError('authentication', 'username', 'Is not a valid string');
|
|
|
|
|
self::throwConfigError('authentication', 'username', 'Is not a valid string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! self::getConfig('authentication', 'password')) {
|
|
|
|
|
self::reportConfigurationError('authentication', 'password', 'Is not a valid string');
|
|
|
|
|
self::throwConfigError('authentication', 'password', 'Is not a valid string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!empty(self::getConfig('admin', 'email'))
|
|
|
|
|
&& !filter_var(self::getConfig('admin', 'email'), FILTER_VALIDATE_EMAIL)
|
|
|
|
|
) {
|
|
|
|
|
self::reportConfigurationError('admin', 'email', 'Is not a valid email address');
|
|
|
|
|
self::throwConfigError('admin', 'email', 'Is not a valid email address');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_bool(self::getConfig('admin', 'donations'))) {
|
|
|
|
|
self::reportConfigurationError('admin', 'donations', 'Is not a valid Boolean');
|
|
|
|
|
self::throwConfigError('admin', 'donations', 'Is not a valid Boolean');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_string(self::getConfig('error', 'output'))) {
|
|
|
|
|
self::reportConfigurationError('error', 'output', 'Is not a valid String');
|
|
|
|
|
self::throwConfigError('error', 'output', 'Is not a valid String');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!is_numeric(self::getConfig('error', 'report_limit'))
|
|
|
|
|
|| self::getConfig('error', 'report_limit') < 1
|
|
|
|
|
) {
|
|
|
|
|
self::reportConfigurationError('admin', 'report_limit', 'Value is invalid');
|
|
|
|
|
self::throwConfigError('admin', 'report_limit', 'Value is invalid');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the value of a parameter identified by section and key.
|
|
|
|
|
*
|
|
|
|
|
* @param string $section The section name.
|
|
|
|
|
* @param string $key The property name (key).
|
|
|
|
|
* @return mixed|null The parameter value.
|
|
|
|
|
*/
|
|
|
|
|
public static function getConfig($section, $key)
|
|
|
|
|
public static function getConfig(string $section, string $key)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($section, self::$config) && array_key_exists($key, self::$config[$section])) {
|
|
|
|
|
return self::$config[$section][$key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
return self::$config[strtolower($section)][strtolower($key)] ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static function setConfig(string $section, string $key, $value): void
|
|
|
|
|
{
|
|
|
|
|
self::$config[strtolower($section)][strtolower($key)] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the current version string of RSS-Bridge.
|
|
|
|
|
*
|
|
|
|
|
* This function returns the contents of {@see Configuration::$VERSION} for
|
|
|
|
|
* regular installations and the git branch name and commit id for instances
|
|
|
|
|
* running in a git environment.
|
|
|
|
|
*
|
|
|
|
|
* @return string The version string.
|
|
|
|
|
*/
|
|
|
|
|
public static function getVersion()
|
|
|
|
|
{
|
|
|
|
|
$headFile = __DIR__ . '/../.git/HEAD';
|
|
|
|
|
|
|
|
|
|
// '@' is used to mute open_basedir warning
|
|
|
|
|
if (@is_readable($headFile)) {
|
|
|
|
|
$revisionHashFile = '.git/' . substr(file_get_contents($headFile), 5, -1);
|
|
|
|
|
$parts = explode('/', $revisionHashFile);
|
|
|
|
@@ -260,39 +198,11 @@ final class Configuration
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Configuration::$VERSION;
|
|
|
|
|
return self::VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reports an configuration error for the specified section and key to the
|
|
|
|
|
* user and ends execution
|
|
|
|
|
*
|
|
|
|
|
* @param string $section The section name
|
|
|
|
|
* @param string $key The configuration key
|
|
|
|
|
* @param string $message An optional message to the user
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
private static function reportConfigurationError($section, $key, $message = '')
|
|
|
|
|
private static function throwConfigError($section, $key, $message = '')
|
|
|
|
|
{
|
|
|
|
|
$report = "Parameter [{$section}] => \"{$key}\" is invalid!" . PHP_EOL;
|
|
|
|
|
|
|
|
|
|
if (file_exists(FILE_CONFIG)) {
|
|
|
|
|
$report .= 'Please check your configuration file at ' . FILE_CONFIG . PHP_EOL;
|
|
|
|
|
} elseif (!file_exists(FILE_CONFIG_DEFAULT)) {
|
|
|
|
|
$report .= 'The default configuration file is missing at ' . FILE_CONFIG_DEFAULT . PHP_EOL;
|
|
|
|
|
} else {
|
|
|
|
|
$report .= 'The default configuration file is broken.' . PHP_EOL
|
|
|
|
|
. 'Restore the original file from ' . REPOSITORY . PHP_EOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$report .= $message;
|
|
|
|
|
self::reportError($report);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static function reportError($message)
|
|
|
|
|
{
|
|
|
|
|
throw new \Exception(sprintf('Configuration error: %s', $message));
|
|
|
|
|
throw new \Exception("Config [$section] => [$key] is invalid. $message");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|