mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-06-11 00:50:58 +02:00
.github
actions
bridges
cache
caches
config
contrib
docs
formats
lib
ActionFactory.php
ActionInterface.php
Authentication.php
BridgeAbstract.php
BridgeCard.php
BridgeFactory.php
BridgeInterface.php
BridgeList.php
CacheFactory.php
CacheInterface.php
Configuration.php
Debug.php
FeedExpander.php
FeedItem.php
FormatAbstract.php
FormatFactory.php
FormatInterface.php
ParameterValidator.php
XPathAbstract.php
contents.php
error.php
html.php
php8backports.php
rssbridge.php
static
templates
tests
vendor
.dockerignore
.git-blame-ignore-revs
.gitattributes
.gitignore
CONTRIBUTORS.md
Dockerfile
README.md
UNLICENSE
app.json
composer.json
composer.lock
config.default.ini.php
docker-bake.hcl
docker-entrypoint.sh
index.php
phpcompatibility.xml
phpcs.xml
phpunit.xml
scalingo.json
whitelist.default.txt
* docs: Do not use constant names when referring to config options The options are customizable using a config file and no longer hardcoded in index.php since8ac8e08abf
* Do not use constants for configuration Since <8ac8e08abf
>, they are just set to the configuration object values.
319 lines
11 KiB
PHP
319 lines
11 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
|
|
*/
|
|
|
|
/**
|
|
* Configuration module for RSS-Bridge.
|
|
*
|
|
* This class implements a configuration module for RSS-Bridge.
|
|
*/
|
|
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';
|
|
|
|
/**
|
|
* Holds the configuration data.
|
|
*
|
|
* Do not access this property directly!
|
|
* Use {@see Configuration::getConfig()} instead.
|
|
*
|
|
* @var array|null
|
|
*/
|
|
private static $config = null;
|
|
|
|
/**
|
|
* Throw an exception when trying to create a new instance of this class.
|
|
*
|
|
* @throws \LogicException if called.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
throw new \LogicException('Can\'t create object of this class!');
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public static function verifyInstallation()
|
|
{
|
|
// Check PHP version
|
|
// PHP Supported Versions: https://www.php.net/supported-versions.php
|
|
if (version_compare(PHP_VERSION, '7.4.0') === -1) {
|
|
self::reportError('RSS-Bridge requires at least PHP version 7.4.0!');
|
|
}
|
|
|
|
// Extensions check
|
|
|
|
// OpenSSL: https://www.php.net/manual/en/book.openssl.php
|
|
if (!extension_loaded('openssl')) {
|
|
self::reportError('"openssl" extension not loaded. Please check "php.ini"');
|
|
}
|
|
|
|
// libxml: https://www.php.net/manual/en/book.libxml.php
|
|
if (!extension_loaded('libxml')) {
|
|
self::reportError('"libxml" extension not loaded. Please check "php.ini"');
|
|
}
|
|
|
|
// Multibyte String (mbstring): https://www.php.net/manual/en/book.mbstring.php
|
|
if (!extension_loaded('mbstring')) {
|
|
self::reportError('"mbstring" extension not loaded. Please check "php.ini"');
|
|
}
|
|
|
|
// SimpleXML: https://www.php.net/manual/en/book.simplexml.php
|
|
if (!extension_loaded('simplexml')) {
|
|
self::reportError('"simplexml" extension not loaded. Please check "php.ini"');
|
|
}
|
|
|
|
// Client URL Library (curl): https://www.php.net/manual/en/book.curl.php
|
|
// 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')))) {
|
|
self::reportError('"curl" extension not loaded. Please check "php.ini"');
|
|
}
|
|
|
|
// JavaScript Object Notation (json): https://www.php.net/manual/en/book.json.php
|
|
if (!extension_loaded('json')) {
|
|
self::reportError('"json" extension not loaded. Please check "php.ini"');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
if (!file_exists(FILE_CONFIG_DEFAULT)) {
|
|
self::reportError('The default configuration file is missing at ' . FILE_CONFIG_DEFAULT);
|
|
}
|
|
|
|
Configuration::$config = parse_ini_file(FILE_CONFIG_DEFAULT, true, INI_SCANNER_TYPED);
|
|
if (!Configuration::$config) {
|
|
self::reportError('Error parsing ' . FILE_CONFIG_DEFAULT);
|
|
}
|
|
|
|
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) {
|
|
Configuration::$config[$header][$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (getenv() as $envkey => $value) {
|
|
// Replace all settings with their respective environment variable if available
|
|
$keyArray = explode('_', $envkey);
|
|
if ($keyArray[0] === 'RSSBRIDGE') {
|
|
$header = strtolower($keyArray[1]);
|
|
$key = strtolower($keyArray[2]);
|
|
if ($value === 'true' || $value === 'false') {
|
|
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
|
}
|
|
Configuration::$config[$header][$key] = $value;
|
|
}
|
|
}
|
|
|
|
if (
|
|
!is_string(self::getConfig('system', 'timezone'))
|
|
|| !in_array(self::getConfig('system', 'timezone'), timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))
|
|
) {
|
|
self::reportConfigurationError('system', 'timezone');
|
|
}
|
|
|
|
date_default_timezone_set(self::getConfig('system', 'timezone'));
|
|
|
|
if (!is_string(self::getConfig('proxy', 'url'))) {
|
|
/** URL of the proxy server */
|
|
self::reportConfigurationError('proxy', 'url', 'Is not a valid string');
|
|
}
|
|
|
|
/** True if proxy usage can be enabled selectively for each bridge */
|
|
if (!is_bool(self::getConfig('proxy', 'by_bridge'))) {
|
|
self::reportConfigurationError('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');
|
|
}
|
|
|
|
if (!is_string(self::getConfig('cache', 'type'))) {
|
|
self::reportConfigurationError('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');
|
|
}
|
|
|
|
/** True if the cache timeout can be specified by the user */
|
|
define('CUSTOM_CACHE_TIMEOUT', self::getConfig('cache', 'custom_timeout'));
|
|
|
|
if (!is_bool(self::getConfig('authentication', 'enable'))) {
|
|
self::reportConfigurationError('authentication', 'enable', 'Is not a valid Boolean');
|
|
}
|
|
|
|
if (!is_string(self::getConfig('authentication', 'username'))) {
|
|
self::reportConfigurationError('authentication', 'username', 'Is not a valid string');
|
|
}
|
|
|
|
if (!is_string(self::getConfig('authentication', 'password'))) {
|
|
self::reportConfigurationError('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');
|
|
}
|
|
|
|
if (!is_bool(self::getConfig('admin', 'donations'))) {
|
|
self::reportConfigurationError('admin', 'donations', 'Is not a valid Boolean');
|
|
}
|
|
|
|
if (!is_string(self::getConfig('error', 'output'))) {
|
|
self::reportConfigurationError('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');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (array_key_exists($section, self::$config) && array_key_exists($key, self::$config[$section])) {
|
|
return self::$config[$section][$key];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 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 = PATH_ROOT . '.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);
|
|
|
|
if (isset($parts[3])) {
|
|
$branchName = $parts[3];
|
|
if (file_exists($revisionHashFile)) {
|
|
return 'git.' . $branchName . '.' . substr(file_get_contents($revisionHashFile), 0, 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Configuration::$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 = '')
|
|
{
|
|
$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);
|
|
}
|
|
|
|
/**
|
|
* Reports an error message to the user and ends execution
|
|
*
|
|
* @param string $message The error message
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function reportError($message)
|
|
{
|
|
http_response_code(500);
|
|
print render('error.html.php', [
|
|
'message' => "Configuration error: $message",
|
|
]);
|
|
exit;
|
|
}
|
|
}
|