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
|
|
|
} |