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 ( 2dec65...536865 )
by Dane
02:51
created

ServerRepository::delete()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 57
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 57
rs 7.6759
c 0
b 0
f 0
cc 7
eloc 28
nc 4
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
    /**
41
     * An array of daemon permission to assign to this server.
42
     *
43
     * @var array
44
     */
45
    protected $daemonPermissions = [
46
        's:*',
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  null|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
     *
79
     * @param   array  $data
80
     * @return \Pterodactyl\Models\Server
81
     *
82
     * @throws \Pterodactyl\Exceptions\DisplayException
83
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
84
     */
85
    public function create(array $data)
86
    {
87
88
        // Validate Fields
89
        $validator = Validator::make($data, [
90
            'user_id' => 'required|exists:users,id',
91
            'name' => 'required|regex:/^([\w .-]{1,200})$/',
92
            'memory' => 'required|numeric|min:0',
93
            'swap' => 'required|numeric|min:-1',
94
            'io' => 'required|numeric|min:10|max:1000',
95
            'cpu' => 'required|numeric|min:0',
96
            'disk' => 'required|numeric|min:0',
97
            'service_id' => 'required|numeric|min:1|exists:services,id',
98
            'option_id' => 'required|numeric|min:1|exists:service_options,id',
99
            'location_id' => 'required|numeric|min:1|exists:locations,id',
100
            'pack_id' => 'sometimes|nullable|numeric|min:0',
101
            'custom_container' => 'string',
102
            'startup' => 'string',
103
            'auto_deploy' => 'sometimes|required|accepted',
104
            'custom_id' => 'sometimes|required|numeric|unique:servers,id',
105
        ]);
106
107
        $validator->sometimes('node_id', 'required|numeric|min:1|exists:nodes,id', function ($input) {
108
            return ! ($input->auto_deploy);
109
        });
110
111
        $validator->sometimes('allocation_id', 'required|numeric|exists:allocations,id', function ($input) {
112
            return ! ($input->auto_deploy);
113
        });
114
115
        $validator->sometimes('allocation_additional.*', 'sometimes|required|numeric|exists:allocations,id', function ($input) {
116
            return ! ($input->auto_deploy);
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(json_encode($validator->errors()));
123
        }
124
125
        $user = Models\User::findOrFail($data['user_id']);
126
127
        $autoDeployed = false;
128
        if (isset($data['auto_deploy']) && $data['auto_deploy']) {
129
            // This is an auto-deployment situation
130
            // Ignore any other passed node data
131
            unset($data['node_id'], $data['allocation_id']);
132
133
            $autoDeployed = true;
134
            $node = DeploymentService::smartRandomNode($data['memory'], $data['disk'], $data['location_id']);
135
            $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...
136
        } else {
137
            $node = Models\Node::findOrFail($data['node_id']);
138
        }
139
140
        // Verify IP & Port are a.) free and b.) assigned to the node.
141
        // We know the node exists because of 'exists:nodes,id' in the validation
142
        if (! $autoDeployed) {
143
            $allocation = Models\Allocation::where('id', $data['allocation_id'])->where('node_id', $data['node_id'])->whereNull('server_id')->first();
144
        }
145
146
        // Something failed in the query, either that combo doesn't exist, or it is in use.
147
        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...
148
            throw new DisplayException('The selected Allocation ID is either already in use, or unavaliable for this node.');
149
        }
150
151
        // Validate those Service Option Variables
152
        // We know the service and option exists because of the validation.
153
        // We need to verify that the option exists for the service, and then check for
154
        // any required variable fields. (fields are labeled env_<env_variable>)
155
        $option = Models\ServiceOption::where('id', $data['option_id'])->where('service_id', $data['service_id'])->first();
156
        if (! $option) {
157
            throw new DisplayException('The requested service option does not exist for the specified service.');
158
        }
159
160
        // Validate the Pack
161
        if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
162
            $data['pack_id'] = null;
163
        } else {
164
            $pack = Models\Pack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
165
            if (! $pack) {
166
                throw new DisplayException('The requested service pack does not seem to exist for this combination.');
167
            }
168
        }
169
170
        // Load up the Service Information
171
        $service = Models\Service::find($option->service_id);
172
173
        // Check those Variables
174
        $variables = Models\ServiceVariable::where('option_id', $data['option_id'])->get();
175
        $variableList = [];
176
        if ($variables) {
177
            foreach ($variables as $variable) {
178
179
                // Is the variable required?
180
                if (! isset($data['env_' . $variable->env_variable])) {
181
                    if ($variable->required) {
182
                        throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
183
                    }
184
                    $variableList[] = [
185
                        'id' => $variable->id,
186
                        'env' => $variable->env_variable,
187
                        'val' => $variable->default_value,
188
                    ];
189
                    continue;
190
                }
191
192
                // Check aganist Regex Pattern
193
                if (! is_null($variable->regex) && ! preg_match($variable->regex, $data['env_' . $variable->env_variable])) {
194
                    throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').');
195
                }
196
197
                $variableList[] = [
198
                    'id' => $variable->id,
199
                    'env' => $variable->env_variable,
200
                    'val' => $data['env_' . $variable->env_variable],
201
                ];
202
                continue;
203
            }
204
        }
