Passed
Push — develop ( db74f4...3f7c1e )
by Nikolay
04:30
created

SystemLoader::getInfoMessage()   F

Complexity

Conditions 18
Paths 216

Size

Total Lines 84
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 49
c 0
b 0
f 0
dl 0
loc 84
rs 3.8333
cc 18
nc 216
nop 0

How to fix   Long Method    Complexity   

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
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use MikoPBX\Common\Models\LanInterfaces;
23
use MikoPBX\Common\Models\PbxSettings;
24
use MikoPBX\Common\Models\PbxSettingsConstants;
25
use MikoPBX\Common\Providers\RegistryProvider;
26
use MikoPBX\Core\Asterisk\Configs\SIPConf;
27
use MikoPBX\Core\System\Configs\ACPIDConf;
28
use MikoPBX\Core\System\Configs\BeanstalkConf;
29
use MikoPBX\Core\System\Configs\CronConf;
30
use MikoPBX\Core\System\Configs\IptablesConf;
31
use MikoPBX\Core\System\Configs\NatsConf;
32
use MikoPBX\Core\System\Configs\NginxConf;
33
use MikoPBX\Core\System\Configs\NTPConf;
34
use MikoPBX\Core\System\Configs\PHPConf;
35
use MikoPBX\Core\System\Configs\RedisConf;
36
use MikoPBX\Core\System\Configs\SentryConf;
37
use MikoPBX\Core\System\Configs\SSHConf;
38
use MikoPBX\Core\System\Configs\SyslogConf;
39
use MikoPBX\Core\System\Configs\VmToolsConf;
40
use MikoPBX\Core\System\Upgrade\UpdateDatabase;
41
use MikoPBX\Core\System\Upgrade\UpdateSystemConfig;
42
use Phalcon\Di;
43
44
/**
45
 * SystemLoader class
46
 *
47
 * This class is responsible for loading the system services.
48
 *
49
 * @package MikoPBX\Core\System
50
 * @property \Phalcon\Config config
51
 */
