Passed
Push — develop ( 9a9917...0313a3 )
by Портнов
04:57
created

restoreDefaultSettings()   B

Complexity

Conditions 8
Paths 25

Size

Total Lines 81
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 58
c 3
b 0
f 0
dl 0
loc 81
rs 7.6719
cc 8
nc 25
nop 0

How to fix   Long Method   

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\PBXCoreREST\Lib;
21
22
use MikoPBX\Common\Models\AsteriskManagerUsers;
23
use MikoPBX\Common\Models\CallQueueMembers;
24
use MikoPBX\Common\Models\CallQueues;
25
use MikoPBX\Common\Models\CustomFiles;
26
use MikoPBX\Common\Models\ExtensionForwardingRights;
27
use MikoPBX\Common\Models\Extensions;
28
use MikoPBX\Common\Models\FirewallRules;
29
use MikoPBX\Common\Models\Iax;
30
use MikoPBX\Common\Models\IncomingRoutingTable;
31
use MikoPBX\Common\Models\IvrMenu;
32
use MikoPBX\Common\Models\IvrMenuActions;
33
use MikoPBX\Common\Models\NetworkFilters;
34
use MikoPBX\Common\Models\OutgoingRoutingTable;
35
use MikoPBX\Common\Models\OutWorkTimes;
36
use MikoPBX\Common\Models\PbxExtensionModules;
37
use MikoPBX\Common\Models\PbxSettings;
38
use MikoPBX\Common\Models\Sip;
39
use MikoPBX\Common\Models\SoundFiles;
40
use MikoPBX\Common\Models\Users;
41
use MikoPBX\Common\Providers\PBXConfModulesProvider;
42
use MikoPBX\Core\Asterisk\CdrDb;
43
use MikoPBX\Core\System\Notifications;
44
use MikoPBX\Core\System\PBX;
45
use MikoPBX\Core\System\Processes;
46
use MikoPBX\Core\System\Storage;
47
use MikoPBX\Core\System\System;
48
use MikoPBX\Core\System\Upgrade\UpdateDatabase;
49
use MikoPBX\Core\System\Util;
50
use MikoPBX\Modules\PbxExtensionState;
51
use MikoPBX\Modules\PbxExtensionUtils;
52
use MikoPBX\Modules\Setup\PbxExtensionSetupFailure;
53
use MikoPBX\PBXCoreREST\Workers\WorkerModuleInstaller;
54
use Phalcon\Di;
55
use Phalcon\Di\Injectable;
56
57
58
/**
59
 * Class SystemManagementProcessor
60
 *
61
 * @package MikoPBX\PBXCoreREST\Lib
62
 *
63
 */