205
206
        // Check Overallocation
207
        if (! $autoDeployed) {
208
            if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) {
209
                $totals = Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node_id', $node->id)->first();
0 ignored issues
show
Bug introduced by
The method select() does not exist on Pterodactyl\Models\Server. Did you maybe mean addSelectsToQuery()?

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...
210
211
                // Check memory limits
212 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...
213
                    $newMemory = $totals->memory + $data['memory'];
214
                    $memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100)));
215
                    if ($newMemory > $memoryLimit) {
216
                        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.');
217
                    }
218
                }
219
220
                // Check Disk Limits
221 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...
222
                    $newDisk = $totals->disk + $data['disk'];
223
                    $diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100)));
224
                    if ($newDisk > $diskLimit) {
225
                        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.');
226
                    }
227
                }
228
            }
229
        }
230
231
        DB::beginTransaction();
232
233
        try {
234
            $uuid = new UuidService;
235
236
            // Add Server to the Database
237
            $server = new Models\Server;
238
            $genUuid = $uuid->generate('servers', 'uuid');
239
            $genShortUuid = $uuid->generateShort('servers', 'uuidShort', $genUuid);
240
241
            if (isset($data['custom_id'])) {
242
                $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...
243
            }
244
245
            $server->fill([
246
                'uuid' => $genUuid,
247
                'uuidShort' => $genShortUuid,
248
                'node_id' => $node->id,
249
                'name' => $data['name'],
250
                'suspended' => 0,
251
                'owner_id' => $user->id,
252
                'memory' => $data['memory'],
253
                'swap' => $data['swap'],
254
                'disk' => $data['disk'],
255
                'io' => $data['io'],
256
                'cpu' => $data['cpu'],
257
                'oom_disabled' => (isset($data['oom_disabled'])) ? true : false,
258
                'allocation_id' => $allocation->id,
259
                'service_id' => $data['service_id'],
260
                'option_id' => $data['option_id'],
261
                'pack_id' => $data['pack_id'],
262
                'startup' => $data['startup'],
263
                'daemonSecret' => $uuid->generate('servers', 'daemonSecret'),
264
                'image' => (isset($data['custom_container']) && ! empty($data['custom_container'])) ? $data['custom_container'] : $option->docker_image,
265
                'username' => $this->generateSFTPUsername($data['name'], $genShortUuid),
266
                'sftp_password' => Crypt::encrypt('not set'),
267
            ]);
268
            $server->save();
269
270
            // Mark Allocation in Use
271
            $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...
272
            $allocation->save();
273
274
            // Add Additional Allocations
275
            if (isset($data['allocation_additional']) && is_array($data['allocation_additional'])) {
276
                foreach ($data['allocation_additional'] as $allocation) {
277
                    $model = Models\Allocation::where('id', $allocation)->where('node_id', $data['node_id'])->whereNull('server_id')->first();
278
                    if (! $model) {
279
                        continue;
280
                    }
281
282
                    $model->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...
283
                    $model->save();
284
                }
285
            }
286
287
            // Add Variables
288
            $environmentVariables = [
289
                'STARTUP' => $data['startup'],
290
            ];
291
292
            foreach ($variableList as $item) {
293
                $environmentVariables[$item['env']] = $item['val'];
294
295
                Models\ServerVariable::create([
296
                    '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...
297
                    'variable_id' => $item['id'],
298
                    'variable_value' => $item['val'],
299
                ]);
300
            }
301
302
            $server->load('allocation', 'allocations');
303
            $node->guzzleClient(['X-Access-Token' => $node->daemonSecret])->request('POST', '/servers', [
304
                'json' => [
305
                    '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...
306
                    '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...
307
                    'build' => [
308
                        'default' => [
309
                            'ip' => $server->allocation->ip,
0 ignored issues
show
Documentation introduced by
The property allocation 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...
310
                            'port' => $server->allocation->port,
0 ignored issues
show
Documentation introduced by
The property allocation 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...
311
                        ],
312
                        'ports' => $server->allocations->groupBy('ip')->map(function ($item) {
0 ignored issues
show
Documentation introduced by
The property allocations 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
                            return $item->pluck('port');
314
                        })->toArray(),
315
                        'env' => $environmentVariables,
316
                        '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...
317
                        '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...
318
                        '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...
319
                        '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...
320
                        '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...
321
                        'image' => $server->image,
0 ignored issues
show
Documentation introduced by
The property image 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...
322
                    ],
323
                    'service' => [
324
                        'type' => $service->folder,
325
                        'option' => $option->tag,
326
                        'pack' => (isset($pack)) ? $pack->uuid : null,
327
                    ],
328
                    'keys' => [
329
                        (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...
330
                    ],
331
                    'rebuild' => false,
332
                ],
333
            ]);
334
335
            DB::commit();
336
337
            return $server;
338
        } catch (TransferException $ex) {
339
            DB::rollBack();
340
            throw new DisplayException('There was an error while attempting to connect to the daemon to add this server.', $ex);
341
        } catch (\Exception $ex) {
342
            DB::rollBack();
343
            throw $ex;
344
        }
345
    }