52
53
class SystemLoader extends Di\Injectable
54
{
55
    /**
56
     * Message displayed during the start of a stage.
57
     *
58
     * @var string
59
     */
60
    private string $stageMessage = '';
61
62
    /**
63
     * Check if the system is running in Docker
64
     *
65
     * @var bool
66
     */
67
    private bool $isDocker = false;
68
69
70
    /**
71
     * Check if the system is running from live cd
72
     *
73
     * @var bool
74
     */
75
    private bool $isRecoveryMode = false;
76
77
78
    /**
79
     * Constructor
80
     */
81
    public function __construct()
82
    {
83
        $this->isDocker = Util::isDocker();
84
        $this->isRecoveryMode = Util::isRecoveryMode();
85
    }
86
87
88
    /**
89
     * Echoes the starting message for a stage.
90
     *
91
     * @param string $message The message to echo.
92
     */
93
    private function echoStartMsg(string $message):void
94
    {
95
        $this->stageMessage = $message;
96
        SystemMessages::echoToTeletype($this->stageMessage);
97
        SystemMessages::echoWithSyslog($this->stageMessage);
98
    }
99
100
    /**
101
     * Echoes the result message for a stage.
102
     *
103
     * @param string $result The result of the stage.
104
     */
105
    private function echoResultMsg(string $result = SystemMessages::RESULT_DONE):void
106
    {
107
        SystemMessages::teletypeEchoResult($this->stageMessage, $result);
108
        SystemMessages::echoResult($this->stageMessage, $result);
109
        $this->stageMessage = '';
110
    }
111
112
    /**
113
     * Starts the system services.
114
     *
115
     * @return bool True on success, false otherwise.
116
     */
117
    public function startSystem(): bool
118
    {
119
        $system = new System();
120
        // Is the configuration default?
121
        // Try restore config...
122
        if($system->isDefaultConf() && !$this->isRecoveryMode){
123
            $this->echoStartMsg(' - Try restore backup of settings... ');
124
            $system->tryRestoreConf();
125
            $this->echoResultMsg();
126
        }
127
128
        // Check if the system is running on T2SDELinux
129
        $itIsT2SDELinux = Util::isT2SdeLinux();
130
131
        // Mark the registry as booting
132
        $this->di->getShared(RegistryProvider::SERVICE_NAME)->booting = true;
133
134
        // Start the ACPID daemon
135
        $this->echoStartMsg(PHP_EOL);
136
        $this->echoStartMsg(' - Start acpid daemon...');
137
        $ACPIDConf = new ACPIDConf();
138
        $ACPIDConf->reStart();
139
        $this->echoResultMsg();
140
141
        // Start the Beanstalkd daemon
142
        $this->echoStartMsg(' - Start beanstalkd daemon...');
143
        $beanstalkConf = new BeanstalkConf();
144
        $beanstalkConf->reStart();
145
        $this->echoResultMsg();
146
147
        // Start the Redis daemon
148
        $this->echoStartMsg(' - Start redis daemon...');
149
        $redisConf = new RedisConf();
150
        $redisConf->reStart();
151
        $this->echoResultMsg();
152
153
        // Configure Sentry error logger
154
        $this->echoStartMsg(' - Configuring sentry error logger ...');
155
        if (!$this->isRecoveryMode){
156
            $sentryConf = new SentryConf();
157
            $sentryConf->configure();
158
            $this->echoResultMsg();
159
        } else {
160
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
161
        }
162
163
        // Configure the system timezone
164
        $this->echoStartMsg(' - Configuring timezone...');
165
        if (!$this->isRecoveryMode){
166
            System::timezoneConfigure();
167
            $this->echoResultMsg();
168
        } else {
169
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
170
        }
171
172
        // Mount the storage disk
173
        $storage       = new Storage();
174
        if($itIsT2SDELinux){
175
            // Do not need to set on Docker or Debian linux
176
            $storage->saveFstab();
177
        }
178
        $storage->configure();
179
        $this->echoStartMsg(' - Mount storage disk...');
180
        $this->echoResultMsg();
181
182
        // Additional tasks for T2SDELinux
183
        if($itIsT2SDELinux) {
184
            $this->echoStartMsg(' - Connect swap...');
185
            Processes::mwExecBg('/etc/rc/connect_swap');
186
            $this->echoResultMsg();
187
        }
188
189
        // Start the syslogd daemon
190
        $this->echoStartMsg(' - Start syslogd daemon...');
191
        $syslogConf = new SyslogConf();
192
        $syslogConf->reStart();
193
        $this->echoResultMsg();
194
195
        // Update the database structure
196
        $dbUpdater = new UpdateDatabase();
197
        $dbUpdater->updateDatabaseStructure();
198
199
        // Create directories required by modules after DB upgrade
200
        $this->echoStartMsg(' - Create modules links and folders...');
201
        $storage->createWorkDirsAfterDBUpgrade();
202
        $this->echoResultMsg();
203
204
        // Update the system configuration and applications
205
        $this->echoStartMsg(' - Update configs and applications...'.PHP_EOL);
206
        $confUpdate = new UpdateSystemConfig();
207
        $confUpdate->updateConfigs();
208
        $this->echoStartMsg(' - Update configs...');
209
        $this->echoResultMsg();
210
211
        // Configure VM tools
212
        $this->echoStartMsg(' - Configuring VM tools...');
213
        if (!$this->isRecoveryMode){
214
            $vmwareTools    = new VmToolsConf();
215
            $resultVMTools  = $vmwareTools->configure();
216
            $this->echoResultMsg((string)$resultVMTools);
217
        } else {
218
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
219
        }
220
221
        // Configure the system hostname
222
        $this->echoStartMsg(' - Configuring hostname...');
223
        $network = new Network();
224
        $network->hostnameConfigure();
225
        $this->echoResultMsg();
226
227
        // Generate resolv.conf
228
        $this->echoStartMsg(' - Configuring resolv.conf...');
229
        $network->resolvConfGenerate();
230
        $this->echoResultMsg();
231
232
        // Configure LAN interface
233
        $this->echoStartMsg(' - Configuring LAN interface...');
234
        if ($this->isDocker){
235
            $network->configureLanInDocker();
236
        } else {
237
            $network->lanConfigure();
238
        }
239
        $this->echoResultMsg();
240
241
        // SSL rehash
242
        $this->echoStartMsg(' - SSL rehash...');
243
        System::sslRehash();
244
        $this->echoResultMsg();
245
246
        // Configure the firewall
247
        $this->echoStartMsg(' - Configuring Firewall...');
248
        $firewall = new IptablesConf();
249
        $firewall->applyConfig();
250
        $this->echoResultMsg();
251
252
        // Configure NTP
253
        $this->echoStartMsg(' - Configuring ntpd...');
254
        if (!$this->isRecoveryMode){
255
            NTPConf::configure();
256
            $this->echoResultMsg();
257
        } else {
258
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
259
        }
260
261
        // Do not need to set Debian SSH service
262
        if(!Util::isSystemctl()){
263
            $this->echoStartMsg(' - Configuring SSH console...');
264
            $sshConf = new SSHConf();
265
            $resSsh  = $sshConf->configure();
266
            $this->echoResultMsg((string)$resSsh);
267
        }
268
269
        // Start cloud provisioning
270
        if (!$this->isDocker && !$this->isRecoveryMode) {
271
            $this->echoStartMsg(' - Attempt to cloud provisioning...'.PHP_EOL);
272
            CloudProvisioning::start();
273
        } else {
274
            $this->echoStartMsg(' - Attempt to cloud provisioning...');
275
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
276
        }
277
278
        // Connect storage in a cloud if needed
279
        $this->echoStartMsg(' - Auto connect storage for a cloud ...');
280
        if (!$this->isDocker && !$this->isRecoveryMode) {
281
            $connectResult = Storage::connectStorageInCloud();
282
            $this->echoResultMsg($connectResult);
283
        } else {
284
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
285
        }
286
287
        // Update external IP if needed
288
        if (!$this->isRecoveryMode) {
289
            $this->echoStartMsg(' - Update external IP...');
290
            $network->updateExternalIp();
291
            $this->echoResultMsg();
292
        } else {
293
            $this->echoResultMsg(SystemMessages::RESULT_SKIPPED);
294
        }
295
        $this->di->getShared(RegistryProvider::SERVICE_NAME)->booting = false;
296
297
        return true;
298
    }
299
300
    /**
301
     * Load Asterisk and Web interface
302
     *
303
     * @return bool
304
     */
305
    public function startMikoPBX(): bool
306
    {
307
        $this->di->getShared(RegistryProvider::SERVICE_NAME)->booting = true;
308
309
        // Start the NATS queue daemon
310
        $this->echoStartMsg(' - Start nats queue daemon...');
311
        $natsConf = new NatsConf();
312
        $natsConf->reStart();
313
        $this->echoResultMsg();
314
315
        // Start the PHP-FPM daemon
316
        $this->echoStartMsg(' - Start php-fpm daemon...');
317
        PHPConf::reStart();
318
        $this->echoResultMsg();
319
320
        // Configure Asterisk and start it
321
        $this->echoStartMsg(' - Configuring Asterisk...'.PHP_EOL);
322
        $pbx = new PBX();
323
        $pbx->configure();
324
325
        $this->echoStartMsg(' - Start Asterisk...');
326
        $pbx->start();
327
        $this->echoResultMsg();
328
329
        // Wait for Asterisk to fully boot and reload SIP settings
330
        $this->echoStartMsg(' - Wait asterisk fully booted...');
331
        $asteriskResult = PBX::waitFullyBooted();
332
        $this->echoResultMsg((string)$asteriskResult);
333
        if($asteriskResult){
334
            $this->echoStartMsg(' - Reload SIP settings in AstDB...');
335
            $sip = new SIPConf();
336
            $sip->updateAsteriskDatabase();
337
            $this->echoResultMsg();
338
        }
339
340
        // Configure and restart cron tasks
341
        $this->echoStartMsg(' - Configuring Cron tasks...');
342
        $cron = new CronConf();
343
        $cron->reStart();
344
        $this->echoResultMsg();
345
346
        // Start the Nginx daemon
347
        $this->echoStartMsg(' - Start Nginx daemon...');
348
        $nginx = new NginxConf();
349
        $nginx->generateConf();
350
        $nginx->reStart();
351
        $this->echoResultMsg();
352
353
        // Log that all services are fully loaded if Asterisk is fully booted
354
        if($asteriskResult){
355
            $this->echoStartMsg("┌────────────────────────────────────────────────┐".PHP_EOL);
356
            $this->echoStartMsg("│                                                │".PHP_EOL);
357
            $this->echoStartMsg("│  🌟 MikoPBX - All services are fully loaded 🌟 │".PHP_EOL);
358
            $this->echoStartMsg("│                                                │".PHP_EOL);
359
            $this->echoStartMsg("├────────────────────────────────────────────────┤");
360
        }
361
362
        // Display network information
363
        $this->echoStartMsg(self::getInfoMessage());
364
365
        $this->di->getShared(RegistryProvider::SERVICE_NAME)->booting = false;
366
367
        return true;
368
    }
369
370
    /**
371
     * Retrieves the information message containing available web interface addresses.
372
     *
373
     * @return string The information message.
374
     */
375
    public static function getInfoMessage(): string
376
    {
377
        $addresses = [
378
            'local' => [],
379
            'external' => []
380
        ];
381
        /** @var LanInterfaces $interface */
382
        $interfaces = LanInterfaces::find("disabled='0'");
383
        foreach ($interfaces as $interface) {
384
            if (!empty($interface->ipaddr)) {
385
                $addresses['local'][] = $interface->ipaddr;
386
            }
387
            if (!empty($interface->exthostname) && !in_array($interface->exthostname, $addresses['local'], true)) {
388
                $addresses['external'][] = explode(':', $interface->exthostname)[0] ?? '';
389
            }
390
            if (!empty($interface->extipaddr) && !in_array($interface->extipaddr, $addresses['local'], true)) {
391
                $addresses['external'][] = explode(':', $interface->extipaddr)[0] ?? '';
392
            }
393
        }
394
        unset($interfaces);
395
396
        // Assuming a total width of 50 characters for each line
397
        $lineWidth = 53;
398
        $addressSpace = $lineWidth - 7 - 5; // 7 for "│    ➜ " and 5 for " │" at the end
399
400
        // Local network
401
        $port = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_HTTPS_PORT);
402
        $info = PHP_EOL . "│                                                │";
403
        $info .= PHP_EOL . "│           🌐 Web Interface Access 🌐           │";
404
        $info .= PHP_EOL . "│                                                │";
405
        $info .= PHP_EOL . "│    Local Network Address:                      │";
406
        foreach ($addresses['local'] as $address) {
407
            if (empty($address)) {
408
                continue;
409
            }
410
            $formattedAddress = $port === '443' ? "https://$address" : "https://$address:$port";
411
412
            // Ensure the address fits within the designated space, truncating if necessary
413
            $formattedAddress = strlen($formattedAddress) > $addressSpace ? substr($formattedAddress, 0, $addressSpace - 3) . '...' : $formattedAddress;
414
415
            // Use sprintf to format the string with padding to ensure constant length
416
            $info .= PHP_EOL . sprintf("│    ➜ %-{$addressSpace}s │", $formattedAddress);
417
418
        }
419
        $info .= PHP_EOL."│                                                │";
420
421
        // External web address info
422
        if (!empty($addresses['external'])) {
423
            $info .= PHP_EOL . "│    External Network Address:                   │";
424
            foreach ($addresses['external'] as $address) {
425
                if (empty($address)) {
426
                    continue;
427
                }
428
                $formattedAddress = $port === '443' ? "https://$address" : "https://$address:$port";
429
430
                // Ensure the address fits within the designated space, truncating if necessary
431
                $formattedAddress = strlen($formattedAddress) > $addressSpace ? substr($formattedAddress, 0, $addressSpace - 3) . '...' : $formattedAddress;
432
433
                // Use sprintf to format the string with padding to ensure constant length
434
                $info .= PHP_EOL . sprintf("│    ➜ %-{$addressSpace}s │", $formattedAddress);
435
436
            }
437
            $info .= PHP_EOL . "│                                                │";
438
        }
439
440
        // Default web user info
441
        $cloudInstanceId = PbxSettings::getValueByKey(PbxSettingsConstants::CLOUD_INSTANCE_ID);
442
        $webAdminPassword = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_ADMIN_PASSWORD);