64
class SystemManagementProcessor extends Injectable
65
{
66
    /**
67
     * Processes System requests
68
     *
69
     * @param array $request
70
     *
71
     * @return PBXApiResult An object containing the result of the API call.
72
     *
73
     * @throws \Exception
74
     */
75
    public static function callBack(array $request): PBXApiResult
76
    {
77
        $action         = $request['action'];
78
        $data           = $request['data'];
79
        $res            = new PBXApiResult();
80
        $res->processor = __METHOD__;
81
        switch ($action) {
82
            case 'reboot':
83
                System::rebootSync();
84
                $res->success = true;
85
                break;
86
            case 'shutdown':
87
                System::shutdown();
88
                $res->success = true;
89
                break;
90
            case 'getDate':
91
                $res->success           = true;
92
                $res->data['timestamp'] = time();
93
                break;
94
            case 'setDate':
95
                $res->success = System::setDate($data['timestamp'], $data['userTimeZone']);
96
                break;
97
            case 'updateMailSettings':
98
                $notifier     = new Notifications();
99
                $res->success = $notifier->sendTestMail();
100
                break;
101
            case 'sendMail':
102
                $res = self::sendMail($data);
103
                break;
104
            case 'unBanIp':
105
                $res = FirewallManagementProcessor::fail2banUnbanAll($data['ip']);
106
                break;
107
            case 'getBanIp':
108
                $res = FirewallManagementProcessor::getBanIp();
109
                break;
110
            case 'upgrade':
111
                $res = self::upgradeFromImg($data['temp_filename']);
112
                break;
113
            case 'installNewModule':
114
                $filePath = $request['data']['filePath'];
115
                $res      = self::installModule($filePath);
116
                break;
117
            case 'statusOfModuleInstallation':
118
                $filePath = $request['data']['filePath'];
119
                $res      = self::statusOfModuleInstallation($filePath);
120
                break;
121
            case 'enableModule':
122
                $moduleUniqueID = $request['data']['uniqid'];
123
                $res            = self::enableModule($moduleUniqueID);
124
                break;
125
            case 'disableModule':
126
                $moduleUniqueID = $request['data']['uniqid'];
127
                $res            = self::disableModule($moduleUniqueID);
128
                break;
129
            case 'uninstallModule':
130
                $moduleUniqueID = $request['data']['uniqid'];
131
                $keepSettings   = $request['data']['keepSettings'] === 'true';
132
                $res            = self::uninstallModule($moduleUniqueID, $keepSettings);
133
                break;
134
            case 'restoreDefault':
135
                $ch = 0;
136
                do{
137
                    $ch++;
138
                    $res = self::restoreDefaultSettings();
139
                    sleep(1);
140
                }while($ch <= 10 && !$res->success);
141
                break;
142
            case 'convertAudioFile':
143
                $mvPath = Util::which('mv');
144
                Processes::mwExec("{$mvPath} {$request['data']['temp_filename']} {$request['data']['filename']}");
145
                $res = self::convertAudioFile($request['data']['filename']);
146
                break;
147
            default:
148
                $res             = new PBXApiResult();
149
                $res->processor  = __METHOD__;
150
                $res->messages[] = "Unknown action - {$action} in systemCallBack";
151
        }
152
153
        $res->function = $action;
154
155
        return $res;
156
    }
157
158
    /**
159
     * Sends an email notification.
160
     *
161
     * @param array $data The data containing email, subject, and body.
162
     *
163
     * @return PBXApiResult An object containing the result of the API call.
164
     */
165
    private static function sendMail(array $data): PBXApiResult
166
    {
167
        $res            = new PBXApiResult();
168
        $res->processor = __METHOD__;
169
        if (isset($data['email']) && isset($data['subject']) && isset($data['body'])) {
170
            if (isset($data['encode']) && $data['encode'] === 'base64') {
171
                $data['subject'] = base64_decode($data['subject']);
172
                $data['body']    = base64_decode($data['body']);
173
            }
174
            $notifier = new Notifications();
175
            $result   = $notifier->sendMail($data['email'], $data['subject'], $data['body']);
176
            if ($result === true) {
177
                $res->success = true;
178
            } else {
179
                $res->success    = false;
180
                $res->messages[] = 'Notifications::sendMail method returned false';
181
            }
182
        } else {
183
            $res->success    = false;
184
            $res->messages[] = 'Not all query parameters were set';
185
        }
186
187
        return $res;
188
    }
189
190
    /**
191
     * Upgrade the system from an image file.
192
     *
193
     * @param string $tempFilename The path to the temporary image file.
194
     *
195
     * @return PBXApiResult An object containing the result of the API call.
196
     */
197
    public static function upgradeFromImg(string $tempFilename): PBXApiResult
198
    {
199
        $res                  = new PBXApiResult();
200
        $res->processor       = __METHOD__;
201
        $res->success         = true;
202
        $res->data['message'] = 'In progress...';
203
204
        if ( ! file_exists($tempFilename)) {
205
            $res->success    = false;
206
            $res->messages[] = "Update file '{$tempFilename}' not found.";
207
208
            return $res;
209
        }
210
211
        if ( ! file_exists('/var/etc/cfdevice')) {
212
            $res->success    = false;
213
            $res->messages[] = "The system is not installed";
214
215
            return $res;
216
        }
217
        $dev     = trim(file_get_contents('/var/etc/cfdevice'));
218
        $storage = new Storage();
219
220
        // Generate update script
221
        $cmd = '/bin/busybox grep "$(/bin/busybox  cat /var/etc/storage_device) " < /etc/fstab | /bin/busybox awk -F"[= ]" "{ print \$2}"';
222
        $storage_uuid = trim(shell_exec($cmd));
223
        $cf_uuid      = $storage->getUuid("{$dev}3");
224
        $data = "#!/bin/sh".PHP_EOL.
225
                'rm -rf "$0";'.PHP_EOL.
226
                "export storage_uuid='$storage_uuid';".PHP_EOL.
227
                "export cf_uuid='$cf_uuid';".PHP_EOL.
228
                "export updateFile='$tempFilename';".PHP_EOL;
229
230
        // Mount boot partition
231
        $cmd = '/bin/lsblk -o UUID,PKNAME -p | /bin/busybox grep "'.$cf_uuid.'" | /bin/busybox cut -f 2 -d " "';
232
        $bootDisc = trim(shell_exec($cmd));
233
234
        $systemDir = '/system';
235
        Util::mwMkdir($systemDir);
236
        $result = Processes::mwExec("mount {$bootDisc}1 $systemDir");
237
        if($result === 0){
238
            file_put_contents("$systemDir/update.sh", $data);
239
            // Reboot the system
240
            System::rebootSyncBg();
241
        }else{
242
            $res->success    = false;
243
            $res->messages[] = "Fail mount boot device...";
244
        }
245
246
        return $res;
247
    }
248
249
    /**
250
     * Install a new additional extension module.
251
     *
252
     * @param string $filePath The path to the module file.
253
     *
254
     * @return PBXApiResult An object containing the result of the API call.
255
     */
256
    public static function installModule($filePath): PBXApiResult
257
    {
258
        $res            = new PBXApiResult();
259
        $res->processor = __METHOD__;
260
        $resModuleMetadata = FilesManagementProcessor::getMetadataFromModuleFile($filePath);
261
        if ( ! $resModuleMetadata->success) {
262
            return $resModuleMetadata;
263
        }
264
265
        $moduleUniqueID = $resModuleMetadata->data['uniqid'];
266
        // Disable the module if it's enabled
267
        if (PbxExtensionUtils::isEnabled($moduleUniqueID)){
268
            $res = self::disableModule($moduleUniqueID);
269
            if (!$res->success){
270
                return $res;
271
            }
272
        }
273
274
        $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID);
275
        $needBackup       = is_dir($currentModuleDir);
276
277
        if ($needBackup) {
278
            self::uninstallModule($moduleUniqueID, true);
279
        }
280
281
        // Start the background process to install the module
282
        $temp_dir            = dirname($filePath);
283
284
        // Create a progress file to track the installation progress
285
        file_put_contents( $temp_dir . '/installation_progress', '0');
286
287
        // Create an error file to store any installation errors
288
        file_put_contents( $temp_dir . '/installation_error', '');
289
290
        $install_settings = [
291
            'filePath' => $filePath,
292
            'currentModuleDir' => $currentModuleDir,
293
            'uniqid' => $moduleUniqueID,
294
        ];
295
296
        // Save the installation settings to a JSON file
297
        $settings_file  = "{$temp_dir}/install_settings.json";
298
        file_put_contents(
299
            $settings_file,
300
            json_encode($install_settings, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
301
        );
302
        $phpPath               = Util::which('php');
303
        $workerFilesMergerPath = Util::getFilePathByClassName(WorkerModuleInstaller::class);
304
305
        // Execute the background process to install the module
306
        Processes::mwExecBg("{$phpPath} -f {$workerFilesMergerPath} start '{$settings_file}'");
307
        $res->data['filePath']= $filePath;
308
        $res->success = true;
309
310
        return $res;
311
    }
312
313
314
    /**
315
     * Uninstall a module.
316
     *
317
     * @param string $moduleUniqueID The unique ID of the module to uninstall.
318
     * @param bool   $keepSettings   Indicates whether to keep the module settings.
319
     *
320
     * @return PBXApiResult An object containing the result of the API call.
321
     */
322
    public static function uninstallModule(string $moduleUniqueID, bool $keepSettings): PBXApiResult
323
    {
324
        $res              = new PBXApiResult();
325
        $res->processor   = __METHOD__;
326
        $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID);
327
328
        // Kill all module processes
329
        if (is_dir("{$currentModuleDir}/bin")) {
330
            $busyboxPath = Util::which('busybox');
331
            $killPath    = Util::which('kill');
332
            $lsofPath    = Util::which('lsof');
333
            $grepPath    = Util::which('grep');
334
            $awkPath     = Util::which('awk');
335
            $uniqPath    = Util::which('uniq');
336
337
            // Execute the command to kill all processes related to the module
338
            Processes::mwExec(
339
                "{$busyboxPath} {$killPath} -9 $({$lsofPath} {$currentModuleDir}/bin/* |  {$busyboxPath} {$grepPath} -v COMMAND | {$busyboxPath} {$awkPath}  '{ print $2}' | {$busyboxPath} {$uniqPath})"
340
            );
341
        }
342
343
        // Uninstall module with keep settings and backup db
344
        $moduleClass = "\\Modules\\{$moduleUniqueID}\\Setup\\PbxExtensionSetup";
345
346
        try {
347
            if (class_exists($moduleClass)
348
                && method_exists($moduleClass, 'uninstallModule')) {
349
                // Instantiate the module setup class and call the uninstallModule method
350
                $setup = new $moduleClass($moduleUniqueID);
351
            } else {
352
353
                // Use a fallback class to uninstall the module from the database if it doesn't exist on disk
354
                $moduleClass = PbxExtensionSetupFailure::class;
355
                $setup       = new $moduleClass($moduleUniqueID);
356
            }
357
            $setup->uninstallModule($keepSettings);
358
        } finally {
359
            if (is_dir($currentModuleDir)) {
360
                // If the module directory still exists, force uninstallation
361
                $rmPath = Util::which('rm');
362
363
                // Remove the module directory recursively
364
                Processes::mwExec("{$rmPath} -rf {$currentModuleDir}");
365
366
                // Use the fallback class to unregister the module from the database
367
                $moduleClass = PbxExtensionSetupFailure::class;
368
                $setup       = new $moduleClass($moduleUniqueID);
369
                $setup->unregisterModule();
370
            }
371
        }
372
        $res->success = true;
373
374
        return $res;
375
    }