346
347
    /**
348
     * Update the details for a server.
349
     *
350
     * @param  int    $id
351
     * @param  array  $data
352
     * @return bool
353
     *
354
     * @throws \Pterodactyl\Exceptions\DisplayException
355
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
356
     */
357
    public function updateDetails($id, array $data)
358
    {
359
        $uuid = new UuidService;
360
        $resetDaemonKey = false;
361
362
        // Validate Fields
363
        $validator = Validator::make($data, [
364
            'owner_id' => 'sometimes|required|integer|exists:users,id',
365
            'name' => 'sometimes|required|regex:([\w .-]{1,200})',
366
            'reset_token' => 'sometimes|required|accepted',
367
        ]);
368
369
        // Run validator, throw catchable and displayable exception if it fails.
370
        // Exception includes a JSON result of failed validation rules.
371
        if ($validator->fails()) {
372
            throw new DisplayValidationException(json_encode($validator->errors()));
373
        }
374
375
        DB::beginTransaction();
376
377
        try {
378
            $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...
379
380
            // Update daemon secret if it was passed.
381
            if (isset($data['reset_token']) || (isset($data['owner_id']) && (int) $data['owner_id'] !== $server->user->id)) {
382
                $oldDaemonKey = $server->daemonSecret;
383
                $server->daemonSecret = $uuid->generate('servers', 'daemonSecret');
384
                $resetDaemonKey = true;
385
            }
386
387
            // Update Server Owner if it was passed.
388
            if (isset($data['owner_id']) && (int) $data['owner_id'] !== $server->user->id) {
389
                $server->owner_id = $data['owner_id'];
390
            }
391
392
            // Update Server Name if it was passed.
393
            if (isset($data['name'])) {
394
                $server->name = $data['name'];
395
            }
396
397
            // Save our changes
398
            $server->save();
399
400
            // Do we need to update? If not, return successful.
401
            if (! $resetDaemonKey) {
402
                DB::commit();
403
404
                return true;
405
            }
406
407
            $res = $server->node->guzzleClient([
408
                'X-Access-Server' => $server->uuid,
409
                'X-Access-Token' => $server->node->daemonSecret,
410
            ])->request('PATCH', '/server', [
411
                'exceptions' => false,
412
                'json' => [
413
                    'keys' => [
414
                        (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...
415
                        (string) $server->daemonSecret => $this->daemonPermissions,
416
                    ],
417
                ],
418
            ]);
419
420
            if ($res->getStatusCode() === 204) {
421
                DB::commit();
422
423
                return true;
424
            } else {
425
                throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode());
426
            }
427
        } catch (\Exception $ex) {
428
            DB::rollBack();
429
            Log::error($ex);
430
            throw new DisplayException('An error occured while attempting to update this server\'s information.');
431
        }
432
    }