443
        $defaultPassword = PbxSettings::getDefaultArrayValues(PbxSettingsConstants::WEB_ADMIN_PASSWORD);
0 ignored issues
show
Unused Code introduced by
The call to MikoPBX\Common\Models\Pb...getDefaultArrayValues() has too many arguments starting with MikoPBX\Common\Models\Pb...nts::WEB_ADMIN_PASSWORD. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

443
        /** @scrutinizer ignore-call */ 
444
        $defaultPassword = PbxSettings::getDefaultArrayValues(PbxSettingsConstants::WEB_ADMIN_PASSWORD);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
444
        if ($cloudInstanceId === $webAdminPassword || $webAdminPassword===$defaultPassword) {
445
            $adminUser = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_ADMIN_LOGIN);
446
            $info .= PHP_EOL . "│    🔑 Default Credentials:                     │";
447
            // Login
448
            $loginSpace = $lineWidth - 12 - 5; // 7 for "│    Login: " and 5 for " │" at the end
449
            $loginLine = sprintf("│    Login: %-{$loginSpace}s │", $adminUser); // Format the login line
450
            $info .= PHP_EOL . $loginLine;
451
452
            // Password
453
            $passwordSpace = $lineWidth - 15 - 5; // 7 for "│    Password: " and 5 for " │" at the end
454
            $passwordLine = sprintf("│    Password: %-{$passwordSpace}s │", $cloudInstanceId); // Format the password line
455
            $info .= PHP_EOL . $passwordLine;
456
        }
457
        $info .= PHP_EOL . "└────────────────────────────────────────────────┘".PHP_EOL .PHP_EOL;
458
        return $info;
459
    }
460
}