376
377
    /**
378
     * Enables extension module
379
     *
380
     * @param string $moduleUniqueID
381
     *
382
     * @return PBXApiResult An object containing the result of the API call.
383
     */
384
    private static function enableModule(string $moduleUniqueID): PBXApiResult
385
    {
386
        $res                  = new PBXApiResult();
387
        $res->processor       = __METHOD__;
388
        $moduleStateProcessor = new PbxExtensionState($moduleUniqueID);
389
        if ($moduleStateProcessor->enableModule() === false) {
390
            $res->success  = false;
391
            $res->messages = $moduleStateProcessor->getMessages();
392
        } else {
393
            PBXConfModulesProvider::recreateModulesProvider();
394
            $res->data    = $moduleStateProcessor->getMessages();
395
            $res->success = true;
396
        }
397
398
        return $res;
399
    }
400
401
    /**
402
     * Disables extension module
403
     *
404
     * @param string $moduleUniqueID
405
     *
406
     * @return PBXApiResult An object containing the result of the API call.
407
     */
408
    private static function disableModule(string $moduleUniqueID): PBXApiResult
409
    {
410
        $res                  = new PBXApiResult();
411
        $res->processor       = __METHOD__;
412
        $moduleStateProcessor = new PbxExtensionState($moduleUniqueID);
413
        if ($moduleStateProcessor->disableModule() === false) {
414
            $res->success  = false;
415
            $res->messages = $moduleStateProcessor->getMessages();
416
        } else {
417
            PBXConfModulesProvider::recreateModulesProvider();
418
            $res->data    = $moduleStateProcessor->getMessages();
419
            $res->success = true;
420
        }
421
422
        return $res;
423
    }