433
434
    /**
435
     * Update the container for a server.
436
     *
437
     * @param  int    $id
438
     * @param  array  $data
439
     * @return bool
440
     *
441
     * @throws \Pterodactyl\Exceptions\DisplayException
442
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
443
     */
444 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...
445
    {
446
        $validator = Validator::make($data, [
447
            'docker_image' => 'required|string',
448
        ]);
449
450
        // Run validator, throw catchable and displayable exception if it fails.
451
        // Exception includes a JSON result of failed validation rules.
452
        if ($validator->fails()) {
453
            throw new DisplayValidationException(json_encode($validator->errors()));
454
        }
455
456
        DB::beginTransaction();
457
        try {
458
            $server = Models\Server::findOrFail($id);
459
460
            $server->image = $data['docker_image'];
461
            $server->save();
462
463
            $server->node->guzzleClient([
464
                'X-Access-Server' => $server->uuid,
465
                'X-Access-Token' => $server->node->daemonSecret,
466
            ])->request('PATCH', '/server', [
467
                'json' => [
468
                    'build' => [
469
                        'image' => $server->image,
470
                    ],
471
                ],
472
            ]);
473
474
            DB::commit();
475
476
            return true;
477
        } catch (TransferException $ex) {
478
            DB::rollBack();
479
            throw new DisplayException('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.', $ex);
480
        } catch (\Exception $ex) {
481
            DB::rollBack();
482
            throw $ex;
483
        }
484
    }
485
486
    /**
487
     * Update the build details for a server.
488
     *
489
     * @param  int    $id
490
     * @param  array  $data
491
     * @return \Pterodactyl\Models\Server
492
     *
493
     * @throws \Pterodactyl\Exceptions\DisplayException
494
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
495
     */
496
    public function changeBuild($id, array $data)
