Fix file parser failing if multiple configuration values are present on same line; closes #2604

This commit is contained in:
Dane Everitt 2020-11-01 12:25:02 -08:00
parent 8c8feffcb7
commit 61f501abc9
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53

View file

@ -5,18 +5,10 @@ namespace Pterodactyl\Services\Eggs;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Services\Servers\ServerConfigurationStructureService; use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
class EggConfigurationService class EggConfigurationService
{ {
private const NOT_MATCHED = '__no_match';
/**
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
*/
private $repository;
/** /**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/ */
@ -25,14 +17,10 @@ class EggConfigurationService
/** /**
* EggConfigurationService constructor. * EggConfigurationService constructor.
* *
* @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
*/ */
public function __construct( public function __construct(ServerConfigurationStructureService $configurationStructureService)
EggRepositoryInterface $repository, {
ServerConfigurationStructureService $configurationStructureService
) {
$this->repository = $repository;
$this->configurationStructureService = $configurationStructureService; $this->configurationStructureService = $configurationStructureService;
} }
@ -41,8 +29,6 @@ class EggConfigurationService
* *
* @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Server $server
* @return array * @return array
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function handle(Server $server): array public function handle(Server $server): array
{ {
@ -111,8 +97,6 @@ class EggConfigurationService
* @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Server $server
* @param object $configs * @param object $configs
* @return array * @return array
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
protected function replacePlaceholders(Server $server, object $configs) protected function replacePlaceholders(Server $server, object $configs)
{ {
@ -130,6 +114,7 @@ class EggConfigurationService
foreach ($configs as $file => $data) { foreach ($configs as $file => $data) {
$append = ['file' => $file, 'replace' => []]; $append = ['file' => $file, 'replace' => []];
/** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
// I like to think I understand PHP pretty well, but if you don't pass $value // I like to think I understand PHP pretty well, but if you don't pass $value
// by reference here, you'll end up with a resursive array loop if the config // by reference here, you'll end up with a resursive array loop if the config
// file has two replacements that reference the same item in the configuration // file has two replacements that reference the same item in the configuration
@ -167,62 +152,81 @@ class EggConfigurationService
} }
/** /**
* Replaces the legacy modifies from eggs with their new counterpart. The legacy Daemon would
* set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their respective values on the Daemon
* side. Ensure that anything referencing those properly replaces them with the matching config
* value.
*
* @param string $key
* @param string $value * @param string $value
* @param array $structure * @return string
* @return string|null
*/ */
protected function matchAndReplaceKeys(string $value, array $structure): ?string protected function replaceLegacyModifiers(string $key, string $value): string
{ {
preg_match('/{{(?<key>.*)}}/', $value, $matches); switch ($key) {
case 'config.docker.interface':
if (! $key = $matches['key'] ?? null) { $replace = 'config.docker.network.interface';
return self::NOT_MATCHED; break;
case 'server.build.env.SERVER_MEMORY':
case 'env.SERVER_MEMORY':
$replace = 'server.build.memory';
break;
case 'server.build.env.SERVER_IP':
case 'env.SERVER_IP':
$replace = 'server.build.default.ip';
break;
case 'server.build.env.SERVER_PORT':
case 'env.SERVER_PORT':
$replace = 'server.build.default.port';
break;
// By default we don't need to change anything, only if we ended up matching a specific legacy item.
default:
$replace = $key;
} }
return str_replace("{{{$key}}}", "{{{$replace}}}", $value);
}
/**
* @param mixed $value
* @param array $structure
* @return mixed|null
*/
protected function matchAndReplaceKeys($value, array $structure)
{
preg_match_all('/{{(?<key>[\w.-]*)}}/', $value, $matches);
foreach ($matches['key'] as $key) {
// Matched something in {{server.X}} format, now replace that with the actual // Matched something in {{server.X}} format, now replace that with the actual
// value from the server properties. // value from the server properties.
// //
// The Daemon supports server.X, env.X, and config.X placeholders. // The Daemon supports server.X, env.X, and config.X placeholders.
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) { if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
return self::NOT_MATCHED; continue;
} }
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their // Don't do a replacement on anything that is not a string, we don't want to unintentionally
// respective values on the Daemon side. Ensure that anything referencing those properly // modify the resulting output.
// replaces them with the matching config value. if (! is_string($value)) {
switch ($key) { continue;
case 'config.docker.interface':
$key = 'config.docker.network.interface';
break;
case 'server.build.env.SERVER_MEMORY':
case 'env.SERVER_MEMORY':
$key = 'server.build.memory';
break;
case 'server.build.env.SERVER_IP':
case 'env.SERVER_IP':
$key = 'server.build.default.ip';
break;
case 'server.build.env.SERVER_PORT':
case 'env.SERVER_PORT':
$key = 'server.build.default.port';
break;
} }
$value = $this->replaceLegacyModifiers($key, $value);
// We don't want to do anything with config keys since the Daemon will need to handle // We don't want to do anything with config keys since the Daemon will need to handle
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
// interface to proxy through, but the Panel would be unaware of that. // interface to proxy through, but the Panel would be unaware of that.
if (Str::startsWith($key, 'config.')) { if (Str::startsWith($key, 'config.')) {
return preg_replace('/{{(.*)}}/', "{{{$key}}}", $value); continue;
} }
// Replace anything starting with "server." with the value out of the server configuration // Replace anything starting with "server." with the value out of the server configuration
// array that used to be created for the old daemon. // array that used to be created for the old daemon.
if (Str::startsWith($key, 'server.')) { if (Str::startsWith($key, 'server.')) {
$plucked = Arr::get( $plucked = Arr::get($structure, preg_replace('/^server\./', '', $key), '');
$structure, preg_replace('/^server\./', '', $key), ''
);
return preg_replace('/{{(.*)}}/', $plucked, $value); $value = str_replace("{{{$key}}}", $plucked, $value);
continue;
} }
// Finally, replace anything starting with env. with the expected environment // Finally, replace anything starting with env. with the expected environment
@ -231,7 +235,10 @@ class EggConfigurationService
$structure, preg_replace('/^env\./', 'build.env.', $key), '' $structure, preg_replace('/^env\./', 'build.env.', $key), ''
); );
return preg_replace('/{{(.*)}}/', $plucked, $value); $value = str_replace("{{{$key}}}", $plucked, $value);
}
return $value;
} }
/** /**
@ -255,12 +262,7 @@ class EggConfigurationService
continue; continue;
} }
$response = $this->matchAndReplaceKeys($value, $structure); $value = $this->matchAndReplaceKeys($value, $structure);
if ($response === self::NOT_MATCHED) {
continue;
}
$value = $response;
} }
} }
} }