424
425
    /**
426
     * Perform pre-cleaning operations on specific columns of certain models.
427
     *
428
     * @param PBXApiResult $res The result object to store any error messages.
429
     *
430
     * @return void
431
     */
432
    public static function preCleaning(PBXApiResult &$res):void
433
    {
434
        $preCleaning = [
435
            CallQueues::class => [
436
                'redirect_to_extension_if_empty',
437
                'redirect_to_extension_if_unanswered',
438
                'redirect_to_extension_if_repeat_exceeded'
439
            ],
440
            IvrMenu::class => [
441
                'timeout_extension'
442
            ]
443
        ];
444
        foreach ($preCleaning as $class => $columns) {
445
            $records = call_user_func([$class, 'find']);
446
            foreach ($records as $record){
447
                foreach ($columns as $column){
448
                    $record->$column = '';
449
                }
450
                if ( ! $record->save()) {
451
                    $res->messages[] = $record->getMessages();
452
                    $res->success    = false;
453
                }
454
            }
455
        }
456
    }
457
458
    /**
459
     * Perform cleaning operations on main tables.
460
     *
461
     * @param PBXApiResult $res The result object to store any error messages.
462
     *
463
     * @return void
464
     */
465
    public static function cleaningMainTables(&$res):void
466
    {
467
        // Define the models and conditions for cleaning
468
        $clearThisModels = [
469
            [ExtensionForwardingRights::class => ''],
470
            [OutWorkTimes::class => ''],
471
            [IvrMenuActions::class => ''],
472
            [CallQueueMembers::class => ''],
473
            [OutgoingRoutingTable::class => ''],
474
            [IncomingRoutingTable::class => 'id>1'],
475
            [Sip::class => ''], // All SIP providers
476
            [Iax::class => ''], // All IAX providers
477
            [AsteriskManagerUsers::class => ''],
478
            [Extensions::class => 'type="' . Extensions::TYPE_IVR_MENU . '"'],  // IVR Menu
479
            [Extensions::class => 'type="' . Extensions::TYPE_CONFERENCE . '"'],  // CONFERENCE
480
            [Extensions::class => 'type="' . Extensions::TYPE_QUEUE . '"'],  // QUEUE
481
            [Users::class => 'id>"1"'], // All except root with their extensions
482
            [CustomFiles::class => ''],
483
            [NetworkFilters::class=>'permit!="0.0.0.0/0" AND deny!="0.0.0.0/0"'] //Delete all other rules
484
        ];
485
486
        // Iterate over each model and perform deletion based on conditions
487
        foreach ($clearThisModels as $modelParams) {
488
            foreach ($modelParams as $key => $value) {
489
                $records = call_user_func([$key, 'find'], $value);
490
                if (!$records->delete()) {
491
                    // If deletion fails, add error messages to the result object
492
                    $res->messages[] = $records->getMessages();
493
                    $res->success    = false;
494
                }
495
            }
496
        }
497
        // Allow all connections for 0.0.0.0/0 in firewall rules
498
        $firewallRules = FirewallRules::find();
499
        foreach ($firewallRules as $firewallRule){
500
            $firewallRule->action = 'allow';
501
            $firewallRule->save();
502
        }
503
    }