497
    {
498
        $validator = Validator::make($data, [
499
            'allocation_id' => 'sometimes|required|exists:allocations,id',
500
            'add_allocations' => 'sometimes|required|array',
501
            'remove_allocations' => 'sometimes|required|array',
502
            'memory' => 'sometimes|required|integer|min:0',
503
            'swap' => 'sometimes|required|integer|min:-1',
504
            'io' => 'sometimes|required|integer|min:10|max:1000',
505
            'cpu' => 'sometimes|required|integer|min:0',
506
            'disk' => 'sometimes|required|integer|min:0',
507
        ]);
508
509
        // Run validator, throw catchable and displayable exception if it fails.
510
        // Exception includes a JSON result of failed validation rules.
511
        if ($validator->fails()) {
512
            throw new DisplayValidationException(json_encode($validator->errors()));
513
        }
514
515
        DB::beginTransaction();
516
517
        try {
518
            $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...
519
            $newBuild = [];
520
            $newAllocations = [];
0 ignored issues
show
Unused Code introduced by
$newAllocations is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
521
522
            if (isset($data['allocation_id'])) {
523
                if ((int) $data['allocation_id'] !== $server->allocation_id) {
524
                    $selection = $server->allocations->where('id', $data['allocation_id'])->first();
525
                    if (! $selection) {
526
                        throw new DisplayException('The requested default connection is not allocated to this server.');
527
                    }
528
529
                    $server->allocation_id = $selection->id;
530
                    $newBuild['default'] = ['ip' => $selection->ip, 'port' => $selection->port];
531
532
                    $server->load('allocation');
533
                }
534
            }
535
536
            $newPorts = false;
537
            // Remove Assignments
538 View Code Duplication
            if (isset($data['remove_allocations'])) {
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...
539
                foreach ($data['remove_allocations'] as $allocation) {
540
                    // Can't remove the assigned IP/Port combo
541
                    if ((int) $allocation === $server->allocation_id) {
542
                        continue;
543
                    }
544
545
                    $newPorts = true;
546
                    Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([
547
                        'server_id' => null,
548
                    ]);
549
                }
550
551
                $server->load('allocations');
552
            }
553
554
            // Add Assignments
555 View Code Duplication
            if (isset($data['add_allocations'])) {
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...
556
                foreach ($data['add_allocations'] as $allocation) {
557
                    $model = Models\Allocation::where('id', $allocation)->whereNull('server_id')->first();
558
                    if (! $model) {
559
                        continue;
560
                    }
561
562
                    $newPorts = true;
563
                    $model->update([
564
                        'server_id' => $server->id,
565
                    ]);
566
                }
567
568
                $server->load('allocations');
569
            }
570
571
            if ($newPorts) {
572
                $newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) {
573
                    return $item->pluck('port');
574
                })->toArray();
575
            }
576
577
            // @TODO: verify that server can be set to this much memory without
578
            // going over node limits.
579 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...
580
                $server->memory = $data['memory'];
581
                $newBuild['memory'] = (int) $server->memory;
582
            }
583
584 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...
585
                $server->swap = $data['swap'];
586
                $newBuild['swap'] = (int) $server->swap;
587
            }
588
589
            // @TODO: verify that server can be set to this much disk without
590
            // going over node limits.
591 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...
592
                $server->disk = $data['disk'];
593
                $newBuild['disk'] = (int) $server->disk;
594
            }
595
596 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...
597
                $server->cpu = $data['cpu'];
598
                $newBuild['cpu'] = (int) $server->cpu;
599
            }
600
601 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...
602
                $server->io = $data['io'];
603
                $newBuild['io'] = (int) $server->io;
604
            }
605
606
            // Try save() here so if it fails we haven't contacted the daemon
607
            // This won't be committed unless the HTTP request succeedes anyways
608
            $server->save();
609
610
            if (! empty($newBuild)) {
611
                $server->node->guzzleClient([
612
                    'X-Access-Server' => $server->uuid,
613
                    'X-Access-Token' => $server->node->daemonSecret,
614
                ])->request('PATCH', '/server', [
615
                    'json' => [
616
                        'build' => $newBuild,
617
                    ],
618
                ]);
619
            }
620
621
            DB::commit();
622
623
            return $server;
624
        } catch (TransferException $ex) {
625
            DB::rollBack();
626
            throw new DisplayException('A TransferException occured while attempting to update the server configuration, check that the daemon is online. This error has been logged.', $ex);
627
        } catch (\Exception $ex) {
628
            DB::rollBack();
629
            throw $ex;
630
        }
631
    }
632
633
    /**
634
     * Update the startup details for a server.
635
     *
636
     * @param  int    $id
637
     * @param  array  $data
638
     * @param  bool   $admin
639
     * @return void
640
     *
641
     * @throws \GuzzleHttp\Exception\RequestException
642
     * @throws \Pterodactyl\Exceptions\DisplayException
643
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
644
     */
645
    public function updateStartup($id, array $data, $admin = false)
