Skip to content

Commit 3d22499

Browse files
committed
Fix logic for parsing egg data to not explode with nested objects
1 parent 1327bbb commit 3d22499

File tree

1 file changed

+99
-65
lines changed

1 file changed

+99
-65
lines changed

app/Services/Eggs/EggConfigurationService.php

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Services\Eggs;
114

125
use Illuminate\Support\Arr;
136
use Illuminate\Support\Str;
14-
use Pterodactyl\Models\Egg;
157
use Pterodactyl\Models\Server;
168
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
179
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
1810

1911
class EggConfigurationService
2012
{
13+
private const NOT_MATCHED = '__no_match';
14+
2115
/**
2216
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
2317
*/
@@ -109,63 +103,8 @@ protected function replacePlaceholders(Server $server, object $configs)
109103
// can property map the egg placeholders to values.
110104
$structure = $this->configurationStructureService->handle($server, true);
111105

112-
foreach ($configs as $file => $data) {
113-
foreach ($data->find ?? [] as &$value) {
114-
preg_match('/^{{(?<key>.*)}}$/', $value, $matches);
115-
116-
if (! $key = $matches['key'] ?? null) {
117-
continue;
118-
}
119-
120-
// Matched something in {{server.X}} format, now replace that with the actual
121-
// value from the server properties.
122-
//
123-
// The Daemon supports server.X, env.X, and config.X placeholders.
124-
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
125-
continue;
126-
}
127-
128-
// We don't want to do anything with config keys since the Daemon will need to handle
129-
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
130-
// interface to proxy through, but the Panel would be unaware of that.
131-
if (Str::startsWith($key, 'config.')) {
132-
$value = "{{{$key}}}";
133-
continue;
134-
}
135-
136-
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their
137-
// respective values on the Daemon side. Ensure that anything referencing those properly
138-
// replaces them with the matching config value.
139-
switch ($key) {
140-
case 'server.build.env.SERVER_MEMORY':
141-
case 'env.SERVER_MEMORY':
142-
$key = 'server.build.memory';
143-
break;
144-
case 'server.build.env.SERVER_IP':
145-
case 'env.SERVER_IP':
146-
$key = 'server.build.default.ip';
147-
break;
148-
case 'server.build.env.SERVER_PORT':
149-
case 'env.SERVER_PORT':
150-
$key = 'server.build.default.port';
151-
break;
152-
}
153-
154-
// Replace anything starting with "server." with the value out of the server configuration
155-
// array that used to be created for the old daemon.
156-
if (Str::startsWith($key, 'server.')) {
157-
$value = Arr::get(
158-
$structure, preg_replace('/^server\./', '', $key), ''
159-
);
160-
continue;
161-
}
162-
163-
// Finally, replace anything starting with env. with the expected environment
164-
// variable from the server configuration.
165-
$value = Arr::get(
166-
$structure, preg_replace('/^env\./', 'build.env.', $key), ''
167-
);
168-
}
106+
foreach ($configs as $file => &$data) {
107+
$this->iterate($data->find, $structure);
169108
}
170109

171110
$response = [];
@@ -194,4 +133,99 @@ protected function replacePlaceholders(Server $server, object $configs)
194133

195134
return $response;
196135
}
136+
137+
/**
138+
* @param string $value
139+
* @param array $structure
140+
* @return string|null
141+
*/
142+
protected function matchAndReplaceKeys(string $value, array $structure): ?string
143+
{
144+
preg_match('/{{(?<key>.*)}}/', $value, $matches);
145+
146+
if (! $key = $matches['key'] ?? null) {
147+
return self::NOT_MATCHED;
148+
}
149+
150+
// Matched something in {{server.X}} format, now replace that with the actual
151+
// value from the server properties.
152+
//
153+
// The Daemon supports server.X, env.X, and config.X placeholders.
154+
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
155+
return self::NOT_MATCHED;
156+
}
157+
158+
// We don't want to do anything with config keys since the Daemon will need to handle
159+
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
160+
// interface to proxy through, but the Panel would be unaware of that.
161+
if (Str::startsWith($key, 'config.')) {
162+
return "{{{$key}}}";
163+
}
164+
165+
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their
166+
// respective values on the Daemon side. Ensure that anything referencing those properly
167+
// replaces them with the matching config value.
168+
switch ($key) {
169+
case 'server.build.env.SERVER_MEMORY':
170+
case 'env.SERVER_MEMORY':
171+
$key = 'server.build.memory';
172+
break;
173+
case 'server.build.env.SERVER_IP':
174+
case 'env.SERVER_IP':
175+
$key = 'server.build.default.ip';
176+
break;
177+
case 'server.build.env.SERVER_PORT':
178+
case 'env.SERVER_PORT':
179+
$key = 'server.build.default.port';
180+
break;
181+
}
182+
183+
// Replace anything starting with "server." with the value out of the server configuration
184+
// array that used to be created for the old daemon.
185+
if (Str::startsWith($key, 'server.')) {
186+
$plucked = Arr::get(
187+
$structure, preg_replace('/^server\./', '', $key), ''
188+
);
189+
190+
return preg_replace('/{{(.*)}}/', $plucked, $value);
191+
}
192+
193+
// Finally, replace anything starting with env. with the expected environment
194+
// variable from the server configuration.
195+
$plucked = Arr::get(
196+
$structure, preg_replace('/^env\./', 'build.env.', $key), ''
197+
);
198+
199+
return preg_replace('/{{(.*)}}/', $plucked, $value);
200+
}
201+
202+
/**
203+
* Iterates over a set of "find" values for a given file in the parser configuration. If
204+
* the value of the line match is something iterable, continue iterating, otherwise perform
205+
* a match & replace.
206+
*
207+
* @param mixed $data
208+
* @param array $structure
209+
*/
210+
private function iterate(&$data, array $structure)
211+
{
212+
if (! is_iterable($data) && ! is_object($data)) {
213+
return;
214+
}
215+
216+
foreach ($data as &$value) {
217+
if (is_iterable($value) || is_object($value)) {
218+
$this->iterate($value, $structure);
219+
220+
continue;
221+
}
222+
223+
$response = $this->matchAndReplaceKeys($value, $structure);
224+
if ($response === self::NOT_MATCHED) {
225+
continue;
226+
}
227+
228+
$value = $response;
229+
}
230+
}
197231
}

0 commit comments

Comments
 (0)