Passed
Push — develop ( fe499f...dd242c )
by Nikolay
05:43 queued 12s
created

FilesManagementProcessor::fileReadContent()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
dl 0
loc 18
rs 9.8333
c 1
b 0
f 0
cc 4
nc 3
nop 2
1
<?php
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 7 2020
7
 */
8
9
namespace MikoPBX\PBXCoreREST\Lib;
10
11
use MikoPBX\Common\Models\CustomFiles;
12
use MikoPBX\Core\System\Util;
13
use MikoPBX\Modules\PbxExtensionUtils;
14
use MikoPBX\Modules\Setup\PbxExtensionSetupFailure;
15
use MikoPBX\PBXCoreREST\Workers\WorkerDownloader;
16
use MikoPBX\PBXCoreREST\Workers\WorkerMergeUploadedFile;
17
use Phalcon\Di;
18
use Phalcon\Di\Injectable;
19
use Phalcon\Http\Message\StreamFactory;
20
use Phalcon\Http\Message\UploadedFile;
21
22
class FilesManagementProcessor extends Injectable
23
{
24
    /**
25
     * Process resumable upload files
26
     *
27
     * @param $parameters
28
     *
29
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
30
     */
31
    public static function uploadResumable($parameters): PBXApiResult
32
    {
33
        $res = new PBXApiResult();
34
        $res->processor = __METHOD__;
35
        $di             = Di::getDefault();
36
        if ($di === null) {
37
            $res->success = false;
38
            $res->messages[]='Dependency injector not initialized';
39
            return $res;
40
        }
41
        $upload_id            = $parameters['upload_id'];
42
        $resumableFilename    = $parameters['resumableFilename'];
43
        $resumableIdentifier  = $parameters['resumableIdentifier'];
44
        $resumableChunkNumber = $parameters['resumableChunkNumber'];
45
        $resumableTotalSize   = $parameters['resumableTotalSize'];
46
        $uploadPath           = $di->getShared('config')->path('core.uploadPath');
47
48
        $factory = new StreamFactory();
49
50
        foreach ($parameters['files'] as $file_data) {
51
            $stream = $factory->createStreamFromFile($file_data['file_path'], 'r');
52
            $file   = new UploadedFile(
53
                $stream,
54
                $file_data['file_size'],
55
                $file_data['file_error'],
56
                $file_data['file_name'],
57
                $file_data['file_type']
58
            );
59
60
            if (isset($resumableIdentifier) && trim($resumableIdentifier) !== '') {
61
                $temp_dir         = $uploadPath . '/' . Util::trimExtensionForFile(basename($resumableFilename));
62
                $temp_dst_file    = $uploadPath . '/' . $upload_id . '/' . $upload_id . '_' . basename(
63
                        $resumableFilename
64
                    );
65
                $chunks_dest_file = $temp_dir . '/' . $resumableFilename . '.part' . $resumableChunkNumber;
66
            } else {
67
                $temp_dir         = $uploadPath . '/' . $upload_id;
68
                $temp_dst_file    = $temp_dir . '/' . $upload_id . '_' . basename($file->getClientFilename());
69
                $chunks_dest_file = $temp_dst_file;
70
            }
71
            if ( ! Util::mwMkdir($temp_dir) || ! Util::mwMkdir(dirname($temp_dst_file))) {
72
                Util::sysLogMsg('UploadFile', "Error create dir '$temp_dir'");
73
                $res->success = false;
74
                $res->messages[] ="Error create dir '{$temp_dir}'";
75
                return $res;
76
            }
77
            $file->moveTo($chunks_dest_file);
78
            // if (file_exists($file->))
79
            if ($resumableFilename) {
80
                $res->success = true;
81
                // Передача файлов частями.
82
                $result = Util::createFileFromChunks($temp_dir, $resumableTotalSize);
83
                if ($result === true) {
84
                    $merge_settings = [
85
                        'data'   => [
86
                            'result_file'          => $temp_dst_file,
87
                            'temp_dir'             => $temp_dir,
88
                            'resumableFilename'    => $resumableFilename,
89
                            'resumableTotalChunks' => $resumableChunkNumber,
90
                        ],
91
                        'action' => 'merge',
92
                    ];
93
                    $settings_file  = "{$temp_dir}/merge_settings";
94
                    file_put_contents(
95
                        $settings_file,
96
                        json_encode($merge_settings, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
97
                    );
98
99
                    // Отправляем задачу на склеивание файла.
100
                    $phpPath               = Util::which('php');
101
                    $workerFilesMergerPath = Util::getFilePathByClassName(WorkerMergeUploadedFile::class);
102
                    Util::mwExecBg("{$phpPath} -f {$workerFilesMergerPath} '{$settings_file}'");
103
                    $res->data['upload_id'] = $upload_id;
104
                    $res->data['filename']  = $temp_dst_file;
105
                    $res->data['d_status']  = 'INPROGRESS';
106
                }
107
            } else {
108
                $res->success = true;
109
                // Передача файла целиком.
110
                $res->data['upload_id'] = $upload_id;
111
                $res->data['filename']  = $temp_dst_file;
112
                $res->data['d_status']  = 'UPLOAD_COMPLETE';
113
                file_put_contents($temp_dir . '/progress', '100');
114
115
                Util::mwExecBg(
116
                    '/sbin/shell_functions.sh killprocesses ' . $temp_dir . ' -TERM 0;rm -rf ' . $temp_dir,
117
                    '/dev/null',
118
                    120
119
                );
120
            }
121
        }
122
123
        return $res;
124
    }
125
126
    /**
127
     * Returns Status of uploading process
128
     *
129
     * @param $postData
130
     *
131
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
132
     */
133
    public static function statusUploadFile($postData): PBXApiResult
134
    {
135
        $res = new PBXApiResult();
136
        $res->processor = __METHOD__;
137
        $di               = Di::getDefault();
138
        if ($di === null) {
139
            $res->success = false;
140
            $res->messages[]='Dependency injector not initialized';
141
            return $res;
142
        }
143
        $uploadPath = $di->getShared('config')->path('core.uploadPath');
144
        if ($postData && isset($postData['id'])) {
145
            $upload_id     = $postData['id'];
146
            $progress_dir  = $uploadPath . '/' . $upload_id;
147
            $progress_file = $progress_dir . '/progress';
148
149
            if (empty($upload_id)) {
150
                $res->success = false;
151
                $res->data['d_status_progress'] = '0';
152
                $res->data['d_status']          = 'ID_NOT_SET';
153
            } elseif ( ! file_exists($progress_file) && file_exists($progress_dir)) {
154
                $res->success = true;
155
                $res->data['d_status_progress'] = '0';
156
                $res->data['d_status']          = 'INPROGRESS';
157
            } elseif ( ! file_exists($progress_dir)) {
158
                $res->success = false;
159
                $res->data['d_status_progress'] = '0';
160
                $res->data['d_status']          = 'NOT_FOUND';
161
            } elseif ('100' === file_get_contents($progress_file)) {
162
                $res->success = true;
163
                $res->data['d_status_progress'] = '100';
164
                $res->data['d_status']          = 'UPLOAD_COMPLETE';
165
            } else {
166
                $res->success = true;
167
                $res->data['d_status_progress'] = file_get_contents($progress_file);
168
            }
169
        }
170
171
        return $res;
172
    }
173
174
    /**
175
     * Конвертация файла в wav 8000.
176
     *
177
     * @param $filename
178
     *
179
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
180
     */
181
    public static function convertAudioFile($filename): PBXApiResult
182
    {
183
        $res = new PBXApiResult();
184
        $res->processor = __METHOD__;
185
        if ( ! file_exists($filename)) {
186
            $res->success = false;
187
            $res->messages[]="File '{$filename}' not found.";
188
            return $res;
189
        }
190
        $out          = [];
191
        $tmp_filename = '/tmp/' . time() . "_" . basename($filename);
192
        if (false === copy($filename, $tmp_filename)) {
193
            $res->success = false;
194
            $res->messages[] = "Unable to create temporary file '{$tmp_filename}'.";
195
            return $res;
196
        }
197
198
        // Принудительно устанавливаем расширение файла в wav.
199
        $n_filename     = Util::trimExtensionForFile($filename) . ".wav";
200
        $n_filename_mp3 = Util::trimExtensionForFile($filename) . ".mp3";
201
        // Конвертируем файл.
202
        $tmp_filename = escapeshellcmd($tmp_filename);
203
        $n_filename   = escapeshellcmd($n_filename);
204
        $soxPath      = Util::which('sox');
205
        Util::mwExec("{$soxPath} -v 0.99 -G '{$tmp_filename}' -c 1 -r 8000 -b 16 '{$n_filename}'", $out);
206
        $result_str = implode('', $out);
207
208
        $lamePath = Util::which('lame');
209
        Util::mwExec("{$lamePath} -b 32 --silent '{$n_filename}' '{$n_filename_mp3}'", $out);
210
        $result_mp3 = implode('', $out);
211
212
        // Чистим мусор.
213
        unlink($tmp_filename);
214
        if ($result_str !== '' && $result_mp3 !== '') {
215
            // Ошибка выполнения конвертации.
216
            $res->success = false;
217
            $res->messages[] = $result_str;
218
            return $res;
219
        }
220
221
        if ($filename !== $n_filename && $filename !== $n_filename_mp3) {
222
            @unlink($filename);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

222
            /** @scrutinizer ignore-unhandled */ @unlink($filename);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
223
        }
224
225
        $res->success = true;
226
        $res->data[]   = $n_filename_mp3;
227
228
        return $res;
229
    }
230
231
    /**
232
     * Unpack ModuleFile and get metadata information
233
     *
234
     * @param $filePath
235
     *
236
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
237
     */
238
    public static function  getMetadataFromModuleFile(string $filePath): PBXApiResult
239
    {
240
        $res = new PBXApiResult();
241
        $res->processor = __METHOD__;
242
243
        if (file_exists($filePath)) {
244
            $sevenZaPath = Util::which('7za');
245
            $grepPath    = Util::which('grep');
246
            $echoPath    = Util::which('echo');
247
            $awkPath     = Util::which('awk');
248
            $cmd = 'f="' . $filePath . '"; p=`' . $sevenZaPath . ' l $f | ' . $grepPath . ' module.json`;if [ "$?" == "0" ]; then ' . $sevenZaPath . ' -so e -y -r $f `' . $echoPath . ' $p |  ' . $awkPath . ' -F" " \'{print $6}\'`; fi';
249
250
            Util::mwExec($cmd, $out);
251
            $settings = json_decode(implode("\n", $out), true);
252
253
            $moduleUniqueID = $settings['moduleUniqueID'] ?? null;
254
            if ( ! $moduleUniqueID) {
255
                $res->messages[] = 'The" moduleUniqueID " in the module file is not described.the json or file does not exist.';
256
                return $res;
257
            }
258
            $res->success = true;
259
            $res->data   = [
260
                'filePath' => $filePath,
261
                'uniqid' => $moduleUniqueID,
262
            ];
263
        }
264
265
        return $res;
266
    }
267
268
    /**
269
     * Считывает содержимое файла, если есть разрешение.
270
     *
271
     * @param $filename
272
     * @param $needOriginal
273
     *
274
     * @return PBXApiResult
275
     */
276
    public static function fileReadContent($filename, $needOriginal = true): PBXApiResult
277
    {
278
        $res = new PBXApiResult();
279
        $res->processor = __METHOD__;
280
        $customFile    = CustomFiles::findFirst("filepath = '{$filename}'");
281
        if ($customFile !== null) {
282
            $filename_orgn = "{$filename}.orgn";
283
            if ($needOriginal && file_exists($filename_orgn)) {
284
                $filename = $filename_orgn;
285
            }
286
            $res->success = true;
287
            $res->data[]   = rawurlencode(file_get_contents($filename));
288
        } else {
289
            $res->success = false;
290
            $res->messages[] = 'No access to the file '.$filename;
291
        }
292
293
        return $res;
294
    }
295
296
    /**
297
     * Download IMG from MikoPBX repository
298
     *
299
     * @param $data
300
     *
301
     * @return PBXApiResult
302
     */
303
    public static function downloadNewFirmware($data): PBXApiResult
304
    {
305
        $di     = Di::getDefault();
306
        if ($di !== null){
307
            $tempDir = $di->getConfig()->path('core.uploadPath');
308
        } else {
309
            $tempDir = '/tmp';
310
        }
311
        $rmPath = Util::which('rm');
312
        $module  = 'NewFirmware';
313
        if ( ! file_exists($tempDir . "/{$module}")) {
314
            Util::mwMkdir($tempDir . "/{$module}");
315
        } else {
316
            // Чистим файлы, загруженные онлайн.
317
            Util::mwExec("{$rmPath} -rf {$tempDir}/{$module}/* ");
318
        }
319
        if (file_exists("{$tempDir}/update.img")) {
320
            // Чистим вручную загруженный файл.
321
            Util::mwExec("{$rmPath} -rf {$tempDir}/update.img");
322
        }
323
324
        $download_settings = [
325
            'res_file' => "{$tempDir}/{$module}/update.img",
326
            'url'      => $data['url'],
327
            'module'   => $module,
328
            'md5'      => $data['md5'],
329
            'action'   => $module,
330
        ];
331
332
        $workerDownloaderPath = Util::getFilePathByClassName(WorkerDownloader::class);
333
334
        file_put_contents($tempDir . "/{$module}/progress", '0');
335
        file_put_contents(
336
            $tempDir . "/{$module}/download_settings.json",
337
            json_encode($download_settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
338
        );
339
        $phpPath = Util::which('php');
340
        Util::mwExecBg("{$phpPath} -f {$workerDownloaderPath} " . $tempDir . "/{$module}/download_settings.json");
341
342
        $res = new PBXApiResult();
343
        $res->processor = __METHOD__;
344
        $res->success = true;
345
        $res->data['filename']=$download_settings['res_file'];
346
        $res->data['d_status']='DOWNLOAD_IN_PROGRESS';
347
348
        return $res;
349
    }
350
351
    /**
352
     * Return download Firnware from remote repository progress
353
     *
354
     * @return PBXApiResult
355
     */
356
    public static function firmwareDownloadStatus(): PBXApiResult
357
    {
358
        clearstatcache();
359
        $res = new PBXApiResult();
360
        $res->processor = __METHOD__;
361
        $res->success = true;
362
        $di     = Di::getDefault();
363
        if ($di !== null){
364
            $tempDir = $di->getConfig()->path('core.uploadPath');
365
        } else {
366
            $tempDir = '/tmp';
367
        }
368
        $modulesDir    = $tempDir . '/NewFirmware';
369
        $progress_file = $modulesDir . '/progress';
370
371
        $error = '';
372
        if (file_exists($modulesDir . '/error')) {
373
            $error = trim(file_get_contents($modulesDir . '/error'));
374
        }
375
376
        if ( ! file_exists($progress_file)) {
377
            $res->data['d_status_progress'] = '0';
378
            $res->data['d_status']          = 'NOT_FOUND';
379
        } elseif ('' !== $error) {
380
            $res->data['d_status']          = 'DOWNLOAD_ERROR';
381
            $res->data['d_status_progress'] = file_get_contents($progress_file);
382
            $res->data['d_error']           = $error;
383
        } elseif ('100' === file_get_contents($progress_file)) {
384
            $res->data['d_status_progress'] = '100';
385
            $res->data['d_status']          = 'DOWNLOAD_COMPLETE';
386
            $res->data['filePath'] = "{$tempDir}/NewFirmware/update.img";
387
        } else {
388
            $res->data['d_status_progress'] = file_get_contents($progress_file);
389
            $d_pid                       = Util::getPidOfProcess($tempDir . '/NewFirmware/download_settings.json');
390
            if (empty($d_pid)) {
391
                $res->data['d_status'] = 'DOWNLOAD_ERROR';
392
                $error              = '';
393
                if (file_exists($modulesDir . '/error')) {
394
                    $error = file_get_contents($modulesDir . '/error');
395
                }
396
                $res->data['d_error'] = $error;
397
            } else {
398
                $res->data['d_status'] = 'DOWNLOAD_IN_PROGRESS';
399
            }
400
        }
401
402
        return $res;
403
    }
404
405
    /**
406
     * Запуск процесса фоновой загрузки доп. модуля АТС.
407
     *
408
     * @param $module
409
     * @param $url
410
     * @param $md5
411
     *
412
     * @return PBXApiResult
413
     */
414
    public static function moduleStartDownload($module, $url, $md5): PBXApiResult
415
    {
416
        $res = new PBXApiResult();
417
        $res->processor = __METHOD__;
418
        $di     = Di::getDefault();
419
        if ($di !== null){
420
            $tempDir = $di->getConfig()->path('core.uploadPath');
421
        } else {
422
            $tempDir = '/tmp';
423
        }
424
425
        $moduleDirTmp = "{$tempDir}/{$module}";
426
        Util::mwMkdir($moduleDirTmp);
427
428
        $download_settings = [
429
            'res_file' => "$moduleDirTmp/modulefile.zip",
430
            'url'      => $url,
431
            'module'   => $module,
432
            'md5'      => $md5,
433
            'action'   => 'moduleInstall',
434
        ];
435
        if (file_exists("$moduleDirTmp/error")) {
436
            unlink("$moduleDirTmp/error");
437
        }
438
        if (file_exists("$moduleDirTmp/installed")) {
439
            unlink("$moduleDirTmp/installed");
440
        }
441
        file_put_contents("$moduleDirTmp/progress", '0');
442
        file_put_contents(
443
            "$moduleDirTmp/download_settings.json",
444
            json_encode($download_settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
445
        );
446
        $workerDownloaderPath = Util::getFilePathByClassName(WorkerDownloader::class);
447
        $phpPath = Util::which('php');
448
        Util::mwExecBg("{$phpPath} -f {$workerDownloaderPath} $moduleDirTmp/download_settings.json");
449
450
        $res->data['uniqid'] = $module;
451
        $res->data['d_status'] = 'DOWNLOAD_IN_PROGRESS';
452
        $res->success = true;
453
        return $res;
454
    }
455
456
    /**
457
     * Возвращает статус скачивания модуля.
458
     *
459
     * @param $moduleUniqueID
460
     *
461
     * @return PBXApiResult
462
     */
463
    public static function moduleDownloadStatus(string $moduleUniqueID): PBXApiResult
464
    {
465
        clearstatcache();
466
        $res = new PBXApiResult();
467
        $res->processor = __METHOD__;
468
        $di     = Di::getDefault();
469
        if ($di !== null){
470
            $tempDir = $di->getConfig()->path('core.uploadPath');
471
        } else {
472
            $tempDir = '/tmp';
473
        }
474
        $moduleDirTmp  = $tempDir . '/' . $moduleUniqueID;
475
        $progress_file = $moduleDirTmp . '/progress';
476
        $error         = '';
477
        if (file_exists($moduleDirTmp . '/error')) {
478
            $error = trim(file_get_contents($moduleDirTmp . '/error'));
479
        }
480
481
        // Ожидание запуска процесса загрузки.
482
        $d_pid = Util::getPidOfProcess("{$moduleDirTmp}/download_settings.json");
483
        if (empty($d_pid)) {
484
            usleep(500000);
485
        }
486
487
        if ( ! file_exists($progress_file)) {
488
            $res->data['d_status_progress'] = '0';
489
            $res->data['d_status']          = 'NOT_FOUND';
490
            $res->success = false;
491
        } elseif ('' !== $error) {
492
            $res->data['d_status']          = 'DOWNLOAD_ERROR';
493
            $res->data['d_status_progress'] = file_get_contents($progress_file);
494
            $res->data['d_error']           = $error;
495
            $res->success = false;
496
        } elseif ('100' === file_get_contents($progress_file)) {
497
            $res->data['d_status_progress'] = '100';
498
            $res->data['d_status']          = 'DOWNLOAD_COMPLETE';
499
            $res->data['filePath'] =  "$moduleDirTmp/modulefile.zip";
500
            $res->success = true;
501
        } else {
502
            $res->data['d_status_progress'] = file_get_contents($progress_file);
503
            $d_pid                       = Util::getPidOfProcess($moduleDirTmp . '/download_settings.json');
504
            if (empty($d_pid)) {
505
                $res->data['d_status'] = 'DOWNLOAD_ERROR';
506
                $error              = '';
507
                if (file_exists($moduleDirTmp . '/error')) {
508
                    $error = file_get_contents($moduleDirTmp . '/error');
509
                }
510
                $res->messages[] = $error;
511
                $res->success = false;
512
            } else {
513
                $res->data['d_status'] = 'DOWNLOAD_IN_PROGRESS';
514
                $res->success = true;
515
            }
516
        }
517
        return $res;
518
    }
519
520
    /**
521
     * Delete file from disk by filepath
522
     *
523
     * @param $filePath
524
     *
525
     * @return PBXApiResult
526
     */
527
    public static function removeAudioFile($filePath): PBXApiResult
528
    {
529
        $res = new PBXApiResult();
530
        $res->processor = __METHOD__;
531
        $extension = Util::getExtensionOfFile($filePath);
532
        if ( ! in_array($extension, ['mp3', 'wav', 'alaw'])) {
533
            $res->success = false;
534
            $res->messages[] = "It is forbidden to remove the file type $extension.";
535
            return $res;
536
        }
537
538
        if ( ! file_exists($filePath)) {
539
            $res->success = true;
540
            $res->data['message']="File '{$filePath}' already deleted";
541
            return $res;
542
        }
543
544
        $out = [];
545
546
        $arrDeletedFiles = [
547
            escapeshellarg(Util::trimExtensionForFile($filePath) . ".wav"),
548
            escapeshellarg(Util::trimExtensionForFile($filePath) . ".mp3"),
549
            escapeshellarg(Util::trimExtensionForFile($filePath) . ".alaw"),
550
        ];
551
552
        $rmPath = Util::which('rm');
553
        Util::mwExec("{$rmPath} -rf " . implode(' ', $arrDeletedFiles), $out);
554
        if (file_exists($filePath)) {
555
            $res->success = false;
556
            $res->messages = $out;
557
        } else {
558
            $res->success = true;
559
        }
560
561
        return $res;
562
    }
563
564
    /**
565
     * Install new additional extension module
566
     *
567
     * @param $filePath
568
     *
569
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
570
     *
571
     */
572
    public static function installModuleFromFile($filePath): PBXApiResult
573
    {
574
        $res = new PBXApiResult();
575
        $res->processor = __METHOD__;
576
        $moduleMetadata = FilesManagementProcessor::getMetadataFromModuleFile($filePath);
577
        if (!$moduleMetadata->success) {
578
            return $moduleMetadata;
579
        } else {
580
            $moduleUniqueID = $moduleMetadata->data['uniqid'];
581
            $res = self::installModule($filePath, $moduleUniqueID);
582
        }
583
        return $res;
584
    }
585
586
    /**
587
     * Install module from file
588
     *
589
     * @param string $filePath
590
     *
591
     * @param string $moduleUniqueID
592
     *
593
     * @return PBXApiResult
594
     */
595
    public static function installModule(string $filePath, string $moduleUniqueID):PBXApiResult
596
    {
597
        $res = new PBXApiResult();
598
        $res->processor = __METHOD__;
599
        $res->success = true;
600
        $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID);
601
        $needBackup       = is_dir($currentModuleDir);
602
603
        if ($needBackup){
604
            self::uninstallModule($moduleUniqueID, true);
605
        }
606
607
        $semZaPath = Util::which('7za');
608
        Util::mwExec("{$semZaPath} e -spf -aoa -o{$currentModuleDir} {$filePath}");
609
        Util::addRegularWWWRights($currentModuleDir);
610
611
        $pbxExtensionSetupClass       = "\\Modules\\{$moduleUniqueID}\\Setup\\PbxExtensionSetup";
612
        if (class_exists($pbxExtensionSetupClass)
613
            && method_exists($pbxExtensionSetupClass, 'installModule'))
614
        {
615
            $setup = new $pbxExtensionSetupClass($moduleUniqueID);
616
            if ( ! $setup->installModule()) {
617
                $res->success = false;
618
                $res->messages[] = $setup->getMessages();
619
            }
620
        } else {
621
            $res->success = false;
622
            $res->messages[]  = "Install error: the class {$pbxExtensionSetupClass} not exists";
623
        }
624
625
        if ($res->success) {
626
            $result['needRestartWorkers'] = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
627
        }
628
629
        return $res;
630
    }
631
632
    /**
633
     * Uninstall module
634
     * @param string $moduleUniqueID
635
     *
636
     * @param bool   $keepSettings
637
     *
638
     * @return PBXApiResult
639
     */
640
    public static function uninstallModule(string $moduleUniqueID, bool $keepSettings):PBXApiResult
641
    {
642
        $res = new PBXApiResult();
643
        $res->processor = __METHOD__;
644
        $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID);
645
        // Kill all module processes
646
        if (is_dir("{$currentModuleDir}/bin")) {
647
            $busyboxPath = Util::which('busybox');
648
            $killPath    = Util::which('kill');
649
            $lsofPath    = Util::which('lsof');
650
            $grepPath    = Util::which('grep');
651
            $awkPath     = Util::which('awk');
652
            $uniqPath    = Util::which('uniq');
653
            Util::mwExec(
654
                "{$busyboxPath} {$killPath} -9 $({$lsofPath} {$currentModuleDir}/bin/* |  {$busyboxPath} {$grepPath} -v COMMAND | {$busyboxPath} {$awkPath}  '{ print $2}' | {$busyboxPath} {$uniqPath})"
655
            );
656
        }
657
        // Uninstall module with keep settings and backup db
658
        $moduleClass = "\\Modules\\{$moduleUniqueID}\\Setup\\PbxExtensionSetup";
659
        if (class_exists($moduleClass)
660
            && method_exists($moduleClass, 'uninstallModule')) {
661
            $setup = new $moduleClass($moduleUniqueID);
662
        } else {
663
            // Заглушка которая позволяет удалить модуль из базы данных, которого нет на диске
664
            $moduleClass = PbxExtensionSetupFailure::class;
665
            $setup       = new $moduleClass($moduleUniqueID);
666
        }
667
        if ($setup->uninstallModule($keepSettings)) {
668
            $res->success = true;
669
            $res->data['needRestartWorkers'] = true;
670
        } else {
671
            $res->success = false;
672
            $res->messages[] = $setup->getMessages();
673
        }
674
675
        if (is_dir($currentModuleDir)) {
676
            // Broken or very old module. Force uninstall.
677
            $rmPath = Util::which('rm');
678
            Util::mwExec("{$rmPath} -rf {$currentModuleDir}");
679
        }
680
        return $res;
681
    }
682
}