GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( 516e2d...336234 )
by Dane
03:06
created

ServerRepository::deleteNow()   C

Complexity

Conditions 9
Paths 152

Size

Total Lines 63
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 63
rs 6.1214
cc 9
eloc 34
nc 152
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Pterodactyl - Panel
4
 * Copyright (c) 2015 - 2017 Dane Everitt <[email protected]>.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
namespace Pterodactyl\Repositories;
26
27
use DB;
28
use Log;
29
use Crypt;
30
use Validator;
31
use Pterodactyl\Models;
32
use Pterodactyl\Services\UuidService;
33
use GuzzleHttp\Exception\TransferException;
34
use Pterodactyl\Services\DeploymentService;
35
use Pterodactyl\Exceptions\DisplayException;
36
use Pterodactyl\Exceptions\DisplayValidationException;
37
38
class ServerRepository
39
{
40
    protected $daemonPermissions = [
41
        's:*',
42
    ];
43
44
    public function __construct()
45
    {
46
        //
47
    }
48
49
    /**
50
     * Generates a SFTP username for a server given a server name.
51
     * format: mumble_67c7a4b0.
52
     *
53
     * @param  string $name
54
     * @param  string $identifier
55
     * @return string
56
     */
57
    protected function generateSFTPUsername($name, $identifier = null)
58
    {
59
        if (is_null($identifier) || ! ctype_alnum($identifier)) {
60
            $unique = str_random(8);
61
        } else {
62
            if (strlen($identifier) < 8) {
63
                $unique = $identifier . str_random((8 - strlen($identifier)));
64
            } else {
65
                $unique = substr($identifier, 0, 8);
66
            }
67
        }
68
69
        // Filter the Server Name
70
        $name = trim(preg_replace('/[^\w]+/', '', $name), '_');
71
        $name = (strlen($name) < 1) ? str_random(6) : $name;
72
73
        return strtolower(substr($name, 0, 6) . '_' . $unique);
74
    }
75
76
    /**
77
     * Adds a new server to the system.
78
     * @param   array  $data  An array of data descriptors for creating the server. These should align to the columns in the database.
79
     * @return  int
80
     */
81
    public function create(array $data)
82
    {
83
84
        // Validate Fields
85
        $validator = Validator::make($data, [
86
            'owner' => 'bail|required',
87
            'name' => 'required|regex:/^([\w .-]{1,200})$/',
88
            'memory' => 'required|numeric|min:0',
89
            'swap' => 'required|numeric|min:-1',
90
            'io' => 'required|numeric|min:10|max:1000',
91
            'cpu' => 'required|numeric|min:0',
92
            'disk' => 'required|numeric|min:0',
93
            'service_id' => 'required|numeric|min:1|exists:services,id',
94
            'option_id' => 'required|numeric|min:1|exists:service_options,id',
95
            'location_id' => 'required|numeric|min:1|exists:locations,id',
96
            'pack_id' => 'sometimes|nullable|numeric|min:0',
97
            'startup' => 'string',
98
            'custom_image_name' => 'required_if:use_custom_image,on',
99
            'auto_deploy' => 'sometimes|boolean',
100
            'custom_id' => 'sometimes|required|numeric|unique:servers,id',
101
        ]);
102
103
        $validator->sometimes('node_id', 'bail|required|numeric|min:1|exists:nodes,id', function ($input) {
104
            return ! ($input->auto_deploy);
105
        });
106
107
        $validator->sometimes('ip', 'required|ip', function ($input) {
108
            return ! $input->auto_deploy && ! $input->allocation;
109
        });
110
111
        $validator->sometimes('port', 'required|numeric|min:1|max:65535', function ($input) {
112
            return ! $input->auto_deploy && ! $input->allocation;
113
        });
114
115
        $validator->sometimes('allocation_id', 'numeric|exists:allocations,id', function ($input) {
116
            return ! ($input->auto_deploy || ($input->port && $input->ip));
117
        });
118
119
        // Run validator, throw catchable and displayable exception if it fails.
120
        // Exception includes a JSON result of failed validation rules.
121
        if ($validator->fails()) {
122
            throw new DisplayValidationException($validator->errors());
123
        }
124
125
        $user = Models\User::select('id', 'email')->where((is_int($data['owner'])) ? 'id' : 'email', $data['owner'])->first();
126
        if (! $user) {
127
            throw new DisplayException('The user id or email passed to the function was not found on the system.');
128
        }
129
130
        $autoDeployed = false;
131
        if (isset($data['auto_deploy']) && in_array($data['auto_deploy'], [true, 1, '1'])) {
132
            // This is an auto-deployment situation
133
            // Ignore any other passed node data
134
            unset($data['node_id'], $data['ip'], $data['port'], $data['allocation_id']);
135
136
            $autoDeployed = true;
137
            $node = DeploymentService::smartRandomNode($data['memory'], $data['disk'], $data['location_id']);
138
            $allocation = DeploymentService::randomAllocation($node->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Node>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
139
        } else {
140
            $node = Models\Node::findOrFail($data['node_id']);
141
        }
142
143
        // Verify IP & Port are a.) free and b.) assigned to the node.
144
        // We know the node exists because of 'exists:nodes,id' in the validation
145
        if (! $autoDeployed) {
146
            if (! isset($data['allocation_id'])) {
147
                $allocation = Models\Allocation::where('ip', $data['ip'])->where('port', $data['port'])->where('node', $data['node'])->whereNull('assigned_to')->first();
148
            } else {
149
                $allocation = Models\Allocation::where('id', $data['allocation'])->where('node', $data['node'])->whereNull('assigned_to')->first();
150
            }
151
        }
152
153
        // Something failed in the query, either that combo doesn't exist, or it is in use.
154
        if (! $allocation) {
0 ignored issues
show
Bug introduced by
The variable $allocation does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
155
            throw new DisplayException('The selected IP/Port combination or Allocation ID is either already in use, or unavaliable for this node.');
156
        }
157
158
        // Validate those Service Option Variables
159
        // We know the service and option exists because of the validation.
160
        // We need to verify that the option exists for the service, and then check for
161
        // any required variable fields. (fields are labeled env_<env_variable>)
162
        $option = Models\ServiceOption::where('id', $data['option'])->where('service_id', $data['service'])->first();
163
        if (! $option) {
164
            throw new DisplayException('The requested service option does not exist for the specified service.');
165
        }
166
167
        // Validate the Pack
168
        if ($data['pack_id'] == 0) {
169
            $data['pack_id'] = null;
170
        }
171
172
        if (! is_null($data['pack_id'])) {
173
            $pack = Models\ServicePack::where('id', $data['pack_id'])->where('option', $data['option_id'])->first();
174
            if (! $pack) {
175
                throw new DisplayException('The requested service pack does not seem to exist for this combination.');
176
            }
177
        }
178
179
        // Load up the Service Information
180
        $service = Models\Service::find($option->service_id);
181
182
        // Check those Variables
183
        $variables = Models\ServiceVariable::where('option_id', $data['option_id'])->get();
184
        $variableList = [];
185
        if ($variables) {
186
            foreach ($variables as $variable) {
187
188
                // Is the variable required?
189
                if (! isset($data['env_' . $variable->env_variable])) {
190
                    if ($variable->required === 1) {
191
                        throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
192
                    }
193
                    $variableList[] = [
194
                        'id' => $variable->id,
195
                        'env' => $variable->env_variable,
196
                        'val' => $variable->default_value,
197
                    ];
198
                    continue;
199
                }
200
201
                // Check aganist Regex Pattern
202 View Code Duplication
                if (! is_null($variable->regex) && ! preg_match($variable->regex, $data['env_' . $variable->env_variable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
                    throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
204
                }
205
206
                $variableList[] = [
207
                    'id' => $variable->id,
208
                    'env' => $variable->env_variable,
209
                    'val' => $data['env_' . $variable->env_variable],
210
                ];
211
                continue;
212
            }
213
        }
214
215
        // Check Overallocation
216
        if (! $autoDeployed) {
217
            if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) {
218
                $totals = Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node', $node->id)->first();
219
220
                // Check memory limits
221 View Code Duplication
                if (is_numeric($node->memory_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
                    $newMemory = $totals->memory + $data['memory'];
223
                    $memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100)));
224
                    if ($newMemory > $memoryLimit) {
225
                        throw new DisplayException('The amount of memory allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->memory_overallocate + 100) . '% of its assigned ' . $node->memory . 'Mb of memory (' . $memoryLimit . 'Mb) of which ' . (($totals->memory / $node->memory) * 100) . '% (' . $totals->memory . 'Mb) is in use already. By allocating this server the node would be at ' . (($newMemory / $node->memory) * 100) . '% (' . $newMemory . 'Mb) usage.');
226
                    }
227
                }
228
229
                // Check Disk Limits
230 View Code Duplication
                if (is_numeric($node->disk_overallocate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
                    $newDisk = $totals->disk + $data['disk'];
232
                    $diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100)));
233
                    if ($newDisk > $diskLimit) {
234
                        throw new DisplayException('The amount of disk allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->disk_overallocate + 100) . '% of its assigned ' . $node->disk . 'Mb of disk (' . $diskLimit . 'Mb) of which ' . (($totals->disk / $node->disk) * 100) . '% (' . $totals->disk . 'Mb) is in use already. By allocating this server the node would be at ' . (($newDisk / $node->disk) * 100) . '% (' . $newDisk . 'Mb) usage.');
235
                    }
236
                }
237
            }
238
        }
239
240
        DB::beginTransaction();
241
242
        try {
243
            $uuid = new UuidService;
244
245
            // Add Server to the Database
246
            $server = new Models\Server;
247
            $genUuid = $uuid->generate('servers', 'uuid');
248
            $genShortUuid = $uuid->generateShort('servers', 'uuidShort', $genUuid);
249
250
            if (isset($data['custom_id'])) {
251
                $server->id = $data['custom_id'];
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
252
            }
253
254
            $server->fill([
255
                'uuid' => $genUuid,
256
                'uuidShort' => $genShortUuid,
257
                'node_id' => $node->id,
258
                'name' => $data['name'],
259
                'suspended' => 0,
260
                'owner_id' => $user->id,
261
                'memory' => $data['memory'],
262
                'swap' => $data['swap'],
263
                'disk' => $data['disk'],
264
                'io' => $data['io'],
265
                'cpu' => $data['cpu'],
266
                'oom_disabled' => (isset($data['oom_disabled'])) ? true : false,
267
                'allocation' => $allocation->id,
268
                'service_id' => $data['service_id'],
269
                'option_id' => $data['option_id'],
270
                'pack_id' => $data['pack_id'],
271
                'startup' => $data['startup'],
272
                'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
273
                'image' => (isset($data['custom_image_name'])) ? $data['custom_image_name'] : $option->docker_image,
274
                'username' => $this->generateSFTPUsername($data['name'], $genShortUuid),
275
                'sftp_password' => Crypt::encrypt('not set'),
276
            ]);
277
            $server->save();
278
279
            // Mark Allocation in Use
280
            $allocation->server_id = $server->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
281
            $allocation->save();
282
283
            // Add Variables
284
            $environmentVariables = [
285
                'STARTUP' => $data['startup'],
286
            ];
287
288
            foreach ($variableList as $item) {
289
                $environmentVariables[$item['env']] = $item['val'];
290
291
                Models\ServerVariable::create([
292
                    'server_id' => $server->id,
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
293
                    'variable_id' => $item['id'],
294
                    'variable_value' => $item['val'],
295
                ]);
296
            }
297
298
            $node->guzzleClient(['X-Access-Token' => $node->daemonSecret])->request('POST', '/servers', [
299
                'json' => [
300
                    'uuid' => (string) $server->uuid,
0 ignored issues
show
Documentation introduced by
The property uuid does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
301
                    'user' => $server->username,
0 ignored issues
show
Documentation introduced by
The property username does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
302
                    'build' => [
303
                        'default' => [
304
                            'ip' => $allocation->ip,
305
                            'port' => (int) $allocation->port,
306
                        ],
307
                        'ports' => [
308
                            (string) $allocation->ip => [(int) $allocation->port],
309
                        ],
310
                        'env' => $environmentVariables,
311
                        'memory' => (int) $server->memory,
0 ignored issues
show
Documentation introduced by
The property memory does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
312
                        'swap' => (int) $server->swap,
0 ignored issues
show
Documentation introduced by
The property swap does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
313
                        'io' => (int) $server->io,
0 ignored issues
show
Documentation introduced by
The property io does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
314
                        'cpu' => (int) $server->cpu,
0 ignored issues
show
Documentation introduced by
The property cpu does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
315
                        'disk' => (int) $server->disk,
0 ignored issues
show
Documentation introduced by
The property disk does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
316
                        'image' => (isset($data['custom_image_name'])) ? $data['custom_image_name'] : $option->docker_image,
317
                    ],
318
                    'service' => [
319
                        'type' => $service->file,
320
                        'option' => $option->tag,
321
                        'pack' => (isset($pack)) ? $pack->uuid : null,
322
                    ],
323
                    'keys' => [
324
                        (string) $server->daemonSecret => $this->daemonPermissions,
0 ignored issues
show
Documentation introduced by
The property daemonSecret does not exist on object<Pterodactyl\Models\Server>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
325
                    ],
326
                    'rebuild' => false,
327
                ],
328
            ]);
329
330
            DB::commit();
331
332
            return $server;
333
        } catch (TransferException $ex) {
334
            DB::rollBack();
335
            throw new DisplayException('There was an error while attempting to connect to the daemon to add this server.', $ex);
336
        } catch (\Exception $ex) {
337
            DB::rollBack();
338
            throw $ex;
339
        }
340
    }
341
342
    /**
343
     * [updateDetails description].
344
     * @param  int  $id
345
     * @param  array    $data
346
     * @return bool
347
     */
348
    public function updateDetails($id, array $data)
349
    {
350
        $uuid = new UuidService;
351
        $resetDaemonKey = false;
352
353
        // Validate Fields
354
        $validator = Validator::make($data, [
355
            'owner' => 'email|exists:users,email',
356
            'name' => 'regex:([\w .-]{1,200})',
357
        ]);
358
359
        // Run validator, throw catchable and displayable exception if it fails.
360
        // Exception includes a JSON result of failed validation rules.
361
        if ($validator->fails()) {
362
            throw new DisplayValidationException($validator->errors());
363
        }
364
365
        DB::beginTransaction();
366
367
        try {
368
            $server = Models\Server::with('user')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
369
370
            // Update daemon secret if it was passed.
371
            if ((isset($data['reset_token']) && $data['reset_token'] === true) || (isset($data['owner']) && $data['owner'] !== $server->user->email)) {
372
                $oldDaemonKey = $server->daemonSecret;
373
                $server->daemonSecret = $uuid->generate('servers', 'daemonSecret');
374
                $resetDaemonKey = true;
375
            }
376
377
            // Update Server Owner if it was passed.
378
            if (isset($data['owner']) && $data['owner'] !== $owner->email) {
0 ignored issues
show
Bug introduced by
The variable $owner does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
379
                $newOwner = Models\User::select('id')->where('email', $data['owner'])->first();
380
                $server->owner_id = $newOwner->id;
381
            }
382
383
            // Update Server Name if it was passed.
384
            if (isset($data['name'])) {
385
                $server->name = $data['name'];
386
            }
387
388
            // Save our changes
389
            $server->save();
390
391
            // Do we need to update? If not, return successful.
392
            if (! $resetDaemonKey) {
393
                DB::commit();
394
395
                return true;
396
            }
397
398
            $res = $server->node->guzzleClient([
399
                'X-Access-Server' => $server->uuid,
400
                'X-Access-Token' => $server->node->daemonSecret,
401
            ])->request('PATCH', '/server', [
402
                'exceptions' => false,
403
                'json' => [
404
                    'keys' => [
405
                        (string) $oldDaemonKey => [],
0 ignored issues
show
Bug introduced by
The variable $oldDaemonKey does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
406
                        (string) $server->daemonSecret => $this->daemonPermissions,
407
                    ],
408
                ],
409
            ]);
410
411
            if ($res->getStatusCode() === 204) {
412
                DB::commit();
413
414
                return true;
415
            } else {
416
                throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode());
417
            }
418
        } catch (\Exception $ex) {
419
            DB::rollBack();
420
            Log::error($ex);
421
            throw new DisplayException('An error occured while attempting to update this server\'s information.');
422
        }
423
    }
