Passed
Push — develop ( 368e3d...115c7b )
by Nikolay
12:16
created

System::isDefaultConf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 12
rs 9.9666
cc 2
nc 2
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\PbxSettings;
25
use MikoPBX\Common\Models\PbxSettingsConstants;
26
use MikoPBX\Core\System\Configs\PHPConf;
27
use MikoPBX\Core\Workers\WorkerModelsEvents;
28
use Phalcon\Di;
29
30
31
/**
32
 * Class System
33
 *
34
 * This class provides various system-level functionalities.
35
 *
36
 * @package MikoPBX\Core\System
37
 * @property \Phalcon\Config config
38
 */
39
class System extends Di\Injectable
40
{
41
    private MikoPBXConfig $mikoPBXConfig;
42
43
    /**
44
     * System constructor - Instantiates MikoPBXConfig.
45
     */
46
    public function __construct()
47
    {
48
        $this->mikoPBXConfig = new MikoPBXConfig();
49
    }
50
51
    /**
52
     * Is the configuration default?
53
     * @return bool
54
     */
55
    public function isDefaultConf():bool
56
    {
57
        $di = Di::getDefault();
58
        if ($di === null) {
59
            return false;
60
        }
61
        $sqlite3 = Util::which('sqlite3');
62
        $md5sum = Util::which('md5sum');
63
        $busybox = Util::which('busybox');
64
        $md5_1 = shell_exec("$sqlite3 ".$di->getConfig()->path('database.dbfile')." .dump | $md5sum | $busybox cut -f 1 -d ' '");
65
        $md5_2 = shell_exec("$sqlite3 /conf.default/mikopbx.db .dump | $md5sum | $busybox cut -f 1 -d ' '");
66
        return $md5_1 === $md5_2;
67
    }
68
69
    /**
70
     * Trying to restore the backup
71
     * @return void
72
     */
73
    public function tryRestoreConf():void
74
    {
75
        $di = Di::getDefault();
76
        if ($di === null) {
77
            return;
78
        }
79
        $storage = new Storage();
80
        $storages = $storage->getStorageCandidate();
81
        $tmpMountDir = '/tmp/mnt';
82
        $backupDir   = str_replace(['/storage/usbdisk1','/mountpoint'],['',''],$di->getConfig()->path('core.confBackupDir'));
83
        $confFile    = $di->getConfig()->path('database.dbfile');
84
        foreach ($storages as $dev => $fs){
85
            SystemMessages::echoToTeletype("    - mount $dev ..."."\n");
86
            Util::mwMkdir($tmpMountDir."/$dev");
87
            $res = Storage::mountDisk($dev, $fs, $tmpMountDir."/$dev");
88
            if(!$res){
89
                SystemMessages::echoToTeletype("    - fail mount $dev ..."."\n");
90
            }
91
        }
92
        $pathBusybox = Util::which('busybox');
93
        $pathFind    = Util::which('find');
94
        $pathMount   = Util::which('umount');
95
        $pathRm    = Util::which('rm');
96
        $pathGzip    = Util::which('gzip');
97
        $pathSqlite3    = Util::which('sqlite3');
98
        $lastBackUp  = trim(shell_exec("$pathFind $tmpMountDir/dev/*$backupDir -type f -printf '%T@ %p\\n' | $pathBusybox sort -n | $pathBusybox tail -1 | $pathBusybox cut -f2- -d' '"));
99
        if(empty($lastBackUp)){
100
            return;
101
        }
102
        SystemMessages::echoToTeletype("    - Restore $lastBackUp ..."."\n");
103
        shell_exec("$pathRm -rf {$confFile}*");
104
        shell_exec("$pathGzip -c -d $lastBackUp | sqlite3 $confFile");
105
        Processes::mwExec("$pathSqlite3 $confFile 'select * from m_Storage'", $out, $ret);
106
        if($ret !== 0){
107
            SystemMessages::echoToTeletype("    - fail restore $lastBackUp ..."."\n");
108
            copy('/conf.default/mikopbx.db', $confFile);
109
        }elseif(!$this->isDefaultConf()){
110
            self::rebootSync();
111
        }
112
        foreach ($storages as $dev => $fs){
113
            shell_exec("$pathMount $dev");
114
        }
115
    }
116
117
    /**
118
     * Returns the directory where logs are stored.
119
     * @depricated use Directories::getDir(Directories::CORE_LOGS_DIR);
120
     *
121
     * @return string - Directory path where logs are stored.
122
     */
123
    public static function getLogDir(): string
124
    {
125
        return Directories::getDir(Directories::CORE_LOGS_DIR);
126
    }
127
128
    /**
129
     * Refreshes networks configs and restarts network daemon.
130
     *
131
     * @return void
132
     */
133
    public static function networkReload(): void
134
    {
135
        // Create Network object and configure settings
136
        $network = new Network();
137
        $network->hostnameConfigure();
138
        $network->resolvConfGenerate();
139
        $network->loConfigure();
140
        $network->lanConfigure();
141
        $network->configureLanInDocker();
142
        $network->updateExternalIp();
143
    }
144
145
    /**
146
     * Restart modules or services based on the provided actions.
147
     * @depricated use WorkerModelsEvents::invokeAction($actions);
148
     *
149
     * @param array $actions - The actions to be performed.
150
     *
151
     * @return void
152
     */
153
    public static function invokeActions(array $actions): void
154
    {
155
        // Process each action
156
        foreach ($actions as $action => $value) {
157
            // Restart modules or services based on action
158
            switch ($action) {
159
                case 'manager':
160
                    WorkerModelsEvents::invokeAction(WorkerModelsEvents::R_MANAGERS);
161
                    break;
162
                case 'cron':
163
                    WorkerModelsEvents::invokeAction(WorkerModelsEvents::R_CRON);
164
                    break;
165
                default:
166
            }
167
        }
168
    }
169
170
    /**
171
     * Sets the system date and time based on timestamp and timezone.
172
     *
173
     * @param int    $timeStamp - Unix timestamp.
174
     * @param string $remote_tz - Timezone string.
175
     *
176
     * @return bool
177
     * @throws \Exception
178
     */
179
    public static function setDate(int $timeStamp, string $remote_tz): bool
180
    {
181
        $datePath = Util::which('date');
182
183
        // Fetch timezone from database
184
        $db_tz = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
185
        $origin_tz = '';
186
187
        // Read existing timezone from file if it exists
188
        if (file_exists('/etc/TZ')) {
189
            $origin_tz = file_get_contents("/etc/TZ");
190
        }
191
192
        // If the timezones are different, configure the timezone
193
        if ($origin_tz !== $db_tz){
194
            self::timezoneConfigure();
195
        }
196
197
        // Calculate the time offset and set the system time
198
        $origin_tz = $db_tz;
199
        $origin_dtz = new DateTimeZone($origin_tz);
200
        $remote_dtz = new DateTimeZone($remote_tz);
201
        $origin_dt  = new DateTime('now', $origin_dtz);
202
        $remote_dt  = new DateTime('now', $remote_dtz);
203
        $offset     = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
204
        $timeStamp  = $timeStamp - $offset;
205
206
        // Execute date command to set system time
207
        Processes::mwExec("{$datePath} +%s -s @{$timeStamp}");
208
209
        return true;
210
    }
211
212
    /**
213
     * Reboots the system after calling system_reboot_cleanup()
214
     *
215
     * @return void
216
     */
217
    public static function rebootSync(): void
218
    {
219
        $pbx_rebootPath = Util::which('pbx_reboot');
220
        Processes::mwExec("{$pbx_rebootPath} > /dev/null 2>&1");
221
    }
222
223
    /**
224
     * Reboots the system after calling system_reboot_cleanup()
225
     */
226
    public static function rebootSyncBg(): void
227
    {
228
        $pbx_rebootPath = Util::which('pbx_reboot');
229
        Processes::mwExecBg("{$pbx_rebootPath} > /dev/null 2>&1");
230
    }
231
232
    /**
233
     * Shutdown the system.
234
     */
235
    public static function shutdown(): void
236
    {
237
        $shutdownPath = Util::which('shutdown');
238
        Processes::mwExec("{$shutdownPath} > /dev/null 2>&1");
239
    }
240
241
    /**
242
     * Configures the system timezone according to the PBXTimezone setting.
243
     *
244
     * @return void
245
     */
246
    public static function timezoneConfigure(): void
247
    {
248
249
        // Get the timezone setting from the database
250
        $timezone = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
251
252
        // If /etc/TZ or /etc/localtime exist, delete them
253
        if (file_exists('/etc/TZ')) {
254
            unlink("/etc/TZ");
255
        }
256
        if (file_exists('/etc/localtime')) {
257
            unlink("/etc/localtime");
258
        }
259
260
        // If a timezone is set, configure it
261
        if ($timezone) {
262
263
            // The path to the zone file
264
            $zone_file = "/usr/share/zoneinfo/{$timezone}";
265
266
            // If the zone file exists, copy it to /etc/localtime
267
            if ( ! file_exists($zone_file)) {
268
                return;
269
            }
270
            $cpPath = Util::which('cp');
271
            Processes::mwExec("{$cpPath}  {$zone_file} /etc/localtime");
272
273
            // Write the timezone to /etc/TZ and set the TZ environment variable
274
            file_put_contents('/etc/TZ', $timezone);
275
            putenv("TZ={$timezone}");
276
277
            // Execute the export TZ command and configure PHP's timezone
278
            Processes::mwExec("export TZ;");
279
            PHPConf::phpTimeZoneConfigure();
280
        }
281
282
    }
283
284
    /**
285
     * Calculate the hash of SSL certificates and extract them from ca-certificates.crt.
286
     *
287
     * @return void
288
     */
289
    public static function sslRehash(): void
290
    {
291
        // Paths to system commands
292
        $openSslPath = Util::which('openssl');
293
        $cutPath     = Util::which('cut');
294
295
        // Get OpenSSL directory and cert file
296
        $openSslDir  = trim(shell_exec("$openSslPath version -d | $cutPath -d '\"' -f 2"));
297
        $certFile    = "$openSslDir/certs/ca-certificates.crt";
298
        $tmpFile     = tempnam('/tmp', 'cert-');
299
        $rawData     = file_get_contents($certFile);
300
        $certs       = explode(PHP_EOL.PHP_EOL, $rawData);
301
        foreach ($certs as $cert){
302
            if(strpos($cert, '-----BEGIN CERTIFICATE-----') === false){
303
                continue;
304
            }
305
            file_put_contents($tmpFile, $cert);
306
            $hash = trim(shell_exec("$openSslPath x509 -subject_hash -noout -in '$tmpFile'"));
307
            rename($tmpFile, "$openSslDir/certs/$hash.0");
308
        }
309
        if(file_exists($tmpFile)){
310
            unlink($tmpFile);
311
        }
312
    }
313
}
314