TeamspeakServer   B
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 423
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 37
c 1
b 1
f 0
lcom 1
cbo 3
dl 0
loc 423
rs 8.6

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A setQueryPort() 0 6 1
A getQueryPort() 0 4 1
A getQueryLogin() 0 4 1
A setQueryPassword() 0 9 2
A getQueryPassword() 0 4 1
A setFiletransferPort() 0 6 1
A getFiletransferPort() 0 4 1
A setVoicePort() 0 6 1
A getVoicePort() 0 4 1
A setLicenceFile() 0 6 1
A hasLicenceFile() 0 4 1
A getInstallationProgress() 0 16 2
B installServer() 0 28 2
B finalizeInstallation() 0 29 3
A uploadConfigFile() 0 14 1
A uploadLicenceFile() 0 19 2
A getGeneralConfig() 0 18 1
A getDatabaseConfig() 0 10 1
A changeState() 0 19 3
A getStartParams() 0 13 3
A getAbsoluteDir() 0 4 1
A getName() 0 4 1
A getFullName() 0 4 1
A getType() 0 4 1
A __toString() 0 4 1
B loadValidatorMetadata() 0 29 1
1
<?php
2
3
/**
4
 * This file is part of Dedipanel project
5
 *
6
 * (c) 2010-2015 Dedipanel <http://www.dedicated-panel.net>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace DP\VoipServer\TeamspeakServerBundle\Entity;
13
14
use Dedipanel\PHPSeclibWrapperBundle\Connection\Connection;
15
use Doctrine\ORM\Mapping as ORM;
16
use DP\VoipServer\TeamspeakServerBundle\Service\ServerQueryFactory;
17
use DP\VoipServer\VoipServerBundle\Entity\VoipServer;
18
use Symfony\Component\HttpFoundation\File\UploadedFile;
19
use Symfony\Component\Validator\Constraints as Assert;
20
use Symfony\Component\Validator\Mapping\ClassMetadata;
21
22
/**
23
 * TeamspeakServer
24
 *
25
 * @ORM\Table(name="teamspeak_server")
26
 * @ORM\Entity(repositoryClass="DP\Core\CoreBundle\Entity\MachineRelatedRepository")
27
 */
