23 changed files with 1020 additions and 905 deletions
@ -0,0 +1,382 @@
|
||||
<?php |
||||
|
||||
namespace Friendica\Util; |
||||
|
||||
use Friendica\Core\Config\Configuration; |
||||
|
||||
/** |
||||
* A class which checks and contains the basic |
||||
* environment for the BaseURL (url, urlpath, ssl_policy, hostname, scheme) |
||||
*/ |
||||
class BaseURL |
||||
{ |
||||
/** |
||||
* No SSL necessary |
||||
*/ |
||||
const SSL_POLICY_NONE = 0; |
||||
|
||||
/** |
||||
* SSL is necessary |
||||
*/ |
||||
const SSL_POLICY_FULL = 1; |
||||
|
||||
/** |
||||
* SSL is optional, but preferred |
||||
*/ |
||||
const SSL_POLICY_SELFSIGN = 2; |
||||
|
||||
/** |
||||
* Define the Default SSL scheme |
||||
*/ |
||||
const DEFAULT_SSL_SCHEME = self::SSL_POLICY_SELFSIGN; |
||||
|
||||
/** |
||||
* The Friendica Config |
||||
* @var Configuration |
||||
*/ |
||||
private $config; |
||||
|
||||
/** |
||||
* The server side variables |
||||
* @var array |
||||
*/ |
||||
private $server; |
||||
|
||||
/** |
||||
* The hostname of the Base URL |
||||
* @var string |
||||
*/ |
||||
private $hostname; |
||||
|
||||
/** |
||||
* The SSL_POLICY of the Base URL |
||||
* @var int |
||||
*/ |
||||
private $sslPolicy; |
||||
|
||||
/** |
||||
* The URL sub-path of the Base URL |
||||
* @var string |
||||
*/ |
||||
private $urlPath; |
||||
|
||||
/** |
||||
* The full URL |
||||
* @var string |
||||
*/ |
||||
private $url; |
||||
|
||||
/** |
||||
* The current scheme of this call |
||||
* @var string |
||||
*/ |
||||
private $scheme; |
||||
|
||||
/** |
||||
* Returns the hostname of this node |
||||
* @return string |
||||
*/ |
||||
public function getHostname() |
||||
{ |
||||
return $this->hostname; |
||||
} |
||||
|
||||
/** |
||||
* Returns the current scheme of this call |
||||
* @return string |
||||
*/ |
||||
public function getScheme() |
||||
{ |
||||
return $this->scheme; |
||||
} |
||||
|
||||
/** |
||||
* Returns the SSL policy of this node |
||||
* @return int |
||||
*/ |
||||
public function getSSLPolicy() |
||||
{ |
||||
return $this->sslPolicy; |
||||
} |
||||
|
||||
/** |
||||
* Returns the sub-path of this URL |
||||
* @return string |
||||
*/ |
||||
public function getUrlPath() |
||||
{ |
||||
return $this->urlPath; |
||||
} |
||||
|
||||
/** |
||||
* Returns the full URL of this call |
||||
* |
||||
* Note: $ssl parameter value doesn't directly correlate with the resulting protocol |
||||
* |
||||
* @param bool $ssl True, if ssl should get used |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function get($ssl = false) |
||||
{ |
||||
if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) { |
||||
return Network::switchScheme($this->url); |
||||
} |
||||
|
||||
return $this->url; |
||||
} |
||||
|
||||
/** |
||||
* Save current parts of the base Url |
||||
* |
||||
* @param string? $hostname |
||||
* @param int? $sslPolicy |
||||
* @param string? $urlPath |
||||
* |
||||
* @return bool true, if successful |
||||
*/ |
||||
public function save($hostname = null, $sslPolicy = null, $urlPath = null) |
||||
{ |
||||
$currHostname = $this->hostname; |
||||
$currSSLPolicy = $this->sslPolicy; |
||||
$currURLPath = $this->urlPath; |
||||
|
||||
if (!empty($hostname) && $hostname !== $this->hostname) { |
||||
if ($this->config->set('config', 'hostname', $hostname)) { |
||||
$this->hostname = $hostname; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { |
||||
if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { |
||||
$this->sslPolicy = $sslPolicy; |
||||
} else { |
||||
$this->hostname = $currHostname; |
||||
$this->config->set('config', 'hostname', $this->hostname); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
if (isset($urlPath) && $urlPath !== $this->urlPath) { |
||||
if ($this->config->set('system', 'urlpath', $urlPath)) { |
||||
$this->urlPath = $urlPath; |
||||
} else { |
||||
$this->hostname = $currHostname; |
||||
$this->sslPolicy = $currSSLPolicy; |
||||
$this->config->set('config', 'hostname', $this->hostname); |
||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
$this->determineBaseUrl(); |
||||
if (!$this->config->set('system', 'url', $this->url)) { |
||||
$this->hostname = $currHostname; |
||||
$this->sslPolicy = $currSSLPolicy; |
||||
$this->urlPath = $currURLPath; |
||||
$this->determineBaseUrl(); |
||||
|
||||
$this->config->set('config', 'hostname', $this->hostname); |
||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy); |
||||
$this->config->set('system', 'urlpath', $this->urlPath); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Save the current url as base URL |
||||
* |
||||
* @param $url |
||||
* |
||||
* @return bool true, if the save was successful |
||||
*/ |
||||
public function saveByURL($url) |
||||
{ |
||||
$parsed = @parse_url($url); |
||||
|
||||
if (empty($parsed)) { |
||||
return false; |
||||
} |
||||
|
||||
$hostname = $parsed['host']; |
||||
if (!empty($hostname) && !empty($parsed['port'])) { |
||||
$hostname .= ':' . $parsed['port']; |
||||
} |
||||
|
||||
$urlPath = null; |
||||
if (!empty($parsed['path'])) { |
||||
$urlPath = trim($parsed['path'], '\\/'); |
||||
} |
||||
|
||||
$sslPolicy = null; |
||||
if (!empty($parsed['scheme'])) { |
||||
if ($parsed['scheme'] == 'https') { |
||||
$sslPolicy = BaseURL::SSL_POLICY_FULL; |
||||
} |
||||
} |
||||
|
||||
return $this->save($hostname, $sslPolicy, $urlPath); |
||||
} |
||||
|
||||
/** |
||||
* Checks, if a redirect to the HTTPS site would be necessary |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function checkRedirectHttps() |
||||
{ |
||||
return $this->config->get('system', 'force_ssl') |
||||
&& ($this->getScheme() == "http") |
||||
&& intval($this->getSSLPolicy()) == BaseURL::SSL_POLICY_FULL |
||||
&& strpos($this->get(), 'https://') === 0 |
||||
&& !empty($this->server['REQUEST_METHOD']) |
||||
&& $this->server['REQUEST_METHOD'] === 'GET'; |
||||
} |
||||
|
||||
/** |
||||
* @param Configuration $config The Friendica configuration |
||||
* @param array $server The $_SERVER array |
||||
*/ |
||||
public function __construct(Configuration $config, array $server) |
||||
{ |
||||
$this->config = $config; |
||||
$this->server = $server; |
||||
|
||||
$this->determineSchema(); |
||||
$this->checkConfig(); |
||||
} |
||||
|
||||
/** |
||||
* Check the current config during loading |
||||
*/ |
||||
public function checkConfig() |
||||
{ |
||||
$this->hostname = $this->config->get('config', 'hostname'); |
||||
$this->urlPath = $this->config->get('system', 'urlpath'); |
||||
$this->sslPolicy = $this->config->get('system', 'ssl_policy'); |
||||
$this->url = $this->config->get('system', 'url'); |
||||
|
||||
if (empty($this->hostname)) { |
||||
$this->determineHostname(); |
||||
|
||||
if (!empty($this->hostname)) { |
||||
$this->config->set('config', 'hostname', $this->hostname); |
||||
} |
||||
} |
||||
|
||||
if (!isset($this->urlPath)) { |
||||
$this->determineURLPath(); |
||||
$this->config->set('system', 'urlpath', $this->urlPath); |
||||
} |
||||
|
||||
if (!isset($this->sslPolicy)) { |
||||
if ($this->scheme == 'https') { |
||||
$this->sslPolicy = self::SSL_POLICY_FULL; |
||||
} else { |
||||
$this->sslPolicy = self::DEFAULT_SSL_SCHEME; |
||||
} |
||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy); |
||||
} |
||||
|
||||
if (empty($this->url)) { |
||||
$this->determineBaseUrl(); |
||||
|
||||
if (!empty($url)) { |
||||
$this->config->set('system', 'url', $this->url); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determines the hostname of this node if not set already |
||||
*/ |
||||
private function determineHostname() |
||||
{ |
||||
$this->hostname = ''; |
||||
|
||||
if (!empty($this->server['SERVER_NAME'])) { |
||||
$this->hostname = $this->server['SERVER_NAME']; |
||||
|
||||
if (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] != 80 && $this->server['SERVER_PORT'] != 443) { |
||||
$this->hostname .= ':' . $this->server['SERVER_PORT']; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Figure out if we are running at the top of a domain or in a sub-directory |
||||
*/ |
||||
private function determineURLPath() |
||||
{ |
||||
$this->urlPath = ''; |
||||
|
||||
/* |
||||
* The automatic path detection in this function is currently deactivated, |
||||
* see issue https://github.com/friendica/friendica/issues/6679 |
||||
* |
||||
* The problem is that the function seems to be confused with some url. |
||||
* These then confuses the detection which changes the url path. |
||||
*/ |
||||
|
||||
/* Relative script path to the web server root |
||||
* Not all of those $_SERVER properties can be present, so we do by inverse priority order |
||||
*/ |
||||
$relative_script_path = ''; |
||||
$relative_script_path = defaults($this->server, 'REDIRECT_URL', $relative_script_path); |
||||
$relative_script_path = defaults($this->server, 'REDIRECT_URI', $relative_script_path); |
||||
$relative_script_path = defaults($this->server, 'REDIRECT_SCRIPT_URL', $relative_script_path); |
||||
$relative_script_path = defaults($this->server, 'SCRIPT_URL', $relative_script_path); |
||||
$relative_script_path = defaults($this->server, 'REQUEST_URI', $relative_script_path); |
||||
|
||||
/* $relative_script_path gives /relative/path/to/friendica/module/parameter |
||||
* QUERY_STRING gives pagename=module/parameter |
||||
* |
||||
* To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING |
||||
*/ |
||||
if (!empty($relative_script_path)) { |
||||
// Module |
||||
if (!empty($this->server['QUERY_STRING'])) { |
||||
$this->urlPath = trim(rdirname($relative_script_path, substr_count(trim($this->server['QUERY_STRING'], '/'), '/') + 1), '/'); |
||||
} else { |
||||
// Root page |
||||
$this->urlPath = trim($relative_script_path, '/'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determine the full URL based on all parts |
||||
*/ |
||||
private function determineBaseUrl() |
||||
{ |
||||
$scheme = 'http'; |
||||
|
||||
if ($this->sslPolicy == self::SSL_POLICY_FULL) { |
||||
$scheme = 'https'; |
||||
} |
||||
|
||||
$this->url = $scheme . '://' . $this->hostname . (!empty($this->urlPath) ? '/' . $this->urlPath : '' ); |
||||
} |
||||
|
||||
/** |
||||
* Determine the scheme of the current used link |
||||
*/ |
||||
private function determineSchema() |
||||
{ |
||||
$this->scheme = 'http'; |
||||
|
||||
if (!empty($this->server['HTTPS']) || |
||||
!empty($this->server['HTTP_FORWARDED']) && preg_match('/proto=https/', $this->server['HTTP_FORWARDED']) || |
||||
!empty($this->server['HTTP_X_FORWARDED_PROTO']) && $this->server['HTTP_X_FORWARDED_PROTO'] == 'https' || |
||||
!empty($this->server['HTTP_X_FORWARDED_SSL']) && $this->server['HTTP_X_FORWARDED_SSL'] == 'on' || |
||||
!empty($this->server['FRONT_END_HTTPS']) && $this->server['FRONT_END_HTTPS'] == 'on' || |
||||
!empty($this->server['SERVER_PORT']) && (intval($this->server['SERVER_PORT']) == 443) // XXX: reasonable assumption, but isn't this hardcoding too much? |
||||
) { |
||||
$this->scheme = 'https'; |
||||
} |
||||
} |
||||
} |
@ -1,341 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Friendica\Util\Config; |
||||
|
||||
/** |
||||
* The ConfigFileSaver saves specific variables into the config-files |
||||
* |
||||
* It is capable of loading the following config files: |
||||
* - *.config.php (current) |
||||
* - *.ini.php (deprecated) |
||||
* - *.htconfig.php (deprecated) |
||||
*/ |
||||
class ConfigFileSaver extends ConfigFileManager |
||||
{ |
||||
/** |
||||
* The standard indentation for config files |
||||
* @var string |
||||
*/ |
||||
const INDENT = "\t"; |
||||
|
||||
/** |
||||
* The settings array to save to |
||||
* @var array |
||||
*/ |
||||
private $settings = []; |
||||
|
||||
/** |
||||
* Adds a given value to the config file |
||||
* Either it replaces the current value or it will get added |
||||
* |
||||
* @param string $cat The configuration category |
||||
* @param string $key The configuration key |
||||
* @param string $value The new value |
||||
*/ |
||||
public function addConfigValue($cat, $key, $value) |
||||
{ |
||||
$settingsCount = count(array_keys($this->settings)); |
||||
|
||||
for ($i = 0; $i < $settingsCount; $i++) { |
||||
// if already set, overwrite the value |
||||
if ($this->settings[$i]['cat'] === $cat && |
||||
$this->settings[$i]['key'] === $key) { |
||||
$this->settings[$i] = ['cat' => $cat, 'key' => $key, 'value' => $value]; |
||||
return; |
||||
} |
||||
} |
||||
|
||||
$this->settings[] = ['cat' => $cat, 'key' => $key, 'value' => $value]; |
||||
} |
||||
|
||||
/** |
||||
* Resetting all added configuration entries so far |
||||
*/ |
||||
public function reset() |
||||
{ |
||||
$this->settings = []; |
||||
} |
||||
|
||||
/** |
||||
* Save all added configuration entries to the given config files |
||||
* After updating the config entries, all configuration entries will be reseted |
||||
* |
||||
* @param string $name The name of the configuration file (default is empty, which means the default name each type) |
||||
* |
||||
* @return bool true, if at least one configuration file was successfully updated or nothing to do |
||||
*/ |
||||
public function saveToConfigFile($name = '') |
||||
{ |
||||
// If no settings et, return true |
||||
if (count(array_keys($this->settings)) === 0) { |
||||
return true; |
||||
} |
||||
|
||||
$saved = false; |
||||
|
||||
// Check for the *.config.php file inside the /config/ path |
||||
list($reading, $writing) = $this->openFile($this->getConfigFullName($name)); |
||||
if (isset($reading) && isset($writing)) { |
||||
$this->saveConfigFile($reading, $writing); |
||||
// Close the current file handler and rename them |
||||
if ($this->closeFile($this->getConfigFullName($name), $reading, $writing)) { |
||||
// just return true, if everything went fine |
||||
$saved = true; |
||||
} |
||||
} |
||||
|
||||
// Check for the *.ini.php file inside the /config/ path |
||||
list($reading, $writing) = $this->openFile($this->getIniFullName($name)); |
||||
if (isset($reading) && isset($writing)) { |
||||
$this->saveINIConfigFile($reading, $writing); |
||||
// Close the current file handler and rename them |
||||
if ($this->closeFile($this->getIniFullName($name), $reading, $writing)) { |
||||
// just return true, if everything went fine |
||||
$saved = true; |
||||
} |
||||
} |
||||
|
||||
// Check for the *.php file (normally .htconfig.php) inside the / path |
||||
list($reading, $writing) = $this->openFile($this->getHtConfigFullName($name)); |
||||
if (isset($reading) && isset($writing)) { |
||||
$this->saveToLegacyConfig($reading, $writing); |
||||
// Close the current file handler and rename them |
||||
if ($this->closeFile($this->getHtConfigFullName($name), $reading, $writing)) { |
||||
// just return true, if everything went fine |
||||
$saved = true; |
||||
} |
||||
} |
||||
|
||||
$this->reset(); |
||||
|
||||
return $saved; |
||||
} |
||||
|
||||
/** |
||||
* Opens a config file and returns two handler for reading and writing |
||||
* |
||||
* @param string $fullName The full name of the current config |
||||
* |
||||
* @return array An array containing the two reading and writing handler |
||||
*/ |
||||
private function openFile($fullName) |
||||
{ |
||||
if (empty($fullName)) { |
||||
return [null, null]; |
||||
} |
||||
|
||||
try { |
||||
$reading = fopen($fullName, 'r'); |
||||
} catch (\Exception $exception) { |
||||
return [null, null]; |
||||
} |
||||
|
||||
if (!$reading) { |
||||
return [null, null]; |
||||
} |
||||
|
||||
try { |
||||
$writing = fopen($fullName . '.tmp', 'w'); |
||||
} catch (\Exception $exception) { |
||||
fclose($reading); |
||||
return [null, null]; |
||||
} |
||||
|
||||
if (!$writing) { |
||||
fclose($reading); |
||||
return [null, null]; |
||||
} |
||||
|
||||
return [$reading, $writing]; |
||||
} |
||||
|
||||
/** |
||||
* Close and rename the config file |
||||
* |
||||
* @param string $fullName The full name of the current config |
||||
* @param resource $reading The reading resource handler |
||||
* @param resource $writing The writing resource handler |
||||
* |
||||
* @return bool True, if the close was successful |
||||
*/ |
||||
private function closeFile($fullName, $reading, $writing) |
||||
{ |
||||
fclose($reading); |
||||
fclose($writing); |
||||
|
||||
try { |
||||
$renamed = rename($fullName, $fullName . '.old'); |
||||
} catch (\Exception $exception) { |
||||
return false; |
||||
} |
||||
|
||||
if (!$renamed) { |
||||
return false; |
||||
} |
||||
|
||||
try { |
||||
$renamed = rename($fullName . '.tmp', $fullName); |
||||
} catch (\Exception $exception) { |
||||
// revert the move of the current config file to have at least the old config |
||||
rename($fullName . '.old', $fullName); |
||||
return false; |
||||
} |
||||
|
||||
if (!$renamed) { |
||||
// revert the move of the current config file to have at least the old config |
||||
rename($fullName . '.old', $fullName); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Saves all configuration values to a config file |
||||
* |
||||
* @param resource $reading The reading handler |
||||
* @param resource $writing The writing handler |
||||
*/ |
||||
private function saveConfigFile($reading, $writing) |
||||
{ |
||||
$settingsCount = count(array_keys($this->settings)); |
||||
$categoryFound = array_fill(0, $settingsCount, false); |
||||
$categoryBracketFound = array_fill(0, $settingsCount, false);; |
||||
$lineFound = array_fill(0, $settingsCount, false);; |
||||
$lineArrowFound = array_fill(0, $settingsCount, false);; |
||||
|
||||
while (!feof($reading)) { |
||||
|
||||
$line = fgets($reading); |
||||
|
||||
// check for each added setting if we have to replace a config line |
||||
for ($i = 0; $i < $settingsCount; $i++) { |
||||
|
||||
// find the first line like "'system' =>" |
||||
if (!$categoryFound[$i] && stristr($line, sprintf('\'%s\'', $this->settings[$i]['cat']))) { |
||||
$categoryFound[$i] = true; |
||||
} |
||||
|
||||
// find the first line with a starting bracket ( "[" ) |
||||
if ($categoryFound[$i] && !$categoryBracketFound[$i] && stristr($line, '[')) { |
||||
$categoryBracketFound[$i] = true; |
||||
} |
||||
|
||||
// find the first line with the key like "'value'" |
||||
if ($categoryBracketFound[$i] && !$lineFound[$i] && stristr($line, sprintf('\'%s\'', $this->settings[$i]['key']))) { |
||||
$lineFound[$i] = true; |
||||
} |
||||
|
||||
// find the first line with an arrow ("=>") after finding the key |
||||
if ($lineFound[$i] && !$lineArrowFound[$i] && stristr($line, '=>')) { |
||||
$lineArrowFound[$i] = true; |
||||
} |
||||
|
||||
// find the current value and replace it |
||||
if ($lineArrowFound[$i] && preg_match_all('/\'(.*?)\'/', $line, $matches, PREG_SET_ORDER)) { |
||||
$lineVal = end($matches)[0]; |
||||
$line = str_replace($lineVal, '\'' . $this->settings[$i]['value'] . '\'', $line); |
||||
$categoryFound[$i] = false; |
||||
$categoryBracketFound[$i] = false; |
||||
$lineFound[$i] = false; |
||||
$lineArrowFound[$i] = false; |
||||
// if a line contains a closing bracket for the category ( "]" ) and we didn't find the key/value pair, |
||||
// add it as a new line before the closing bracket |
||||
} elseif ($categoryBracketFound[$i] && !$lineArrowFound[$i] && stristr($line, ']')) { |
||||
$categoryFound[$i] = false; |
||||
$categoryBracketFound[$i] = false; |
||||
$lineFound[$i] = false; |
||||
$lineArrowFound[$i] = false; |
||||
$newLine = sprintf(self::INDENT . self::INDENT . '\'%s\' => \'%s\',' . PHP_EOL, $this->settings[$i]['key'], $this->settings[$i]['value']); |
||||
$line = $newLine . $line; |
||||
} |
||||
} |
||||
|
||||
fputs($writing, $line); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Saves a value to a ini file |
||||
* |
||||
* @param resource $reading The reading handler |
||||
* @param resource $writing The writing handler |
||||
*/ |
||||
private function saveINIConfigFile($reading, $writing) |
||||
{ |
||||
$settingsCount = count(array_keys($this->settings)); |
||||
$categoryFound = array_fill(0, $settingsCount, false); |
||||
|
||||
while (!feof($reading)) { |
||||
|
||||
$line = fgets($reading); |
||||
|
||||
// check for each added setting if we have to replace a config line |
||||
for ($i = 0; $i < $settingsCount; $i++) { |
||||
|
||||
// find the category of the current setting |
||||
if (!$categoryFound[$i] && stristr($line, sprintf('[%s]', $this->settings[$i]['cat']))) { |
||||
$categoryFound[$i] = true; |
||||
|
||||
// check the current value |
||||
} elseif ($categoryFound[$i] && preg_match_all('/^' . $this->settings[$i]['key'] . '\s*=\s*(.*?)$/', $line, $matches, PREG_SET_ORDER)) { |
||||
$line = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; |
||||
$categoryFound[$i] = false; |
||||
|
||||
// If end of INI file, add the line before the INI end |
||||
} elseif ($categoryFound[$i] && (preg_match_all('/^\[.*?\]$/', $line) || preg_match_all('/^INI;.*$/', $line))) { |
||||
$categoryFound[$i] = false; |
||||
$newLine = $this->settings[$i]['key'] . ' = ' . $this->settings[$i]['value'] . PHP_EOL; |
||||
$line = $newLine . $line; |
||||
} |
||||
} |
||||
|
||||
fputs($writing, $line); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Saves a value to a .php file (normally .htconfig.php) |
||||
* |
||||
* @param resource $reading The reading handler |
||||
* @param resource $writing The writing handler |
||||
*/ |
||||
private function saveToLegacyConfig($reading, $writing) |
||||
{ |
||||
|