504
505
    /**
506
     * Perform cleaning operations on other extensions.
507
     *
508
     * @param PBXApiResult $res The result object to store any error messages.
509
     *
510
     * @return void
511
     */
512
    public static function cleaningOtherExtensions(&$res):void
513
    {
514
        // Define the parameters for querying the extensions to delete
515
        $parameters     = [
516
            'conditions' => 'not number IN ({ids:array})',
517
            'bind'       => [
518
                'ids' => [
519
                    '000063',   // Reads back the extension
520
                    '000064',   // 0000MILLI
521
                    '10003246', // Echo test
522
                    'hangup',   // System Extension
523
                    'busy',     // System Extension
524
                    'did2user', // System Extension
525
                    'voicemail',// System Extension
526
                ],
527
            ],
528
        ];
529
        $stopDeleting   = false;
530
        $countRecords   = Extensions::count($parameters);
531
        $deleteAttempts = 0;
532
        while ($stopDeleting === false) {
533
            $record = Extensions::findFirst($parameters);
534
            if ($record === null) {
535
                $stopDeleting = true;
536
                continue;
537
            }
538
            if ( ! $record->delete()) {
539
                $deleteAttempts += 1;
540
            }
541
            if ($deleteAttempts > $countRecords * 10) {
542
                $stopDeleting    = true; // Prevent loop
543
                $res->messages[] = $record->getMessages();
544
            }
545
        }
546
    }
547
548
    /**
549
     * Clean up custom sound files.
550
     *
551
     * @param PBXApiResult $res The result object to store any error messages.
552
     *
553
     * @return void
554
     */
555
    public static function cleaningSoundFiles(&$res):void
556
    {
557
        $rm     = Util::which('rm');
558
        $parameters = [
559
            'conditions' => 'category = :custom:',
560
            'bind'       => [
561
                'custom' => SoundFiles::CATEGORY_CUSTOM,
562
            ],
563
        ];
564
        $records    = SoundFiles::find($parameters);
565
        foreach ($records as $record) {
566
            if (stripos($record->path, '/storage/usbdisk1/mikopbx') !== false) {
567
                Processes::mwExec("{$rm} -rf {$record->path}");
568
                if ( ! $record->delete()) {
569
                    $res->messages[] = $record->getMessages();
570
                    $res->success    = false;
571
                }
572
            }
573
        }
574
    }