424
425
    /**
426
     * [updateContainer description].
427
     * @param  int      $id
428
     * @param  array    $data
429
     * @return bool
430
     */
431 View Code Duplication
    public function updateContainer($id, array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
432
    {
433
        $validator = Validator::make($data, [
434
            'image' => 'required|string',
435
        ]);
436
437
        // Run validator, throw catchable and displayable exception if it fails.
438
        // Exception includes a JSON result of failed validation rules.
439
        if ($validator->fails()) {
440
            throw new DisplayValidationException($validator->errors());
441
        }
442
443
        DB::beginTransaction();
444
        try {
445
            $server = Models\Server::findOrFail($id);
446
447
            $server->image = $data['image'];
448
            $server->save();
449
450
            $server->node->guzzleClient([
451
                'X-Access-Server' => $server->uuid,
452
                'X-Access-Token' => $server->node->daemonSecret,
453
            ])->request('PATCH', '/server', [
454
                'json' => [
455
                    'build' => [
456
                        'image' => $server->image,
457
                    ],
458
                ],
459
            ]);
460
461
            DB::commit();
462
463
            return true;
464
        } catch (TransferException $ex) {
465
            DB::rollBack();
466
            throw new DisplayException('An error occured while attempting to update the container image.', $ex);
467
        } catch (\Exception $ex) {
468
            DB::rollBack();
469
            throw $ex;
470
        }
471
    }
472
473
    /**
474
     * [changeBuild description].
475
     * @param  int  $id
476
     * @param  array    $data
477
     * @return bool
478
     */
479
    public function changeBuild($id, array $data)
480
    {
481
        $validator = Validator::make($data, [
482
            'default' => [
483
                'string',
484
                'regex:/^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])):(\d{1,5})$/',
485
            ],
486
            'add_additional' => 'nullable|array',
487
            'remove_additional' => 'nullable|array',
488
            'memory' => 'integer|min:0',
489
            'swap' => 'integer|min:-1',
490
            'io' => 'integer|min:10|max:1000',
491
            'cpu' => 'integer|min:0',
492
            'disk' => 'integer|min:0',
493
        ]);
494
495
        // Run validator, throw catchable and displayable exception if it fails.
496
        // Exception includes a JSON result of failed validation rules.
497
        if ($validator->fails()) {
498
            throw new DisplayValidationException($validator->errors());
499
        }
500
501
        DB::beginTransaction();
502
503
        try {
504
            $server = Models\Server::with('allocation', 'allocations')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
505
            $newBuild = [];
506
507
            if (isset($data['default'])) {
508
                list($ip, $port) = explode(':', $data['default']);
509
                if ($ip !== $server->allocation->ip || (int) $port !== $server->allocation->port) {
510
                    $selection = $server->allocations->where('ip', $ip)->where('port', $port)->first();
511
                    if (! $selection) {
512
                        throw new DisplayException('The requested default connection (' . $ip . ':' . $port . ') is not allocated to this server.');
513
                    }
514
515
                    $server->allocation_id = $selection->id;
516
                    $newBuild['default'] = [
517
                        'ip' => $ip,
518
                        'port' => (int) $port,
519
                    ];
520
521
                    // Re-Run to keep updated for rest of function
522
                    $server->load('allocation');
523
                }
524
            }
525
526
            $newPorts = false;
527
            // Remove Assignments
528
            if (isset($data['remove_additional'])) {
529
                foreach ($data['remove_additional'] as $id => $combo) {
530
                    list($ip, $port) = explode(':', $combo);
531
                    // Invalid, not worth killing the whole thing, we'll just skip over it.
532
                    if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) {
533
                        break;
534
                    }
535
536
                    // Can't remove the assigned IP/Port combo
537
                    if ($ip === $server->allocation->ip && (int) $port === (int) $server->allocation->port) {
538
                        break;
539
                    }
540
541
                    $newPorts = true;
542
                    $server->allocations->where('ip', $ip)->where('port', $port)->update([
543
                        'assigned_to' => null,
544
                    ]);
545
                }
546
547
                $server->load('allocations');
548
            }
549
550
            // Add Assignments
551
            if (isset($data['add_additional'])) {
552
                foreach ($data['add_additional'] as $id => $combo) {
553
                    list($ip, $port) = explode(':', $combo);
554
                    // Invalid, not worth killing the whole thing, we'll just skip over it.
555
                    if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) {
556
                        break;
557
                    }
558
559
                    // Don't allow double port assignments
560
                    if ($server->allocations->where('port', $port)->count() !== 0) {
561
                        break;
562
                    }
563
564
                    $newPorts = true;
565
                    Models\Allocation::where('ip', $ip)->where('port', $port)->whereNull('assigned_to')->update([
566
                        'assigned_to' => $server->id,
567
                    ]);
568
                }
569
570
                $server->load('allocations');
571
            }
572
573
            // Loop All Assignments
574
            $additionalAssignments = [];
575
            foreach ($server->allocations as &$assignment) {
576
                if (array_key_exists((string) $assignment->ip, $additionalAssignments)) {
577
                    array_push($additionalAssignments[(string) $assignment->ip], (int) $assignment->port);
578
                } else {
579
                    $additionalAssignments[(string) $assignment->ip] = [(int) $assignment->port];
580
                }
581
            }
582
583
            if ($newPorts === true) {
584
                $newBuild['ports|overwrite'] = $additionalAssignments;
585
            }
586
587
            // @TODO: verify that server can be set to this much memory without
588
            // going over node limits.
589 View Code Duplication
            if (isset($data['memory']) && $server->memory !== (int) $data['memory']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
590
                $server->memory = $data['memory'];
591
                $newBuild['memory'] = (int) $server->memory;
592
            }
593
594 View Code Duplication
            if (isset($data['swap']) && $server->swap !== (int) $data['swap']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
595
                $server->swap = $data['swap'];
596
                $newBuild['swap'] = (int) $server->swap;
597
            }
598
599
            // @TODO: verify that server can be set to this much disk without
600
            // going over node limits.
601 View Code Duplication
            if (isset($data['disk']) && $server->disk !== (int) $data['disk']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
602
                $server->disk = $data['disk'];
603
                $newBuild['disk'] = (int) $server->disk;
604
            }
605
606 View Code Duplication
            if (isset($data['cpu']) && $server->cpu !== (int) $data['cpu']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
607
                $server->cpu = $data['cpu'];
608
                $newBuild['cpu'] = (int) $server->cpu;
609
            }
610
611 View Code Duplication
            if (isset($data['io']) && $server->io !== (int) $data['io']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
612
                $server->io = $data['io'];
613
                $newBuild['io'] = (int) $server->io;
614
            }
615
616
            // Try save() here so if it fails we haven't contacted the daemon
617
            // This won't be committed unless the HTTP request succeedes anyways
618
            $server->save();
619
620
            if (! empty($newBuild)) {
621
                $server->node->guzzleClient([
622
                    'X-Access-Server' => $server->uuid,
623
                    'X-Access-Token' => $server->node->daemonSecret,
624
                ])->request('PATCH', '/server', [
625
                    'json' => [
626
                        'build' => $newBuild,
627
                    ],
628
                ]);
629
            }
630
631
            DB::commit();
632
633
            return true;
634
        } catch (TransferException $ex) {
635
            DB::rollBack();
636
            throw new DisplayException('An error occured while attempting to update the configuration.', $ex);
637
        } catch (\Exception $ex) {
638
            DB::rollBack();
639
            throw $ex;
640
        }
