Passed
Push — develop ( 2f1a30...4a45f7 )
by Портнов
04:35
created

System::setDate()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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