575
576
    /**
577
     * Deleting Backups
578
     * @return void
579
     */
580
    public static function cleaningBackups():void
581
    {
582
        $di = Di::getDefault();
583
        $dir = $di->getShared('config')->path('core.mediaMountPoint').'/mikopbx/backup';
584
        if(file_exists($dir)){
585
            $chAttr     = Util::which('chattr');
586
            Processes::mwExec("$chAttr -i -R $dir");
587
            $rm     = Util::which('rm');
588
            Processes::mwExec("$rm -rf $dir/*");
589
        }
590
    }
591
592
    /**
593
     * Restore the default settings of the PBX.
594
     *
595
     * @return PBXApiResult An object containing the result of the API call.
596
     */
597
    public static function restoreDefaultSettings(): PBXApiResult
598
    {
599
        $res            = new PBXApiResult();
600
        $res->processor = __METHOD__;
601
        $di             = DI::getDefault();
602
        if ($di === null) {
603
            $res->messages[] = 'Error on DI initialize';
604
            return $res;
605
        }
606
        $rm     = Util::which('rm');
607
        $res->success = true;
608
609
        // Change incoming rule to default action
610
        IncomingRoutingTable::resetDefaultRoute();
611
        self::preCleaning($res);
612
613
        self::cleaningMainTables($res);
614
        self::cleaningOtherExtensions($res);
615
        self::cleaningSoundFiles($res);
616
        self::cleaningBackups();
617
618
        // PbxExtensions
619
        $records = PbxExtensionModules::find();
620
        foreach ($records as $record) {
621
            $moduleDir = PbxExtensionUtils::getModuleDir($record->uniqid);
622
            Processes::mwExec("{$rm} -rf {$moduleDir}");
623
            if ( ! $record->delete()) {
624
                $res->messages[] = $record->getMessages();
625
                $res->success    = false;
626
            }
627
        }
628
629
        // Fill PBXSettingsByDefault
630
        $defaultValues = PbxSettings::getDefaultArrayValues();
631
        $fixedKeys = [
632
            'Name',
633
            'Description',
634
            'SSHPassword',
635
            'SSHRsaKey',
636
            'SSHDssKey',
637
            'SSHAuthorizedKeys',
638
            'SSHecdsaKey',
639
            'SSHLanguage',
640
            'WEBHTTPSPublicKey',
641
            'WEBHTTPSPrivateKey',
642
            'RedirectToHttps',
643
            'PBXLanguage',
644
            'PBXVersion',
645
            'WebAdminLogin',
646
            'WebAdminPassword',
647
            'WebAdminLanguage',
648
        ];
649
        foreach ($defaultValues as $key=>$defaultValue){
650
            if (in_array($key, $fixedKeys, true)){
651
                continue;
652
            }
653
            $record = PbxSettings::findFirstByKey($key);
654
            if ($record===null){
655
                $record = new PbxSettings();
656
                $record->key = $key;
657
            }
658
            $record->value = $defaultValue;
659
        }
660
661
        // Delete CallRecords from database
662
        $cdr = CdrDb::getPathToDB();
663
        Processes::mwExec("{$rm} -rf {$cdr}*");
664
        $dbUpdater = new UpdateDatabase();
665
        $dbUpdater->updateDatabaseStructure();
666
667
        // Delete CallRecords sound files
668
        $callRecordsPath = $di->getShared('config')->path('asterisk.monitordir');
669
        if (stripos($callRecordsPath, '/storage/usbdisk1/mikopbx') !== false) {
670
            Processes::mwExec("{$rm} -rf {$callRecordsPath}/*");
671
        }
672
673
        // Restart PBX
674
        $pbxConsole = Util::which('pbx-console');
675
        shell_exec("$pbxConsole services restart-all");
676
        PBX::coreRestart();
677
        return $res;
678
    }
679
680
    /**
681
     * Convert an audio file to different formats.
682
     *
683
     * @param string $filename The path of the audio file to be converted.
684
     * @return PBXApiResult An object containing the result of the API call.
685
     */
686
    public static function convertAudioFile($filename): PBXApiResult