646
    {
647
        $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...
648
649
        DB::transaction(function () use ($admin, $data, $server) {
650
            if (isset($data['startup']) && $admin) {
651
                $server->startup = $data['startup'];
652
                $server->save();
653
            }
654
655
            if ($server->option->variables) {
656
                foreach ($server->option->variables as &$variable) {
657
                    $set = isset($data['env_' . $variable->id]);
658
659
                    // If user is not an admin and are trying to edit a non-editable field
660
                    // or an invisible field just silently skip the variable.
661
                    if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
662
                        continue;
663
                    }
664
665
                    // Perform Field Validation
666
                    $validator = Validator::make([
667
                        'variable_value' => ($set) ? $data['env_' . $variable->id] : null,
668
                    ], [
669
                        'variable_value' => $variable->rules,
670
                    ]);
671
672
                    if ($validator->fails()) {
673
                        throw new DisplayValidationException(json_encode(
674
                            collect([
675
                                'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'],
676
                            ])->merge($validator->errors()->toArray())
677
                        ));
678
                    }
679
680
                    $svar = Models\ServerVariable::firstOrNew([
681
                        'server_id' => $server->id,
682
                        'variable_id' => $variable->id,
683
                    ]);
684
685
                    // Set the value; if one was not passed set it to the default value
686
                    if ($set) {
687
                        $svar->variable_value = $data['env_' . $variable->id];
688
689
                    // Not passed, check if this record exists if so keep value, otherwise set default
690
                    } else {
691
                        $svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
692
                    }
693
694
                    $svar->save();
695
                }
696
            }
697
698
            // Reload Variables
699
            $server->load('variables');
700 View Code Duplication
            $environment = $server->option->variables->map(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...
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...
701
                $display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
702
703
                return [
704
                    'variable' => $item->env_variable,
705
                    'value' => (! is_null($display)) ? $display : $item->default_value,
706
                ];
707
            });
708
709
            $server->node->guzzleClient([
710
                'X-Access-Server' => $server->uuid,
711
                'X-Access-Token' => $server->node->daemonSecret,
712
            ])->request('PATCH', '/server', [
713
                'json' => [
714
                    'build' => [
715
                        'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]),
716
                    ],
717
                ],
718
            ]);
719
        });
720
    }
721
722
    /**
723
     * Delete a server from the system permanetly.
724
     *
725
     * @param  int   $id
726
     * @param  bool  $force
727
     * @return void
728
     *
729
     * @throws \Pterodactyl\Exceptions\DisplayException
730
     */
731
    public function delete($id, $force = false)
732
    {
733
        $server = Models\Server::with('node', 'allocations', '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...
734
735
        // Due to MySQL lockouts if the daemon response fails, we need to
736
        // delete the server from the daemon first. If it succeedes and then
737
        // MySQL fails, users just need to force delete the server.
738
        //
739
        // If this is a force delete, continue anyways.
740
        try {
741
            $server->node->guzzleClient([
742
                'X-Access-Token' => $server->node->daemonSecret,
743
                'X-Access-Server' => $server->uuid,
744
            ])->request('DELETE', '/servers');
745
        } catch (TransferException $ex) {
746
            if (! $force) {
747
                throw new DisplayException($ex->getMessage());
748
            }
749
        } catch (\Exception $ex) {
750
            throw $ex;
751
        }
752
753
        DB::transaction(function () use ($server) {
754
            $server->allocations->each(function ($item) {
755
                $item->server_id = null;
756
                $item->save();
757
            });
758
759
            $server->variables->each(function ($item) {
760
                $item->delete();
761
            });
762
763
            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...
764
                foreach ($subuser->permissions as &$permission) {
765
                    $permission->delete();
766
                }
767
                $subuser->delete();
768
            }
769
770
            // Remove Downloads
771
            Models\Download::where('server', $server->uuid)->delete();
772
773
            // Clear Tasks
774
            Models\Task::where('server', $server->id)->delete();
775
776
            // Delete Databases
777
            // This is the one un-recoverable point where
778
            // transactions will not save us.
779
            $repository = new DatabaseRepository;
780
            foreach (Models\Database::select('id')->where('server_id', $server->id)->get() as $database) {
781
                $repository->drop($database->id);
782
            }
783
784
            // Fully delete the server.
785
            $server->delete();
786
        });
787
    }
