Passed
Push — develop ( 5ea638...6ae8a1 )
by Nikolay
04:34
created

System::networkReload()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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 DateTime;
23
use DateTimeZone;
24
use MikoPBX\Common\Models\CustomFiles;
25
use MikoPBX\Common\Models\PbxSettings;
26
use MikoPBX\Common\Models\PbxSettingsConstants;
27
use MikoPBX\Core\Asterisk\Configs\H323Conf;
28
use MikoPBX\Core\Asterisk\Configs\HepConf;
29
use MikoPBX\Core\System\Configs\CronConf;
30
use MikoPBX\Core\System\Configs\IptablesConf;
31
use MikoPBX\Core\System\Configs\PHPConf;
32
use MikoPBX\Core\System\Configs\NTPConf;
33
use MikoPBX\Core\Asterisk\Configs\QueueConf;
34
use Phalcon\Di;
35
36
37
/**
38
 * Class System
39
 *
40
 * This class provides various system-level functionalities.
41
 *
42
 * @package MikoPBX\Core\System
43
 * @property \Phalcon\Config config
44
 */
45
class System extends Di\Injectable
46
{
47
    private MikoPBXConfig $mikoPBXConfig;
48
49
    /**
50
     * System constructor - Instantiates MikoPBXConfig.
51
     */
52
    public function __construct()
53
    {
54
        $this->mikoPBXConfig = new MikoPBXConfig();
55
    }
56
57
    /**
58
     * Is the configuration default?
59
     * @return bool
60
     */
61
    public function isDefaultConf():bool
62
    {
63
        $di = Di::getDefault();
64
        if ($di === null) {
65
            return false;
66
        }
67
        $sqlite3 = Util::which('sqlite3');
68
        $md5sum = Util::which('md5sum');
69
        $busybox = Util::which('busybox');
70
        $md5_1 = shell_exec("$sqlite3 ".$di->getConfig()->path('database.dbfile')." .dump | $md5sum | $busybox cut -f 1 -d ' '");
71
        $md5_2 = shell_exec("$sqlite3 /conf.default/mikopbx.db .dump | $md5sum | $busybox cut -f 1 -d ' '");
72
        return $md5_1 === $md5_2;
73
    }
74
75
    /**
76
     * Trying to restore the backup
77
     * @return void
78
     */
79
    public function tryRestoreConf():void
80
    {
81
        $di = Di::getDefault();
82
        if ($di === null) {
83
            return;
84
        }
85
        $storage = new Storage();
86
        $storages = $storage->getStorageCandidate();
87
        $tmpMountDir = '/tmp/mnt';
88
        $backupDir   = str_replace(['/storage/usbdisk1','/mountpoint'],['',''],$di->getConfig()->path('core.confBackupDir'));
89
        $confFile    = $di->getConfig()->path('database.dbfile');
90
        foreach ($storages as $dev => $fs){
91
            Util::teletypeEcho("    - mount $dev ..."."\n");
92
            Util::mwMkdir($tmpMountDir."/$dev");
93
            $res = Storage::mountDisk($dev, $fs, $tmpMountDir."/$dev");
94
            if(!$res){
95
                Util::teletypeEcho("    - fail mount $dev ..."."\n");
96
            }
97
        }
98
        $pathBusybox = Util::which('busybox');
99
        $pathFind    = Util::which('find');
100
        $pathMount   = Util::which('umount');
101
        $pathRm    = Util::which('rm');
102
        $pathGzip    = Util::which('gzip');
103
        $pathSqlite3    = Util::which('sqlite3');
104
        $lastBackUp  = trim(shell_exec("$pathFind $tmpMountDir/dev/*$backupDir -type f -printf '%T@ %p\\n' | $pathBusybox sort -n | $pathBusybox tail -1 | $pathBusybox cut -f2- -d' '"));
105
        if(empty($lastBackUp)){
106
            return;
107
        }
108
        Util::teletypeEcho("    - Restore $lastBackUp ..."."\n");
109
        shell_exec("$pathRm -rf {$confFile}*");
110
        shell_exec("$pathGzip -c -d $lastBackUp | sqlite3 $confFile");
111
        Processes::mwExec("$pathSqlite3 $confFile 'select * from m_Storage'", $out, $ret);
112
        if($ret !== 0){
113
            Util::teletypeEcho("    - fail restore $lastBackUp ..."."\n");
114
            copy('/conf.default/mikopbx.db', $confFile);
115
        }elseif(!$this->isDefaultConf()){
116
            self::rebootSync();
117
        }
118
        foreach ($storages as $dev => $fs){
119
            shell_exec("$pathMount $dev");
120
        }
121
    }
122
123
    /**
124
     * Returns the directory where logs are stored.
125
     *
126
     * @return string - Directory path where logs are stored.
127
     */
128
    public static function getLogDir(): string
129
    {
130
        $di = Di::getDefault();
131
        if ($di !== null) {
132
            return $di->getConfig()->path('core.logsDir');
133
        }
134
135
        // Default logs directory
136
        return '/var/log';
137
    }
138
139
    /**
140
     * Updates custom changes in config files
141
     *
142
     * @return void
143
     */
144
    public static function updateCustomFiles():void
145
    {
146
        $actions = [];
147
148
        // Find all custom files marked as changed
149
        /** @var CustomFiles $res_data */
150
        $res_data = CustomFiles::find("changed = '1'");
151
152
        // Process each changed file
153
        foreach ($res_data as $file_data) {
154
            // Always restart asterisk after any custom file change
155
            $actions['asterisk_core_reload'] = 100;
156
            $filename                        = basename($file_data->filepath);
157
158
            // Process based on file name
159
            switch ($filename) {
160
                // Set actions based on the name of the changed file
161
                case 'manager.conf':
162
                    $actions['manager'] = 10;
163
                    break;
164
                case 'musiconhold.conf':
165
                    $actions['musiconhold'] = 100;
166
                    break;
167
                case 'modules.conf':
168
                    $actions['modules'] = 10;
169
                    break;
170
                case 'http.conf':
171
                    $actions['manager'] = 10; //
172
                    break;
173
                case 'hep.conf':
174
                    $actions['hep'] = 10; //
175
                    break;
176
                case 'root': // crontabs
177
                    $actions['cron'] = 10;
178
                    break;
179
                case 'queues.conf':
180
                    $actions['queues'] = 10;
181
                    break;
182
                case 'features.conf':
183
                    $actions['features'] = 10;
184
                    break;
185
                case 'ntp.conf':
186
                    $actions['ntp'] = 100;
187
                    break;
188
                case 'ooh323.conf':
189
                    $actions['h323'] = 100;
190
                    break;
191
                case 'rtp.conf':
192
                    $actions['rtp'] = 10;
193
                    break;
194
                case 'static-routes':
195
                case 'openvpn.ovpn':
196
                    $actions['network'] = 100;
197
                    break;
198
                case 'firewall_additional':
199
                case 'jail.local':
200
                    $actions['firewall'] = 100;
201
                    break;
202
                default:
203
                    break;
204
            }
205
        }
206
207
        // Sort actions and invoke them
208
        asort($actions);
209
        self::invokeActions($actions);
210
211
        // After actions are invoked, reset the changed status and save the file data
212
        foreach ($res_data as $file_data) {
213
            /** @var CustomFiles $file_data */
214
            $file_data->writeAttribute("changed", '0');
215
            $file_data->save();
216
        }
217
    }
218
219
    /**
220
     * Restart modules or services based on the provided actions.
221
     *
222
     * @param array $actions - The actions to be performed.
223
     *
224
     * @return void
225
     */
226
    public static function invokeActions(array $actions): void
227
    {
228
229
        // Process each action
230
        foreach ($actions as $action => $value) {
231
            // Restart modules or services based on action
232
            switch ($action) {
233
                case 'manager':
234
                    PBX::managerReload();
235
                    break;
236
                case 'musiconhold':
237
                    PBX::musicOnHoldReload();
238
                    break;
239
                case 'rtp':
240
                    PBX::rtpReload();
241
                    break;
242
                case 'modules':
243
                    PBX::modulesReload();
244
                    break;
245
                case 'cron':
246
                    $cron = new CronConf();
247
                    $cron->reStart();
248
                    break;
249
                case 'queues':
250
                    QueueConf::queueReload();
251
                    break;
252
                case 'features':
253
                    PBX::managerReload(); //
254
                    break;
255
                case 'ntp':
256
                    NTPConf::configure();
257
                    break;
258
                case 'firewall':
259
                    IptablesConf::reloadFirewall();
260
                    break;
261
                case 'hep':
262
                    HepConf::reload();
263
                    break;
264
                case 'h323':
265
                    H323Conf::reload();
266
                    break;
267
                case 'network':
268
                    self::networkReload();
0 ignored issues
show
Bug introduced by
The method networkReload() does not exist on MikoPBX\Core\System\System. ( Ignorable by Annotation )

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

268
                    self::/** @scrutinizer ignore-call */ 
269
                          networkReload();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
269
                    break;
270
                case 'asterisk_core_reload':
271
                    PBX::sipReload();
272
                    PBX::iaxReload();
273
                    PBX::dialplanReload();
274
                    PBX::coreReload();
275
                    break;
276
                default:
277
            }
278
        }
279
    }
280
281
    /**
282
     * Sets the system date and time based on timestamp and timezone.
283
     *
284
     * @param int    $timeStamp - Unix timestamp.
285
     * @param string $remote_tz - Timezone string.
286
     *
287
     * @return bool
288
     * @throws \Exception
289
     */
290
    public static function setDate(int $timeStamp, string $remote_tz): bool
291
    {
292
        $datePath = Util::which('date');
293
294
        // Fetch timezone from database
295
        $db_tz = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
296
        $origin_tz = '';
297
298
        // Read existing timezone from file if it exists
299
        if (file_exists('/etc/TZ')) {
300
            $origin_tz = file_get_contents("/etc/TZ");
301
        }
302
303
        // If the timezones are different, configure the timezone
304
        if ($origin_tz !== $db_tz){
305
            self::timezoneConfigure();
306
        }
307
308
        // Calculate the time offset and set the system time
309
        $origin_tz = $db_tz;
310
        $origin_dtz = new DateTimeZone($origin_tz);
311
        $remote_dtz = new DateTimeZone($remote_tz);
312
        $origin_dt  = new DateTime('now', $origin_dtz);
313
        $remote_dt  = new DateTime('now', $remote_dtz);
314
        $offset     = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
315
        $timeStamp  = $timeStamp - $offset;
316
317
        // Execute date command to set system time
318
        Processes::mwExec("{$datePath} +%s -s @{$timeStamp}");
319
320
        return true;
321
    }
322
323
    /**
324
     * Reboots the system after calling system_reboot_cleanup()
325
     *
326
     * @return void
327
     */
328
    public static function rebootSync(): void
329
    {
330
        $pbx_rebootPath = Util::which('pbx_reboot');
331
        Processes::mwExec("{$pbx_rebootPath} > /dev/null 2>&1");
332
    }
333
334
    /**
335
     * Reboots the system after calling system_reboot_cleanup()
336
     */
337
    public static function rebootSyncBg(): void
338
    {
339
        $pbx_rebootPath = Util::which('pbx_reboot');
340
        Processes::mwExecBg("{$pbx_rebootPath} > /dev/null 2>&1");
341
    }
342
343
    /**
344
     * Shutdown the system.
345
     */
346
    public static function shutdown(): void
347
    {
348
        $shutdownPath = Util::which('shutdown');
349
        Processes::mwExec("{$shutdownPath} > /dev/null 2>&1");
350
    }
351
352
    /**
353
     * Configures the system timezone according to the PBXTimezone setting.
354
     *
355
     * @return void
356
     */
357
    public static function timezoneConfigure(): void
358
    {
359
360
        // Get the timezone setting from the database
361
        $timezone = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
362
363
        // If /etc/TZ or /etc/localtime exist, delete them
364
        if (file_exists('/etc/TZ')) {
365
            unlink("/etc/TZ");
366
        }
367
        if (file_exists('/etc/localtime')) {
368
            unlink("/etc/localtime");
369
        }
370
371
        // If a timezone is set, configure it
372
        if ($timezone) {
373
374
            // The path to the zone file
375
            $zone_file = "/usr/share/zoneinfo/{$timezone}";
376
377
            // If the zone file exists, copy it to /etc/localtime
378
            if ( ! file_exists($zone_file)) {
379
                return;
380
            }
381
            $cpPath = Util::which('cp');
382
            Processes::mwExec("{$cpPath}  {$zone_file} /etc/localtime");
383
384
            // Write the timezone to /etc/TZ and set the TZ environment variable
385
            file_put_contents('/etc/TZ', $timezone);
386
            putenv("TZ={$timezone}");
387
388
            // Execute the export TZ command and configure PHP's timezone
389
            Processes::mwExec("export TZ;");
390
            PHPConf::phpTimeZoneConfigure();
391
        }
392
393
    }
394
395
    /**
396
     * Calculate the hash of SSL certificates and extract them from ca-certificates.crt.
397
     *
398
     * @return void
399
     */
400
    public static function sslRehash(): void
401
    {
402
        // Paths to system commands
403
        $openSslPath = Util::which('openssl');
404
        $cutPath     = Util::which('cut');
405
406
        // Get OpenSSL directory and cert file
407
        $openSslDir  = trim(shell_exec("$openSslPath version -d | $cutPath -d '\"' -f 2"));
408
        $certFile    = "$openSslDir/certs/ca-certificates.crt";
409
        $tmpFile     = tempnam('/tmp', 'cert-');
410
        $rawData     = file_get_contents($certFile);
411
        $certs       = explode(PHP_EOL.PHP_EOL, $rawData);
412
        foreach ($certs as $cert){
413
            if(strpos($cert, '-----BEGIN CERTIFICATE-----') === false){
414
                continue;
415
            }
416
            file_put_contents($tmpFile, $cert);
417
            $hash = trim(shell_exec("$openSslPath x509 -subject_hash -noout -in '$tmpFile'"));
418
            rename($tmpFile, "$openSslDir/certs/$hash.0");
419
        }
420
        if(file_exists($tmpFile)){
421
            unlink($tmpFile);
422
        }
423
    }
424
}
425