687
    {
688
        $res            = new PBXApiResult();
689
        $res->processor = __METHOD__;
690
        if ( ! file_exists($filename)) {
691
            $res->success    = false;
692
            $res->messages[] = "File '{$filename}' not found.";
693
694
            return $res;
695
        }
696
        $out          = [];
697
        $tmp_filename = '/tmp/' . time() . "_" . basename($filename);
698
        if (false === copy($filename, $tmp_filename)) {
699
            $res->success    = false;
700
            $res->messages[] = "Unable to create temporary file '{$tmp_filename}'.";
701
702
            return $res;
703
        }
704
705
        // Change extension to wav
706
        $trimmedFileName = Util::trimExtensionForFile($filename);
707
        $n_filename     = $trimmedFileName . ".wav";
708
        $n_filename_mp3 = $trimmedFileName . ".mp3";
709
710
        // Convert file to wav format
711
        $tmp_filename = escapeshellcmd($tmp_filename);
712
        $n_filename   = escapeshellcmd($n_filename);
713
        $soxPath      = Util::which('sox');
714
        Processes::mwExec("{$soxPath} -v 0.99 -G '{$tmp_filename}' -c 1 -r 8000 -b 16 '{$n_filename}'", $out);
715
        $result_str = implode('', $out);
716
717
        // Convert wav file to mp3 format
718
        $lamePath = Util::which('lame');
719
        Processes::mwExec("{$lamePath} -b 32 --silent '{$n_filename}' '{$n_filename_mp3}'", $out);
720
        $result_mp3 = implode('', $out);
721
722
        // Convert the file to various codecs using Asterisk
723
        $codecs = ['alaw', 'ulaw', 'gsm', 'g722', 'wav'];
724
        $rmPath       = Util::which('rm');
725
        $asteriskPath = Util::which('asterisk');
726
        foreach ($codecs as $codec){
727
            $result = shell_exec("$asteriskPath -rx 'file convert $tmp_filename $trimmedFileName.$codec'");
728
            if(strpos($result, 'Converted') !== 0){
729
                shell_exec("$rmPath -rf /root/test.{$codec}");
730
            }
731
        }
732
733
        // Remove temporary file
734
        unlink($tmp_filename);
735
        if ($result_str !== '' && $result_mp3 !== '') {
736
            // Conversion failed
737
            $res->success    = false;
738
            $res->messages[] = $result_str;
739
740
            return $res;
741
        }
742
743
        if (file_exists($filename)
744
            && $filename !== $n_filename
745
            && $filename !== $n_filename_mp3) {
746
            // Remove the original file if it's different from the converted files
747
            unlink($filename);
748
        }
749
750
        $res->success = true;
751
        $res->data[]  = $n_filename_mp3;
752
753
        return $res;
754
    }
755
756
    /**
757
     * Check the status of a module installation.
758
     *
759
     * @param string $filePath The path of the module installation file.
760
     * @return PBXApiResult An object containing the result of the API call.
761
     */
762
    public static function statusOfModuleInstallation(string $filePath): PBXApiResult
763
    {
764
        $res            = new PBXApiResult();
765
        $res->processor = __METHOD__;
766
        $di             = Di::getDefault();
767
        if ($di === null) {
768
            $res->messages[] = 'Dependency injector does not initialized';
769
770
            return $res;
771
        }
772
        $temp_dir            = dirname($filePath);
773
        $progress_file = $temp_dir . '/installation_progress';
774
        $error_file = $temp_dir . '/installation_error';
775
        if (!file_exists($error_file)|| !file_exists($progress_file)){
776
            $res->success                   = false;
777
            $res->data['i_status']          = 'PROGRESS_FILE_NOT_FOUND';
778
            $res->data['i_status_progress'] = '0';
779
        }
780
        elseif (file_get_contents($error_file)!=='') {
781
            $res->success                   = false;
782
            $res->data['i_status']          = 'INSTALLATION_ERROR';
783
            $res->data['i_status_progress'] = '0';
784
            $res->messages[]                = file_get_contents($error_file);
785
        } elseif ('100' === file_get_contents($progress_file)) {
786
            $res->success                   = true;
787
            $res->data['i_status_progress'] = '100';
788
            $res->data['i_status']          = 'INSTALLATION_COMPLETE';
789
        } else {
790
            $res->success                   = true;
791
            $res->data['i_status']          = 'INSTALLATION_IN_PROGRESS';
792
            $res->data['i_status_progress'] = file_get_contents($progress_file);
793
        }
794
795
        return $res;
796
    }
797
}