788
789
    /**
790
     * Toggle the install status of a serve.
791
     *
792
     * @param  int    $id
793
     * @return bool
794
     *
795
     * @throws \Pterodactyl\Exceptions\DisplayException
796
     */
797
    public function toggleInstall($id)
798
    {
799
        $server = Models\Server::findOrFail($id);
800
        if ($server->installed > 1) {
801
            throw new DisplayException('This server was marked as having a failed install or being deleted, you cannot override this.');
802
        }
803
        $server->installed = ! $server->installed;
804
805
        return $server->save();
806
    }
807
808
    /**
809
     * Suspends a server.
810
     *
811
     * @param  int   $id
812
     * @param  bool  $deleted
813
     * @return void
814
     *
815
     * @throws \Pterodactyl\Exceptions\DisplayException
816
     */
817 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...
818
    {
819
        $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...
820
821
        DB::beginTransaction();
822
823
        try {
824
825
            // Already suspended, no need to make more requests.
826
            if ($server->suspended) {
827
                return true;
828
            }
829
830
            $server->suspended = 1;
831
            $server->save();
832
833
            $server->node->guzzleClient([
834
                'X-Access-Token' => $server->node->daemonSecret,
835
                'X-Access-Server' => $server->uuid,
836
            ])->request('POST', '/server/suspend');
837
838
            return DB::commit();
839
        } catch (TransferException $ex) {
840
            DB::rollBack();
841
            throw new DisplayException('An error occured while attempting to contact the remote daemon to suspend this server.', $ex);
842
        } catch (\Exception $ex) {
843
            DB::rollBack();
844
            throw $ex;
845
        }
846
    }
847
848
    /**
849
     * Unsuspends a server.
850
     *
851
     * @param  int   $id
852
     * @return void
853
     *
854
     * @throws \Pterodactyl\Exceptions\DisplayException
855
     */
856 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...
857
    {
858
        $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...
859
860
        DB::beginTransaction();
861
862
        try {
863
864
            // Already unsuspended, no need to make more requests.
865
            if ($server->suspended === 0) {
866
                return true;
867
            }
868
869
            $server->suspended = 0;
870
            $server->save();
871
872
            $server->node->guzzleClient([
873
                'X-Access-Token' => $server->node->daemonSecret,
874
                'X-Access-Server' => $server->uuid,
875
            ])->request('POST', '/server/unsuspend');
876
877
            return DB::commit();
878
        } catch (TransferException $ex) {
879
            DB::rollBack();
880
            throw new DisplayException('An error occured while attempting to contact the remote daemon to un-suspend this server.', $ex);
881
        } catch (\Exception $ex) {
882
            DB::rollBack();
883
            throw $ex;
884
        }
885
    }
886
887
    /**
888
     * Updates the SFTP password for a server.
889
     *
890
     * @param  int     $id
891
     * @param  string  $password
892
     * @return void
893
     *
894
     * @throws \Pterodactyl\Exceptions\DisplayException
895
     * @throws \Pterodactyl\Exceptions\DisplayValidationException
896
     */
897 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...
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
        $validator = Validator::make(['password' => $password], [
902
            'password' => 'required|regex:/^((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})$/',
903
        ]);
904
905
        if ($validator->fails()) {
906
            throw new DisplayValidationException(json_encode($validator->errors()));
907
        }
908
909
        DB::beginTransaction();
910
        $server->sftp_password = Crypt::encrypt($password);
911
912
        try {
913
            $server->save();
914
915
            $server->node->guzzleClient([
916
                'X-Access-Token' => $server->node->daemonSecret,
917
                'X-Access-Server' => $server->uuid,
918
            ])->request('POST', '/server/password', [
919
                'json' => ['password' => $password],
920
            ]);
921
922
            DB::commit();
923
924
            return true;
925
        } catch (TransferException $ex) {
926
            DB::rollBack();
927
            throw new DisplayException('There was an error while attmping to contact the remote service to change the password.', $ex);
928
        } catch (\Exception $ex) {
929
            DB::rollBack();
930
            throw $ex;
931
        }
932
    }
933
}
934