2018-06-27 20:09:41 +03:00
|
|
|
<?php
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* Configuration module for RSS-Bridge.
|
|
|
|
*
|
|
|
|
* This class implements a configuration module for RSS-Bridge.
|
|
|
|
*/
|
2018-11-18 17:33:02 +03:00
|
|
|
final class Configuration
|
|
|
|
{
|
2023-09-24 21:53:07 +03:00
|
|
|
private const VERSION = '2023-09-24';
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2022-09-04 05:50:01 +03:00
|
|
|
private static $config = [];
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2022-08-06 23:46:28 +03:00
|
|
|
private function __construct()
|
2018-11-18 17:29:50 +03:00
|
|
|
{
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* Verifies the current installation of RSS-Bridge and PHP.
|
|
|
|
*
|
|
|
|
* Returns an error message and aborts execution if the installation does
|
|
|
|
* not satisfy the requirements of RSS-Bridge.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2018-06-27 20:09:41 +03:00
|
|
|
public static function verifyInstallation()
|
|
|
|
{
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors = [];
|
2022-07-05 17:39:44 +03:00
|
|
|
|
|
|
|
// OpenSSL: https://www.php.net/manual/en/book.openssl.php
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!extension_loaded('openssl')) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'openssl extension not loaded';
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-07-05 17:39:44 +03:00
|
|
|
// libxml: https://www.php.net/manual/en/book.libxml.php
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!extension_loaded('libxml')) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'libxml extension not loaded';
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-07-05 17:39:44 +03:00
|
|
|
// Multibyte String (mbstring): https://www.php.net/manual/en/book.mbstring.php
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!extension_loaded('mbstring')) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'mbstring extension not loaded';
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-07-05 17:39:44 +03:00
|
|
|
// SimpleXML: https://www.php.net/manual/en/book.simplexml.php
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!extension_loaded('simplexml')) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'simplexml extension not loaded';
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-07-05 17:39:44 +03:00
|
|
|
// Client URL Library (curl): https://www.php.net/manual/en/book.curl.php
|
2018-12-27 00:31:30 +03:00
|
|
|
// Allow RSS-Bridge to run without curl module in CLI mode without root certificates
|
|
|
|
if (!extension_loaded('curl') && !(php_sapi_name() === 'cli' && empty(ini_get('curl.cainfo')))) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'curl extension not loaded';
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-07-05 17:39:44 +03:00
|
|
|
// JavaScript Object Notation (json): https://www.php.net/manual/en/book.json.php
|
2018-08-22 18:21:20 +03:00
|
|
|
if (!extension_loaded('json')) {
|
2022-08-06 23:46:28 +03:00
|
|
|
$errors[] = 'json extension not loaded';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($errors) {
|
|
|
|
throw new \Exception(sprintf('Configuration error: %s', implode(', ', $errors)));
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2022-08-23 22:19:53 +03:00
|
|
|
public static function loadConfiguration(array $customConfig = [], array $env = [])
|
2018-06-27 20:09:41 +03:00
|
|
|
{
|
2022-08-23 22:19:53 +03:00
|
|
|
if (!file_exists(__DIR__ . '/../config.default.ini.php')) {
|
|
|
|
throw new \Exception('The default configuration file is missing');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2022-08-23 22:19:53 +03:00
|
|
|
$config = parse_ini_file(__DIR__ . '/../config.default.ini.php', true, INI_SCANNER_TYPED);
|
2022-08-02 16:03:54 +03:00
|
|
|
if (!$config) {
|
2022-08-23 22:19:53 +03:00
|
|
|
throw new \Exception('Error parsing config');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2022-08-23 22:19:53 +03:00
|
|
|
foreach ($config as $header => $section) {
|
|
|
|
foreach ($section as $key => $value) {
|
|
|
|
self::setConfig($header, $key, $value);
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
2022-08-23 22:19:53 +03:00
|
|
|
foreach ($customConfig as $header => $section) {
|
|
|
|
foreach ($section as $key => $value) {
|
|
|
|
self::setConfig($header, $key, $value);
|
|
|
|
}
|
|
|
|
}
|
2023-06-11 04:16:03 +03:00
|
|
|
|
|
|
|
if (file_exists(__DIR__ . '/../DEBUG')) {
|
|
|
|
// The debug mode has been moved to config. Preserve existing installs which has this DEBUG file.
|
|
|
|
self::setConfig('system', 'enable_debug_mode', true);
|
|
|
|
$debug = trim(file_get_contents(__DIR__ . '/../DEBUG'));
|
|
|
|
if ($debug) {
|
|
|
|
self::setConfig('system', 'debug_mode_whitelist', explode("\n", str_replace("\r", '', $debug)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_exists(__DIR__ . '/../whitelist.txt')) {
|
2023-07-07 12:25:36 +03:00
|
|
|
$enabledBridges = trim(file_get_contents(__DIR__ . '/../whitelist.txt'));
|
|
|
|
if ($enabledBridges === '*') {
|
2023-06-11 04:16:03 +03:00
|
|
|
self::setConfig('system', 'enabled_bridges', ['*']);
|
|
|
|
} else {
|
2023-07-31 00:26:59 +03:00
|
|
|
self::setConfig('system', 'enabled_bridges', array_filter(array_map('trim', explode("\n", $enabledBridges))));
|
2023-06-11 04:16:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-23 22:19:53 +03:00
|
|
|
foreach ($env as $envName => $envValue) {
|
|
|
|
$nameParts = explode('_', $envName);
|
|
|
|
if ($nameParts[0] === 'RSSBRIDGE') {
|
2022-09-13 00:14:11 +03:00
|
|
|
if (count($nameParts) < 3) {
|
|
|
|
// Invalid env name
|
|
|
|
continue;
|
|
|
|
}
|
2023-06-11 04:16:03 +03:00
|
|
|
|
|
|
|
// The variable is named $header but it's actually the section in config.ini.php
|
2022-08-23 22:19:53 +03:00
|
|
|
$header = $nameParts[1];
|
2023-06-11 04:16:03 +03:00
|
|
|
|
|
|
|
// Recombine the key if it had multiple underscores
|
|
|
|
$key = implode('_', array_slice($nameParts, 2));
|
2023-07-02 07:47:21 +03:00
|
|
|
$key = strtolower($key);
|
2023-06-11 04:16:03 +03:00
|
|
|
|
|
|
|
// Handle this specifically because it's an array
|
|
|
|
if ($key === 'enabled_bridges') {
|
|
|
|
$envValue = explode(',', $envValue);
|
|
|
|
$envValue = array_map('trim', $envValue);
|
|
|
|
}
|
|
|
|
|
2022-08-02 16:03:54 +03:00
|
|
|
if ($envValue === 'true' || $envValue === 'false') {
|
|
|
|
$envValue = filter_var($envValue, FILTER_VALIDATE_BOOLEAN);
|
2021-12-03 09:15:08 +03:00
|
|
|
}
|
2023-06-11 04:16:03 +03:00
|
|
|
|
2022-08-23 22:19:53 +03:00
|
|
|
self::setConfig($header, $key, $envValue);
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2021-12-03 09:15:08 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2023-06-11 04:16:03 +03:00
|
|
|
if (!is_array(self::getConfig('system', 'enabled_bridges'))) {
|
|
|
|
self::throwConfigError('system', 'enabled_bridges', 'Is not an array');
|
2023-06-02 21:22:09 +03:00
|
|
|
}
|
|
|
|
|
2019-06-07 20:19:36 +03:00
|
|
|
if (
|
|
|
|
!is_string(self::getConfig('system', 'timezone'))
|
|
|
|
|| !in_array(self::getConfig('system', 'timezone'), timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))
|
2022-07-01 16:10:30 +03:00
|
|
|
) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('system', 'timezone');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2023-06-02 21:22:09 +03:00
|
|
|
if (!is_bool(self::getConfig('system', 'enable_debug_mode'))) {
|
|
|
|
self::throwConfigError('system', 'enable_debug_mode', 'Is not a valid Boolean');
|
|
|
|
}
|
|
|
|
if (!is_array(self::getConfig('system', 'debug_mode_whitelist') ?: [])) {
|
|
|
|
self::throwConfigError('system', 'debug_mode_whitelist', 'Is not a valid array');
|
|
|
|
}
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!is_string(self::getConfig('proxy', 'url'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('proxy', 'url', 'Is not a valid string');
|
2018-11-18 17:52:28 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!is_bool(self::getConfig('proxy', 'by_bridge'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('proxy', 'by_bridge', 'Is not a valid Boolean');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!is_string(self::getConfig('proxy', 'name'))) {
|
2022-07-24 20:26:12 +03:00
|
|
|
/** Name of the proxy server */
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('proxy', 'name', 'Is not a valid string');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2019-02-06 20:52:44 +03:00
|
|
|
if (!is_string(self::getConfig('cache', 'type'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('cache', 'type', 'Is not a valid string');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!is_bool(self::getConfig('cache', 'custom_timeout'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('cache', 'custom_timeout', 'Is not a valid Boolean');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if (!is_bool(self::getConfig('authentication', 'enable'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('authentication', 'enable', 'Is not a valid Boolean');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-08-31 19:16:19 +03:00
|
|
|
if (!is_string(self::getConfig('authentication', 'username'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('authentication', 'username', 'Is not a valid string');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2022-08-31 19:16:19 +03:00
|
|
|
if (!is_string(self::getConfig('authentication', 'password'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('authentication', 'password', 'Is not a valid string');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2018-11-10 20:42:36 +03:00
|
|
|
if (
|
|
|
|
!empty(self::getConfig('admin', 'email'))
|
|
|
|
&& !filter_var(self::getConfig('admin', 'email'), FILTER_VALIDATE_EMAIL)
|
2022-07-01 16:10:30 +03:00
|
|
|
) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('admin', 'email', 'Is not a valid email address');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2021-06-25 22:45:25 +03:00
|
|
|
if (!is_bool(self::getConfig('admin', 'donations'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('admin', 'donations', 'Is not a valid Boolean');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2019-10-31 20:40:51 +03:00
|
|
|
if (!is_string(self::getConfig('error', 'output'))) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('error', 'output', 'Is not a valid String');
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
2023-09-10 22:50:15 +03:00
|
|
|
if (!in_array(self::getConfig('error', 'output'), ['feed', 'http', 'none'])) {
|
|
|
|
self::throwConfigError('error', 'output', 'Invalid output');
|
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2019-10-31 20:49:45 +03:00
|
|
|
if (
|
|
|
|
!is_numeric(self::getConfig('error', 'report_limit'))
|
|
|
|
|| self::getConfig('error', 'report_limit') < 1
|
|
|
|
) {
|
2022-08-23 22:19:53 +03:00
|
|
|
self::throwConfigError('admin', 'report_limit', 'Value is invalid');
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
|
2023-07-05 06:33:22 +03:00
|
|
|
public static function getConfig(string $section, string $key, $default = null)
|
2018-11-18 17:43:29 +03:00
|
|
|
{
|
2023-07-05 06:33:22 +03:00
|
|
|
return self::$config[strtolower($section)][strtolower($key)] ?? $default;
|
2022-08-23 22:19:53 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2022-08-23 22:19:53 +03:00
|
|
|
private static function setConfig(string $section, string $key, $value): void
|
|
|
|
{
|
|
|
|
self::$config[strtolower($section)][strtolower($key)] = $value;
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-06-30 11:24:22 +03:00
|
|
|
public static function getVersion()
|
|
|
|
{
|
2022-08-06 23:46:28 +03:00
|
|
|
$headFile = __DIR__ . '/../.git/HEAD';
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2018-10-27 11:53:45 +03:00
|
|
|
if (@is_readable($headFile)) {
|
2018-06-30 11:24:22 +03:00
|
|
|
$revisionHashFile = '.git/' . substr(file_get_contents($headFile), 5, -1);
|
2020-05-28 00:08:06 +03:00
|
|
|
$parts = explode('/', $revisionHashFile);
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2020-05-28 00:08:06 +03:00
|
|
|
if (isset($parts[3])) {
|
|
|
|
$branchName = $parts[3];
|
|
|
|
if (file_exists($revisionHashFile)) {
|
2022-09-04 08:21:57 +03:00
|
|
|
return sprintf('%s (git.%s.%s)', self::VERSION, $branchName, substr(file_get_contents($revisionHashFile), 0, 7));
|
2018-06-30 11:24:22 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
}
|
|
|
|
}
|
2022-08-23 22:19:53 +03:00
|
|
|
return self::VERSION;
|
2019-06-07 21:27:17 +03:00
|
|
|
}
|
2022-07-01 16:10:30 +03:00
|
|
|
|
2022-08-23 22:19:53 +03:00
|
|
|
private static function throwConfigError($section, $key, $message = '')
|
2019-06-07 21:27:17 +03:00
|
|
|
{
|
2022-08-23 22:19:53 +03:00
|
|
|
throw new \Exception("Config [$section] => [$key] is invalid. $message");
|
2019-06-07 21:27:17 +03:00
|
|
|
}
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|