28
class TeamspeakServer extends VoipServer
29
{
30
    /**
31
     * @var integer $queryPort
32
     *
33
     * @ORM\Column(name="query_port", type="integer", nullable=true)
34
     */
35
    private $queryPort;
36
37
    /**
38
     * @var string $queryLogin
39
     *
40
     * @ORM\Column(name="query_login", type="string", length=32, nullable=true)
41
     */
42
    private $queryLogin;
43
44
    /**
45
     * @var string $queryPassword
46
     *
47
     * @ORM\Column(name="query_passwd", type="string", length=32, nullable=true)
48
     */
49
    private $queryPassword;
50
51
    /**
52
     * @var integer $filetransferPort
53
     *
54
     * @ORM\Column(name="filetransfer_port", type="integer", nullable=true)
55
     */
56
    private $filetransferPort;
57
58
    /**
59
     * @var integer $voicePort
60
     *
61
     * @ORM\Column(name="voice_port", type="integer", nullable=true)
62
     */
63
    private $voicePort;
64
65
    /**
66
     * @var UploadedFile $licenceFile
67
     */
68
    private $licenceFile;
69
70
    /** @var bool $firstStart */
71
    private $firstStart;
72
73
74
    public function __construct()
75
    {
76
        parent::__construct();
77
78
        $this->voicePort = 9987;
79
        $this->queryPort  = 10011;
80
        $this->queryLogin = 'serveradmin';
81
        $this->filetransferPort = 30033;
82
    }
83
84
    /**
85
     * Set the port needed by the query
86
     *
87
     * @param integer $port
88
     * @return integer
89
     */
90
    public function setQueryPort($port)
91
    {
92
        $this->queryPort = $port;
93
94
        return $this;
95
    }
96
97
    /**
98
     * Get the port needed by the query
99
     *
100
     * @return integer
101
     */
102
    public function getQueryPort()
103
    {
104
        return $this->queryPort;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->queryPort; (integer) is incompatible with the return type declared by the abstract method DP\VoipServer\VoipServer...oipServer::getQueryPort of type DP\VoipServer\VoipServer...ections\ArrayCollection.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
105
    }
106
107
    /**
108
     * Get the login needed by the query
109
     *
110
     * @return string
111
     */
112
    public function getQueryLogin()
113
    {
114
        return $this->queryLogin;
115
    }
116
117
    /**
118
     * Set the password needed by the query
119
     *
120
     * @param string $password
121
     * @return TeamspeakServer
122
     */
123
    public function setQueryPassword($password)
124
    {
125
        // Prevent unsetting the password from forms
126
        if (!empty($password)) {
127
            $this->queryPassword = $password;
128
        }
129
130
        return $this;
131
    }
132
133
    /**
134
     * Get the password needed by the query
135
     *
136
     * @return string
137
     */
138
    public function getQueryPassword()
139
    {
140
        return $this->queryPassword;
141
    }
142
143
    /**
144
     * Set the filetransfer port used by teamspeak
145
     *
146
     * @param integer $filetransferPort
147
     * @return TeamspeakServer
148
     */
149
    public function setFiletransferPort($filetransferPort)
150
    {
151
        $this->filetransferPort = $filetransferPort;
152
153
        return $this;
154
    }
155
156
    /**
157
     * Get the filetransfer port used by teamspeak
158
     *
159
     * @return integer
160
     */
161
    public function getFiletransferPort()
162
    {
163
        return $this->filetransferPort;
164
    }
165
166
    /**
167
     * Get the default voice port
168
     * (will be used by the first instance)
169
     *
170
     * @param integer $voicePort
171
     * @return TeamspeakServer
172
     */
173
    public function setVoicePort($voicePort)
174
    {
175
        $this->voicePort = $voicePort;
176
177
        return $this;
178
    }
179
180
    /**
181
     * Get the default voice port
182
     * (will be used by the first instance)
183
     *
184
     * @return integer
185
     */
186
    public function getVoicePort()
187
    {
188
        return $this->voicePort;
189
    }
190
191
    /**
192
     * Set the uploaded licence file
193
     *
194
     * @param UploadedFile $licenceFile
195
     * @return TeamspeakServer
196
     */
197
    public function setLicenceFile(UploadedFile $licenceFile = null)
198
    {
199
        $this->licenceFile = $licenceFile;
200
201
        return $this;
202
    }
203
204
    /**
205
     * Has the licence file been uploaded ?
206
     *
207
     * @return boolean
208
     */
209
    public function hasLicenceFile()
210
    {
211
        return $this->licenceFile !== null;
212
    }
213
214
    /** {@inheritdoc} */
215
    public function getInstallationProgress()
216
    {
217
        $conn       = $this->getMachine()->getConnection();
218
        $installDir = $this->getAbsoluteDir();
219
        $logPath    = $installDir . 'install.log';
220
221
        if ($conn->fileExists($installDir . '/ts3server_startscript.sh')) {
222
            return 100;
223
        }
224
225
        // On récupère les 20 dernières lignes du fichier afin de déterminer le pourcentage
226
        $installLog = $conn->exec('tail -n 20 ' . $logPath);
227
        $percent    = $this->getPercentFromInstallLog($installLog);
228
229
        return $percent;
230
    }
231
232
    /** {@inheritdoc} */
233
    public function installServer(\Twig_Environment $twig)
234
    {
235
        $conn = $this->getMachine()->getConnection();
236
        $installDir = $this->getAbsoluteDir();
237
        $logPath = $installDir . '/install.log';
238
        $tempPath = $installDir . '/server.tgz';
239
240
        $conn->mkdir($installDir);
241
242
        $dlUrl = 'http://dl.4players.de/ts/releases/3.0.10.3/teamspeak3-server_linux-x86-3.0.10.3.tar.gz';
243
        $untarDir = $installDir . '/teamspeak3-server_linux-x86';
244
        if ($this->getMachine()->is64Bit()) {
245
            $dlUrl = 'http://dl.4players.de/ts/releases/3.0.10.3/teamspeak3-server_linux-amd64-3.0.10.3.tar.gz';
246
            $untarDir = $installDir . '/teamspeak3-server_linux-amd64';
247
        }
248
249
        $cmd  = 'wget -o ' . $logPath . ' -O ' . $tempPath . ' ' . $dlUrl . ' ';
250
        $cmd .= '&& tar zxf ' . $tempPath . ' -C ' . $installDir . ' ';
251
        $cmd .= '&& mv ' . $untarDir . '/* ' . $installDir . ' ';
252
        $cmd .= '&& wget -O ' . $installDir . '/sql/defaults.sql http://media.teamspeak.com/literature/defaults.sql ';
253
        $cmd .= '&& rm -Rf ' . $untarDir . ' ' . $tempPath . ' ' . $logPath;
254
255
        $conn->exec($cmd);
256
257
        $this->installationStatus = 0;
258
259
        return true;
260
    }
261
262
    /** {@inheritdoc} */
263
    public function finalizeInstallation(\Twig_Environment $twig)
264
    {
265
        $conn = $this->getMachine()->getConnection();
266
        $installDir = $this->getAbsoluteDir();
267
268
        if (!$conn->dirExists($installDir)) {
269
            return false;
270
        }
271
272
        $this->uploadConfigFile();
273
274
        if ($this->hasLicenceFile()) {
275
            $this->uploadLicenceFile();
276
        }
277
278
        $conn->exec("echo \$SSH_CLIENT | awk '{print \$1}' >> ${installDir}/query_ip_whitelist.txt");
279
280
        $this->firstStart = true;
281
        $this->changeState('start');
282
        sleep(2);
283
        $this->changeState('stop');
284
285
        $this->firstStart = false;
286
        $this->changeState('start');
287
288
        $this->installationStatus = 101;
289
290
        return true;
291
    }
292
293
    public function uploadConfigFile()
294
    {
295
        $config  = '';
296
        $config .= $this->getGeneralConfig() . "\n";
297
        $config .= $this->getDatabaseConfig();
298
299
        $filepath = $this->getAbsoluteDir() . '/ts3server.ini';
300
301
        return $this
302
            ->getMachine()
303
            ->getConnection()
304
            ->upload($filepath, $config, 0750)
305
        ;
306
    }
307
308
    public function uploadLicenceFile()
309
    {
310
        if (!$this->hasLicenceFile()) {
311
            return false;
312
        }
313
314
        $licencePath = $this->getAbsoluteDir() . '/serverkey.dat';
315
        $filepath = $this->licenceFile->getPathname();
316
        $content = file_get_contents($filepath);
317
318
        @unlink($filepath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
319
        unset($this->licenceFile);
320
321
        return $this
322
            ->getMachine()
323
            ->getConnection()
324
            ->upload($licencePath, $content, 0750)
325
        ;
326
    }
327
328
    public function getGeneralConfig()
329
    {
330
        $publicIp  = $this->getMachine()->getPublicIp();
331
        $privateIp = $this->getMachine()->getPrivateIp();
332
333
        return <<<EOF
334
machine_id=
335
default_voice_port={$this->voicePort}
336
voice_ip=${publicIp}
337
licensepath=
338
filetransfer_port={$this->filetransferPort}
339
filetransfer_ip={$publicIp}
340
query_port={$this->queryPort}
341
query_ip={$privateIp}
342
logpath=logs/
343
logquerycommands=0
344
EOF;
345
    }
346
347
    public function getDatabaseConfig()
348
    {
349
        return <<<EOF
350
dbplugin=ts3db_sqlite3
351
dbpluginparameter=
352
dbsqlpath=sql/
353
dbsqlcreatepath=create_sqlite/
354
EOF;
355
356
    }
357
358
    /** {@inheritdoc} */
359
    public function changeState($state)
360
    {
361
        $cmd = $this->getAbsoluteDir() . '/ts3server_startscript.sh ' . $state;
362
363
        if ($state == 'start') {
364
            $cmd .= ' ' . $this->getStartParams();
365
        }
366
367
        $core = $this->getCore();
368
        if (!empty($core)) {
369
            $cmd = 'taskset -c ' . implode(',', $core) . ' ' . $cmd;
370
        }
371
372
        return $this
373
            ->getMachine()
374
            ->getConnection()
375
            ->exec($cmd)
376
        ;
377
    }
378
379
    public function getStartParams()
380
    {
381
        $params = array();
382
        $params[] = 'create_default_virtualserver=0';
383
384
        if ($this->firstStart) {
385
            $password = \TeamSpeak3_Helper_String::factory($this->queryPassword)->escape();
0 ignored issues
show
Unused Code introduced by
$password 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...
386
387
            $params[] = 'serveradmin_password=' . $this->queryPassword;
388
        }
389
390
        return '"' . implode(' ', $params) . '"' . (($this->firstStart) ? ' &' : '');
391
    }
392
393
    /** {@inheritdoc} */
394
    public function getAbsoluteDir()
395
    {
396
        return rtrim($this->getMachine()->getHome(), '/') . '/' . trim($this->dir, '/');
397
    }
398
399
    /** {@inheritdoc} */
400
    public function getName()
401
    {
402
        return strval($this);
403
    }
404
405
    public function getFullName()
406
    {
407
        return $this->getName();
408
    }
409
410
    /** {@inheritdoc} */
411
    public function getType()
412
    {
413
        return 'teamspeak';
414
    }
415
416
    public function __toString()
417
    {
418
        return strval($this->getMachine());
419
    }
420
421
    public static function loadValidatorMetadata(ClassMetadata $metadata)
422
    {
423
        $metadata->addPropertyConstraint('voicePort', new Assert\NotBlank(array('message' => 'teamspeak_server.assert.voice_port.empty')));
424
        $metadata->addPropertyConstraint('voicePort', new Assert\Range(array(
425
            'min' => 1024,
426
            'minMessage' => 'teamspeak_server.assert.voice_port.min',
427
            'max' => 65536,
428
            'maxMessage' => 'teamspeak_server.assert.voice_port.max',
429
        )));
430
        $metadata->addPropertyConstraint('queryPort', new Assert\NotBlank(array('message' => 'teamspeak_server.assert.query_port.empty')));
431
        $metadata->addPropertyConstraint('queryPort', new Assert\Range(array(
432
            'min' => 1024,
433
            'minMessage' => 'teamspeak_server.assert.query_port.min',
434
            'max' => 65536,
435
            'maxMessage' => 'teamspeak_server.assert.query_port.max',
436
        )));
437
        $metadata->addPropertyConstraint('queryPassword', new Assert\NotBlank(array('message' => 'teamspeak_server.assert.query_password.empty')));
438
        $metadata->addPropertyConstraint('filetransferPort', new Assert\NotBlank(array('message' => 'teamspeak_server.assert.filetransfer_port.empty')));
439
        $metadata->addPropertyConstraint('filetransferPort', new Assert\Range(array(
440
            'min' => 1024,
441
            'minMessage' => 'teamspeak_server.assert.filetransfer_port.min',
442
            'max' => 65536,
443
            'maxMessage' => 'teamspeak_server.assert.filetransfer_port.max',
444
        )));
445
        $metadata->addPropertyConstraint('licenceFile', new Assert\File(array(
446
            'maxSize' => 200,
447
            'maxSizeMessage' => 'teamspeak_server.assert.licence_file.max_size',
448
        )));
449
    }
450
}
451