2018-06-27 20:09:41 +03:00
|
|
|
<?php
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-11-18 17:33:02 +03:00
|
|
|
final class Configuration {
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-01-20 08:17:59 +03:00
|
|
|
public static $VERSION = 'dev.2022-01-20';
|
2018-06-30 11:24:22 +03:00
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* Holds the configuration data.
|
|
|
|
*
|
|
|
|
* Do not access this property directly!
|
|
|
|
* Use {@see Configuration::getConfig()} instead.
|
|
|
|
*
|
|
|
|
* @var array|null
|
|
|
|
*/
|
2018-11-18 17:58:32 +03:00
|
|
|
private static $config = null;
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-18 17:29:50 +03:00
|
|
|
/**
|
|
|
|
* 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!');
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* **Requirements**
|
2021-12-03 02:12:16 +03:00
|
|
|
* - PHP 7.1.0 or higher
|
2018-11-16 23:48:59 +03:00
|
|
|
* - `openssl` extension
|
|
|
|
* - `libxml` extension
|
|
|
|
* - `mbstring` extension
|
|
|
|
* - `simplexml` extension
|
|
|
|
* - `curl` extension
|
|
|
|
* - `json` extension
|
|
|
|
* - The cache folder specified by {@see PATH_CACHE} requires write permission
|
|
|
|
* - The whitelist file specified by {@see WHITELIST} requires write permission
|
|
|
|
*
|
|
|
|
* @link http://php.net/supported-versions.php PHP Supported Versions
|
|
|
|
* @link http://php.net/manual/en/book.openssl.php OpenSSL
|
|
|
|
* @link http://php.net/manual/en/book.libxml.php libxml
|
|
|
|
* @link http://php.net/manual/en/book.mbstring.php Multibyte String (mbstring)
|
|
|
|
* @link http://php.net/manual/en/book.simplexml.php SimpleXML
|
|
|
|
* @link http://php.net/manual/en/book.curl.php Client URL Library (curl)
|
|
|
|
* @link http://php.net/manual/en/book.json.php JavaScript Object Notation (json)
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2018-06-27 20:09:41 +03:00
|
|
|
public static function verifyInstallation() {
|
|
|
|
|
|
|
|
// Check PHP version
|
2021-12-03 02:12:16 +03:00
|
|
|
if(version_compare(PHP_VERSION, '7.1.0') === -1)
|
|
|
|
self::reportError('RSS-Bridge requires at least PHP version 7.1.0!');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
// extensions check
|
|
|
|
if(!extension_loaded('openssl'))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"openssl" extension not loaded. Please check "php.ini"');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!extension_loaded('libxml'))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"libxml" extension not loaded. Please check "php.ini"');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!extension_loaded('mbstring'))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"mbstring" extension not loaded. Please check "php.ini"');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!extension_loaded('simplexml'))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"simplexml" extension not loaded. Please check "php.ini"');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
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'))))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"curl" extension not loaded. Please check "php.ini"');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-08-22 18:21:20 +03:00
|
|
|
if(!extension_loaded('json'))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('"json" extension not loaded. Please check "php.ini"');
|
2018-08-22 18:21:20 +03:00
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* 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:
|
2019-06-07 20:45:47 +03:00
|
|
|
* - {@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.
|
2018-11-16 23:48:59 +03:00
|
|
|
*
|
2019-06-07 20:45:47 +03:00
|
|
|
* 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
|
2018-11-16 23:48:59 +03:00
|
|
|
* 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
|
|
|
|
*/
|
2018-06-27 20:09:41 +03:00
|
|
|
public static function loadConfiguration() {
|
|
|
|
|
2019-06-07 20:45:47 +03:00
|
|
|
if(!file_exists(FILE_CONFIG_DEFAULT))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('The default configuration file is missing at ' . FILE_CONFIG_DEFAULT);
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2019-06-07 20:45:47 +03:00
|
|
|
Configuration::$config = parse_ini_file(FILE_CONFIG_DEFAULT, true, INI_SCANNER_TYPED);
|
2018-06-27 20:09:41 +03:00
|
|
|
if(!Configuration::$config)
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportError('Error parsing ' . FILE_CONFIG_DEFAULT);
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2019-06-07 20:45:47 +03:00
|
|
|
if(file_exists(FILE_CONFIG)) {
|
2018-06-27 20:09:41 +03:00
|
|
|
// Replace default configuration with custom settings
|
2019-06-07 20:45:47 +03:00
|
|
|
foreach(parse_ini_file(FILE_CONFIG, true, INI_SCANNER_TYPED) as $header => $section) {
|
2018-06-27 20:09:41 +03:00
|
|
|
foreach($section as $key => $value) {
|
2020-12-12 19:05:22 +03:00
|
|
|
Configuration::$config[$header][$key] = $value;
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-03 09:15:08 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('system', 'timezone');
|
2019-06-07 20:19:36 +03:00
|
|
|
|
|
|
|
date_default_timezone_set(self::getConfig('system', 'timezone'));
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if(!is_string(self::getConfig('proxy', 'url')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('proxy', 'url', 'Is not a valid string');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-18 17:52:28 +03:00
|
|
|
if(!empty(self::getConfig('proxy', 'url'))) {
|
|
|
|
/** URL of the proxy server */
|
2018-06-27 20:09:41 +03:00
|
|
|
define('PROXY_URL', self::getConfig('proxy', 'url'));
|
2018-11-18 17:52:28 +03:00
|
|
|
}
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!is_bool(self::getConfig('proxy', 'by_bridge')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('proxy', 'by_bridge', 'Is not a valid Boolean');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-18 17:52:28 +03:00
|
|
|
/** True if proxy usage can be enabled selectively for each bridge */
|
2018-06-27 20:09:41 +03:00
|
|
|
define('PROXY_BYBRIDGE', self::getConfig('proxy', 'by_bridge'));
|
|
|
|
|
|
|
|
if(!is_string(self::getConfig('proxy', 'name')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('proxy', 'name', 'Is not a valid string');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-18 17:52:28 +03:00
|
|
|
/** Name of the proxy server */
|
2018-06-27 20:09:41 +03:00
|
|
|
define('PROXY_NAME', self::getConfig('proxy', 'name'));
|
|
|
|
|
2019-02-06 20:52:44 +03:00
|
|
|
if(!is_string(self::getConfig('cache', 'type')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('cache', 'type', 'Is not a valid string');
|
2019-02-06 20:52:44 +03:00
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
if(!is_bool(self::getConfig('cache', 'custom_timeout')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('cache', 'custom_timeout', 'Is not a valid Boolean');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
2018-11-18 17:52:28 +03:00
|
|
|
/** True if the cache timeout can be specified by the user */
|
2018-06-27 20:09:41 +03:00
|
|
|
define('CUSTOM_CACHE_TIMEOUT', self::getConfig('cache', 'custom_timeout'));
|
|
|
|
|
|
|
|
if(!is_bool(self::getConfig('authentication', 'enable')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('authentication', 'enable', 'Is not a valid Boolean');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!is_string(self::getConfig('authentication', 'username')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('authentication', 'username', 'Is not a valid string');
|
2018-06-27 20:09:41 +03:00
|
|
|
|
|
|
|
if(!is_string(self::getConfig('authentication', 'password')))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('authentication', 'password', 'Is not a valid string');
|
2018-06-27 20:09:41 +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))
|
2019-06-07 21:27:17 +03:00
|
|
|
self::reportConfigurationError('admin', 'email', 'Is not a valid email address');
|
2018-11-10 20:42:36 +03:00
|
|
|
|
2021-06-25 22:45:25 +03:00
|
|
|
if(!is_bool(self::getConfig('admin', 'donations')))
|
|
|
|
self::reportConfigurationError('admin', 'donations', 'Is not a valid Boolean');
|
|
|
|
|
2019-10-31 20:40:51 +03:00
|
|
|
if(!is_string(self::getConfig('error', 'output')))
|
|
|
|
self::reportConfigurationError('error', 'output', 'Is not a valid String');
|
|
|
|
|
2019-10-31 20:49:45 +03:00
|
|
|
if(!is_numeric(self::getConfig('error', 'report_limit'))
|
|
|
|
|| self::getConfig('error', 'report_limit') < 1)
|
|
|
|
self::reportConfigurationError('admin', 'report_limit', 'Value is invalid');
|
|
|
|
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
2018-11-18 17:43:29 +03:00
|
|
|
* Returns the value of a parameter identified by section and key.
|
2018-11-16 23:48:59 +03:00
|
|
|
*
|
2018-11-18 17:43:29 +03:00
|
|
|
* @param string $section The section name.
|
2018-11-16 23:48:59 +03:00
|
|
|
* @param string $key The property name (key).
|
|
|
|
* @return mixed|null The parameter value.
|
|
|
|
*/
|
2018-11-18 17:43:29 +03:00
|
|
|
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];
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-11-16 23:48:59 +03:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-06-30 11:24:22 +03:00
|
|
|
public static function getVersion() {
|
|
|
|
|
2018-11-18 17:41:28 +03:00
|
|
|
$headFile = PATH_ROOT . '.git/HEAD';
|
2018-06-30 11:24:22 +03:00
|
|
|
|
2018-10-27 11:53:45 +03:00
|
|
|
// '@' is used to mute open_basedir warning
|
|
|
|
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);
|
|
|
|
|
|
|
|
if(isset($parts[3])) {
|
|
|
|
$branchName = $parts[3];
|
|
|
|
if(file_exists($revisionHashFile)) {
|
|
|
|
return 'git.' . $branchName . '.' . substr(file_get_contents($revisionHashFile), 0, 7);
|
|
|
|
}
|
2018-06-30 11:24:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Configuration::$VERSION;
|
|
|
|
|
|
|
|
}
|
2019-06-07 21:27:17 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
|
|
|
|
header('Content-Type: text/plain', true, 500);
|
|
|
|
die('Configuration error' . PHP_EOL . $message);
|
|
|
|
|
|
|
|
}
|
2018-06-27 20:09:41 +03:00
|
|
|
}
|