641
    }
642
643
    public function updateStartup($id, array $data, $admin = false)
644
    {
645
        $server = Models\Server::with('variables', 'option.variables')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
646
647
        DB::beginTransaction();
648
649
        try {
650
            // Check the startup
651
            if (isset($data['startup'])) {
652
                $server->startup = $data['startup'];
653
                $server->save();
654
            }
655
656
            // Check those Variables
657
            $server->option->variables->transform(function ($item, $key) use ($server) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
658
                $displayValue = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
659
                $item->server_value = (! is_null($displayValue)) ? $displayValue : $item->default_value;
660
661
                return $item;
662
            });
663
664
            $variableList = [];
665
            if ($server->option->variables) {
666
                foreach ($server->option->variables as &$variable) {
667
                    // Move on if the new data wasn't even sent
668 View Code Duplication
                    if (! isset($data[$variable->env_variable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
669
                        $variableList[] = [
670
                            'id' => $variable->id,
671
                            'env' => $variable->env_variable,
672
                            'val' => $variable->server_value,
673
                        ];
674
                        continue;
675
                    }
676
677
                    // Update Empty but skip validation
678 View Code Duplication
                    if (empty($data[$variable->env_variable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
679
                        $variableList[] = [
680
                            'id' => $variable->id,
681
                            'env' => $variable->env_variable,
682
                            'val' => null,
683
                        ];
684
                        continue;
685
                    }
686
687
                    // Is the variable required?
688
                    // @TODO: is this even logical to perform this check?
689
                    if (isset($data[$variable->env_variable]) && empty($data[$variable->env_variable])) {
690
                        if ($variable->required) {
691
                            throw new DisplayException('A required service option variable field (' . $variable->env_variable . ') was included in this request but was left blank.');
692
                        }
693
                    }
694
695
                    // Variable hidden and/or not user editable
696
                    if ((! $variable->user_viewable || ! $variable->user_editable) && ! $admin) {
697
                        throw new DisplayException('A service option variable field (' . $variable->env_variable . ') does not exist or you do not have permission to edit it.');
698
                    }
699
700
                    // Check aganist Regex Pattern
701 View Code Duplication
                    if (! is_null($variable->regex) && ! preg_match($variable->regex, $data[$variable->env_variable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
702
                        throw new DisplayException('Failed to validate service option variable field (' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
703
                    }
704
705
                    $variableList[] = [
706
                        'id' => $variable->id,
707
                        'env' => $variable->env_variable,
708
                        'val' => $data[$variable->env_variable],
709
                    ];
710
                }
711
            }
712
713
            // Add Variables
714
            $environmentVariables = [
715
                'STARTUP' => $server->startup,
716
            ];
717
            foreach ($variableList as $item) {
718
                $environmentVariables[$item['env']] = $item['val'];
719
720
                // Update model or make a new record if it doesn't exist.
721
                $model = Models\ServerVariable::firstOrNew([
722
                    'variable_id' => $item['id'],
723
                    'server_id' => $server->id,
724
                ]);
725
                $model->variable_value = $item['val'];
726
                $model->save();
727
            }
728
729
            $server->node->guzzleClient([
730
                'X-Access-Server' => $server->uuid,
731
                'X-Access-Token' => $server->node->daemonSecret,
732
            ])->request('PATCH', '/server', [
733
                'json' => [
734
                    'build' => [
735
                        'env|overwrite' => $environmentVariables,
736
                    ],
737
                ],
738
            ]);
739
740
            DB::commit();
741
742
            return true;
743
        } catch (TransferException $ex) {
744
            DB::rollBack();
745
            throw new DisplayException('An error occured while attempting to update the server configuration.', $ex);
746
        } catch (\Exception $ex) {
747
            DB::rollBack();
748
            throw $ex;
749
        }
750
    }
751
752 View Code Duplication
    public function deleteServer($id, $force)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
753
    {
754
        $server = Models\Server::findOrFail($id);
755
        DB::beginTransaction();
756
757
        try {
758
            if ($force === 'force' || $force) {
759
                $server->installed = 3;
760
                $server->save();
761
            }
762
763
            $server->delete();
764
765
            return DB::commit();
766
        } catch (\Exception $ex) {
767
            DB::rollBack();
768
            throw $ex;
769
        }
770
    }
771
772
    public function deleteNow($id, $force = false)
773
    {
774
        $server = Models\Server::withTrashed()->with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
775
776
        // Handle server being restored previously or
777
        // an accidental queue.
778
        if (! $server->trashed()) {
779
            return;
780
        }
781
782
        DB::beginTransaction();
783
        try {
784
            // Unassign Allocations
785
            Models\Allocation::where('server_id', $server->id)->update([
786
                'server_id' => null,
787
            ]);
788
789
            // Remove Variables
790
            Models\ServerVariable::where('server_id', $server->id)->delete();
791
792
            // Remove SubUsers
793
            foreach(Models\Subuser::with('permissions')->where('server_id', $server->id)->get() as &$subuser) {
0 ignored issues
show
Bug introduced by
The method where does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The expression \Pterodactyl\Models\Subu...d', $server->id)->get() cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
794
                foreach($subuser->permissions as &$permission) {
795
                    $permission->delete();
796
                }
797
                $subuser->delete();
798
            }
799
800
            // Remove Downloads
801
            Models\Download::where('server', $server->uuid)->delete();
802
803
            // Clear Tasks
804
            Models\Task::where('server', $server->id)->delete();
805
806
            // Delete Databases
807
            // This is the one un-recoverable point where
808
            // transactions will not save us.
809
            $repository = new DatabaseRepository;
810
            foreach (Models\Database::select('id')->where('server_id', $server->id)->get() as &$database) {
0 ignored issues
show
Bug introduced by
The expression \Pterodactyl\Models\Data...d', $server->id)->get() cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
811
                $repository->drop($database->id);
812
            }
813
814
            $server->node->guzzleClient([
815
                'X-Access-Token' => $server->node->daemonSecret,
816
                'X-Access-Server' => $server->uuid,
817
            ])->request('DELETE', '/servers');
818
819
            $server->forceDelete();
820
            DB::commit();
821
        } catch (TransferException $ex) {
822
            // Set installed is set to 3 when force deleting.
823
            if ($server->installed === 3 || $force) {
824
                $server->forceDelete();
825
                DB::commit();
826
            } else {
827
                DB::rollBack();
828
                throw $ex;
829
            }
830
        } catch (\Exception $ex) {
831
            DB::rollBack();
832
            throw $ex;
833
        }
834
    }
835
836
    public function cancelDeletion($id)
837
    {
838
        $server = Models\Server::withTrashed()->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
839
        $server->restore();
840
841
        $server->installed = 1;
842
        $server->save();
843
    }
844
845
    public function toggleInstall($id)
846
    {
847
        $server = Models\Server::findOrFail($id);
848
        if ($server->installed === 2) {
849
            throw new DisplayException('This server was marked as having a failed install, you cannot override this.');
850
        }
851
        $server->installed = ! $server->installed;
852
853
        return $server->save();
854
    }
855
856
    /**
857
     * Suspends a server instance making it unable to be booted or used by a user.
858
     * @param  int $id
859
     * @return bool
860
     */
861 View Code Duplication
    public function suspend($id, $deleted = false)
0 ignored issues
show
Unused Code introduced by
The parameter $deleted is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
862
    {
863
        $server = Models\Server::withTrashed()->with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method withTrashed() does not exist on Pterodactyl\Models\Server. Did you maybe mean trashed()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
864
865
        DB::beginTransaction();
866
867
        try {
868
869
            // Already suspended, no need to make more requests.
870
            if ($server->suspended) {
871
                return true;
872
            }
873
874
            $server->suspended = 1;
875
            $server->save();
876
877
            $server->node->guzzleClient([
878
                'X-Access-Token' => $server->node->daemonSecret,
879
                'X-Access-Server' => $server->uuid,
880
            ])->request('POST', '/server/suspend');
881
882
            return DB::commit();
883
        } catch (TransferException $ex) {
884
            DB::rollBack();
885
            throw new DisplayException('An error occured while attempting to contact the remote daemon to suspend this server.', $ex);
886
        } catch (\Exception $ex) {
887
            DB::rollBack();
888
            throw $ex;
889
        }
890
    }
891
892
    /**
893
     * Unsuspends a server instance.
894
     * @param  int $id
895
     * @return bool
896
     */
897 View Code Duplication
    public function unsuspend($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
898
    {
899
        $server = Models\Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
900
901
        DB::beginTransaction();
902
903
        try {
904
905
            // Already unsuspended, no need to make more requests.
906
            if ($server->suspended === 0) {
907
                return true;
908
            }
909
910
            $server->suspended = 0;
911
            $server->save();
912
913
            $server->node->guzzleClient([
914
                'X-Access-Token' => $server->node->daemonSecret,
915
                'X-Access-Server' => $server->uuid,
916
            ])->request('POST', '/server/unsuspend');
917
918
            return DB::commit();
919
        } catch (TransferException $ex) {
920
            DB::rollBack();
921
            throw new DisplayException('An error occured while attempting to contact the remote daemon to un-suspend this server.', $ex);
922
        } catch (\Exception $ex) {
923
            DB::rollBack();
924
            throw $ex;
925
        }
926
    }
927
928 View Code Duplication
    public function updateSFTPPassword($id, $password)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
929
    {
930
        $server = Models\Server::with('node')->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
931
932
        $validator = Validator::make(['password' => $password], [
933
            'password' => 'required|regex:/^((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})$/',
934
        ]);
935
936
        if ($validator->fails()) {
937
            throw new DisplayValidationException(json_encode($validator->errors()));
938
        }
939
940
        DB::beginTransaction();
941
        $server->sftp_password = Crypt::encrypt($password);
942
943
        try {
944
            $server->save();
945
946
            $server->node->guzzleClient([
947
                'X-Access-Token' => $server->node->daemonSecret,
948
                'X-Access-Server' => $server->uuid,
949
            ])->request('POST', '/server/password', [
950
                'json' => ['password' => $password],
951
            ]);
952
953
            DB::commit();
954
955
            return true;
956
        } catch (TransferException $ex) {
957
            DB::rollBack();
958
            throw new DisplayException('There was an error while attmping to contact the remote service to change the password.', $ex);
959
        } catch (\Exception $ex) {
960
            DB::rollBack();
961
            throw $ex;
962
        }
963
    }
964
}
965