Passed
Push — develop ( 236043...535087 )
by Портнов
13:08
created

WorkerModelsEvents::reloadSSH()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Workers;
21
22
require_once 'Globals.php';
23
24
use MikoPBX\Common\Handlers\CriticalErrorsHandler;
25
use MikoPBX\Common\Models\{AsteriskManagerUsers,
26
    CallQueueMembers,
27
    CallQueues,
28
    Codecs,
29
    ConferenceRooms,
30
    CustomFiles,
31
    DialplanApplications,
32
    ExtensionForwardingRights,
33
    Extensions,
34
    ExternalPhones,
35
    Fail2BanRules,
36
    FirewallRules,
37
    Iax,
38
    IncomingRoutingTable,
39
    IvrMenu,
40
    IvrMenuActions,
41
    LanInterfaces,
42
    ModelsBase,
43
    NetworkFilters,
44
    OutgoingRoutingTable,
45
    OutWorkTimes,
46
    PbxExtensionModules,
47
    PbxSettings,
48
    PbxSettingsConstants,
49
    Sip,
50
    SipHosts,
51
    SoundFiles,
52
    Users};
53
use MikoPBX\Common\Providers\BeanstalkConnectionModelsProvider;
54
use MikoPBX\Common\Providers\ModulesDBConnectionsProvider;
55
use MikoPBX\Common\Providers\PBXConfModulesProvider;
56
use MikoPBX\Core\Asterisk\Configs\AsteriskConfigInterface;
57
use MikoPBX\Core\Asterisk\Configs\QueueConf;
58
use MikoPBX\Core\Asterisk\Configs\ResParkingConf;
59
use MikoPBX\Core\Providers\AsteriskConfModulesProvider;
60
use MikoPBX\Core\System\{BeanstalkClient,
61
    Configs\CronConf,
62
    Configs\Fail2BanConf,
63
    Configs\IptablesConf,
64
    Configs\NatsConf,
65
    Configs\NginxConf,
66
    Configs\NTPConf,
67
    Configs\PHPConf,
68
    Configs\SentryConf,
69
    Configs\SSHConf,
70
    Configs\SyslogConf,
71
    PBX,
72
    Processes,
73
    System,
74
    Util
75
};
76
use MikoPBX\Modules\Config\SystemConfigInterface;
77
use MikoPBX\PBXCoreREST\Workers\WorkerApiCommands;
78
use Phalcon\Di;
79
use Pheanstalk\Contract\PheanstalkInterface;
80
use Throwable;
81
82
ini_set('error_reporting', E_ALL);
83
ini_set('display_startup_errors', 1);
84
85
/**
86
 * WorkerModelsEvents.
87
 *
88
 * @package MikoPBX\Core\Workers
89
 */
90
class WorkerModelsEvents extends WorkerBase
91
{
92
    private const R_MANAGERS = 'reloadManager';
93
    private const R_QUEUES = 'reloadQueues';
94
    private const R_DIALPLAN = 'reloadDialplan';
95
    private const R_PARKING  = 'reloadParking';
96
    private const R_CUSTOM_F = 'updateCustomFiles';
97
    private const R_FIREWALL = 'reloadFirewall';
98
    private const R_NETWORK = 'networkReload';
99
    private const R_IAX = 'reloadIax';
100
    private const R_SIP = 'reloadSip';
101
    private const R_RTP = 'rtpReload';
102
    private const R_PBX_CORE = 'pbxCoreReload';
103
    private const R_FEATURES = 'reloadFeatures';
104
    private const R_CRON = 'reloadCron';
105
    public const  R_NGINX = 'reloadNginx';
106
    public const  R_NGINX_CONF = 'reloadNginxConf';
107
    public const  R_FAIL2BAN_CONF = 'reloadFail2BanConf';
108
    private const R_PHP_FPM = 'reloadPHPFPM';
109
    private const R_TIMEZONE = 'updateTomeZone';
110
    private const R_SYSLOG = 'restartSyslogD';
111
    private const R_SSH = 'reloadSSH';
112
    private const R_LICENSE = 'reloadLicense';
113
    private const R_NATS = 'reloadNats';
114
    private const R_VOICEMAIL = 'reloadVoicemail';
115
    private const R_REST_API_WORKER = 'reloadRestAPIWorker';
116
    private const R_CALL_EVENTS_WORKER = 'reloadWorkerCallEvents';
117
    private const R_PBX_MODULE_STATE = 'afterModuleStateChanged';
118
    private const R_MOH = 'reloadMoh';
119
    private const R_NTP = 'reloadNtp';
120
    private const R_UPDATE_REC_SAVE_PERIOD = 'updateRecordSavePeriod';
121
    private const R_ADVICES = 'cleanupAdvicesCache';
122
    private const R_SENTRY = 'reloadSentry';
123
124
    private int $last_change;
125
    private array $modified_tables;
126
127
    private int $timeout = 2;
128
129
    // Array of core conf objects
130
    private array $arrAsteriskConfObjects;
131
    private array $PRIORITY_R;
132
    private array $pbxSettingsDependencyTable = [];
133
    private array $modelsDependencyTable = [];
134
135
    /**
136
     * Starts the models events worker.
137
     *
138
     * @param array $argv The command-line arguments passed to the worker.
139
     * @return void
140
     */
141
    public function start(array $argv): void
142
    {
143
        $this->last_change = time() - 2;
144
145
        $this->arrAsteriskConfObjects = $this->di->getShared(AsteriskConfModulesProvider::SERVICE_NAME);
146
147
        $this->initPbxSettingsDependencyTable();
148
        $this->initModelsDependencyTable();
149
150
        $this->PRIORITY_R = [
151
            self::R_PBX_MODULE_STATE,
152
            self::R_TIMEZONE,
153
            self::R_SYSLOG,
154
            self::R_REST_API_WORKER,
155
            self::R_NETWORK,
156
            self::R_FIREWALL,
157
            self::R_FAIL2BAN_CONF,
158
            self::R_SSH,
159
            self::R_LICENSE,
160
            self::R_SENTRY,
161
            self::R_NATS,
162
            self::R_NTP,
163
            self::R_PHP_FPM,
164
            self::R_NGINX,
165
            self::R_NGINX_CONF,
166
            self::R_CRON,
167
            self::R_PBX_CORE,
168
            self::R_FEATURES,
169
            self::R_SIP,
170
            self::R_RTP,
171
            self::R_IAX,
172
            self::R_DIALPLAN,
173
            self::R_PARKING,
174
            self::R_QUEUES,
175
            self::R_MANAGERS,
176
            self::R_CUSTOM_F,
177
            self::R_VOICEMAIL,
178
            self::R_MOH,
179
            self::R_CALL_EVENTS_WORKER,
180
            self::R_UPDATE_REC_SAVE_PERIOD,
181
            self::R_ADVICES,
182
        ];
183
184
        $this->modified_tables = [];
185
186
        /** @var BeanstalkClient $client */
187
        $client = $this->di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
188
        $client->subscribe(self::class, [$this, 'processModelChanges']);
189
        $client->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
190
        $client->setTimeoutHandler([$this, 'timeoutHandler']);
191
192
        while ($this->needRestart === false) {
193
            $client->wait();
194
        }
195
        // Execute all collected changes before exit
196
        $this->timeoutHandler();
197
    }
198
199
    /**
200
     * Initializes the PBX settings dependency table.
201
     */
202
    private function initPbxSettingsDependencyTable(): void
203
    {
204
        $tables = [];
205
        // FeaturesSettings
206
        $tables[] = [
207
            'settingName' => [
208
                'PBXLanguage',
209
                'PBXInternalExtensionLength',
210
                'PBXFeatureAttendedTransfer',
211
                'PBXFeatureBlindTransfer',
212
                'PBXFeatureDigitTimeout',
213
                'PBXFeatureAtxferNoAnswerTimeout',
214
                'PBXFeatureTransferDigitTimeout',
215
                'PBXFeaturePickupExten',
216
            ],
217
            'functions' => [
218
                self::R_FEATURES,
219
                self::R_DIALPLAN,
220
            ],
221
        ];
222
223
        // Parking settings
224
        $tables[] = [
225
            'settingName' => [
226
                PbxSettingsConstants::PBX_CALL_PARKING_EXT,
227
                PbxSettingsConstants::PBX_CALL_PARKING_START_SLOT,
228
                PbxSettingsConstants::PBX_CALL_PARKING_END_SLOT,
229
            ],
230
            'functions' => [
231
                self::R_FEATURES, // ??? parkcall in features.conf ???
232
                self::R_DIALPLAN,
233
                self::R_PARKING,
234
            ],
235
        ];
236
237
        // CallRecordSettings
238
        $tables[] = [
239
            'settingName' => [
240
                'PBXRecordCalls',
241
                'PBXRecordCallsInner',
242
                'PBXSplitAudioThread',
243
            ],
244
            'functions' => [
245
                self::R_DIALPLAN,
246
            ],
247
        ];
248
249
        // CallRecordSettings / The period of storing conversation records
250
        $tables[] = [
251
            'settingName' => [
252
                'PBXRecordSavePeriod',
253
            ],
254
            'functions' => [
255
                self::R_UPDATE_REC_SAVE_PERIOD,
256
            ],
257
        ];
258
259
        // AMIParameters
260
        $tables[] = [
261
            'settingName' => [
262
                'AMIPort',
263
                'AJAMPort',
264
                'AJAMPortTLS',
265
            ],
266
            'functions' => [
267
                self::R_MANAGERS,
268
            ],
269
        ];
270
271
        // IaxParameters
272
        $tables[] = [
273
            'settingName' => [
274
                'IAXPort',
275
            ],
276
            'functions' => [
277
                self::R_IAX,
278
            ],
279
        ];
280
281
        // Guest calls without authorization
282
        $tables[] = [
283
            'settingName' => [
284
                'PBXAllowGuestCalls',
285
                'UseWebRTC',
286
            ],
287
            'functions' => [
288
                self::R_SIP,
289
                self::R_DIALPLAN,
290
            ],
291
        ];
292
293
        // SipParameters
294
        $tables[] = [
295
            'settingName' => [
296
                'SIPPort',
297
                'TLS_PORT',
298
                'SIPDefaultExpiry',
299
                'SIPMinExpiry',
300
                'SIPMaxExpiry',
301
                'PBXLanguage',
302
            ],
303
            'functions' => [
304
                self::R_SIP,
305
            ],
306
        ];
307
308
        // RTPParameters
309
        $tables[] = [
310
            'settingName' => [
311
                'RTPPortFrom',
312
                'RTPPortTo',
313
                'RTPStunServer',
314
            ],
315
            'functions' => [
316
                self::R_RTP,
317
            ],
318
        ];
319
320
        // SSHParameters
321
        $tables[] = [
322
            'settingName' => [
323
                PbxSettingsConstants::SSH_PORT,
324
                'SSHRsaKey',
325
                'SSHDssKey',
326
                PbxSettingsConstants::SSH_PASSWORD,
327
                'SSHecdsaKey',
328
                PbxSettingsConstants::SSH_AUTHORIZED_KEYS,
329
                PbxSettingsConstants::SSH_DISABLE_SSH_PASSWORD,
330
            ],
331
            'functions' => [
332
                self::R_SSH,
333
            ],
334
        ];
335
336
        // FirewallParameters
337
        $tables[] = [
338
            'settingName' => [
339
                'SIPPort',
340
                'TLS_PORT',
341
                'RTPPortFrom',
342
                'RTPPortTo',
343
                'IAXPort',
344
                'AMIPort',
345
                'AJAMPort',
346
                'AJAMPortTLS',
347
                'WEBPort',
348
                'WEBHTTPSPort',
349
                PbxSettingsConstants::SSH_PORT,
350
                'PBXFirewallEnabled',
351
                'PBXFail2BanEnabled',
352
            ],
353
            'functions' => [
354
                self::R_FIREWALL,
355
            ],
356
            'strPosKey' => 'FirewallSettings',
357
        ];
358
359
        // FirewallParameters
360
        $tables[] = [
361
            'settingName' => [
362
                'WEBPort',
363
                'WEBHTTPSPort',
364
                'WEBHTTPSPublicKey',
365
                'WEBHTTPSPrivateKey',
366
                'RedirectToHttps',
367
            ],
368
            'functions' => [
369
                self::R_NGINX,
370
            ],
371
        ];
372
373
        // CronParameters
374
        $tables[] = [
375
            'settingName' => [
376
                'RestartEveryNight',
377
            ],
378
            'functions' => [
379
                self::R_CRON,
380
            ],
381
        ];
382
383
        // DialplanParameters
384
        $tables[] = [
385
            'settingName' => [
386
                'PBXLanguage',
387
                'PBXRecordAnnouncementIn',
388
                'PBXRecordAnnouncementOut',
389
            ],
390
            'functions' => [
391
                self::R_DIALPLAN,
392
            ],
393
        ];
394
        // DialplanParameters
395
        $tables[] = [
396
            'settingName' => [
397
                'PBXLanguage',
398
            ],
399
            'functions' => [
400
                self::R_PBX_CORE,
401
            ],
402
        ];
403
404
        // VoiceMailParameters
405
        $tables[] = [
406
            'settingName' => [
407
                'MailTplVoicemailSubject',
408
                'MailTplVoicemailBody',
409
                'MailTplVoicemailFooter',
410
                'MailSMTPSenderAddress',
411
                'MailSMTPUsername',
412
                'PBXTimezone',
413
                'VoicemailNotificationsEmail',
414
                'SystemNotificationsEmail',
415
                'SystemEmailForMissed',
416
            ],
417
            'functions' => [
418
                self::R_VOICEMAIL,
419
            ],
420
        ];
421
422
        // VisualLanguageSettings
423
        $tables[] = [
424
            'settingName' => [
425
                'SSHLanguage',
426
                'WebAdminLanguage',
427
            ],
428
            'functions' => [
429
                self::R_REST_API_WORKER,
430
            ],
431
        ];
432
433
        // LicenseSettings
434
        $tables[] = [
435
            'settingName' => [
436
                'PBXLicense',
437
            ],
438
            'functions' => [
439
                self::R_LICENSE,
440
                self::R_NATS,
441
            ],
442
        ];
443
444
        // TimeZoneSettings
445
        $tables[] = [
446
            'settingName' => [
447
                'PBXTimezone',
448
            ],
449
            'functions' => [
450
                self::R_TIMEZONE,
451
                self::R_NGINX,
452
                self::R_PHP_FPM,
453
                self::R_REST_API_WORKER,
454
                self::R_CALL_EVENTS_WORKER,
455
                self::R_SYSLOG,
456
            ],
457
        ];
458
459
        // NTPSettings
460
        $tables[] = [
461
            'settingName' => [
462
                'PBXManualTimeSettings',
463
                'NTPServer',
464
                'PBXTimezone',
465
            ],
466
            'functions' => [
467
                self::R_NTP,
468
            ],
469
        ];
470
471
        // Advices
472
        $tables[] = [
473
            'settingName' => [
474
                'WebAdminPassword',
475
                PbxSettingsConstants::SSH_PASSWORD,
476
                'PBXFirewallEnabled',
477
            ],
478
            'functions' => [
479
                self::R_ADVICES,
480
            ],
481
        ];
482
483
        // Sentry
484
        $tables[] = [
485
            'settingName' => [
486
                'SendMetrics',
487
            ],
488
            'functions' => [
489
                self::R_SENTRY,
490
            ],
491
        ];
492
493
        $this->pbxSettingsDependencyTable = $tables;
494
    }
495
496
    /**
497
     * Initializes the models dependency table.
498
     */
499
    private function initModelsDependencyTable(): void
500
    {
501
        $tables = [];
502
        $tables[] = [
503
            'settingName' => [
504
                AsteriskManagerUsers::class,
505
            ],
506
            'functions' => [
507
                self::R_MANAGERS,
508
                self::R_ADVICES,
509
            ],
510
        ];
511
512
        $tables[] = [
513
            'settingName' => [
514
                CallQueueMembers::class,
515
            ],
516
            'functions' => [
517
                self::R_QUEUES,
518
            ],
519
        ];
520
521
        $tables[] = [
522
            'settingName' => [
523
                CallQueues::class,
524
            ],
525
            'functions' => [
526
                self::R_QUEUES,
527
                self::R_DIALPLAN,
528
            ],
529
        ];
530
        $tables[] = [
531
            'settingName' => [
532
                ExternalPhones::class,
533
                Extensions::class,
534
                DialplanApplications::class,
535
                IncomingRoutingTable::class,
536
                IvrMenu::class,
537
                IvrMenuActions::class,
538
                OutgoingRoutingTable::class,
539
                OutWorkTimes::class,
540
                ConferenceRooms::class,
541
            ],
542
            'functions' => [
543
                self::R_DIALPLAN,
544
            ],
545
        ];
546
547
        $tables[] = [
548
            'settingName' => [
549
                CustomFiles::class,
550
            ],
551
            'functions' => [
552
                self::R_CUSTOM_F,
553
                self::R_ADVICES,
554
            ],
555
        ];
556
557
        $tables[] = [
558
            'settingName' => [
559
                Sip::class,
560
            ],
561
            'functions' => [
562
                self::R_SIP,
563
                self::R_DIALPLAN,
564
                self::R_FIREWALL,
565
                self::R_ADVICES,
566
            ],
567
        ];
568
569
        $tables[] = [
570
            'settingName' => [
571
                Users::class,
572
                ExtensionForwardingRights::class,
573
            ],
574
            'functions' => [
575
                self::R_SIP,
576
                self::R_DIALPLAN,
577
            ],
578
        ];
579
580
        $tables[] = [
581
            'settingName' => [
582
                FirewallRules::class,
583
                Fail2BanRules::class,
584
            ],
585
            'functions' => [
586
                self::R_FIREWALL,
587
            ],
588
        ];
589
590
        $tables[] = [
591
            'settingName' => [
592
                Iax::class,
593
            ],
594
            'functions' => [
595
                self::R_IAX,
596
                self::R_DIALPLAN,
597
            ],
598
        ];
599
600
        $tables[] = [
601
            'settingName' => [
602
                Codecs::class,
603
            ],
604
            'functions' => [
605
                self::R_IAX,
606
                self::R_SIP,
607
            ],
608
        ];
609
610
        $tables[] = [
611
            'settingName' => [
612
                SoundFiles::class,
613
            ],
614
            'functions' => [
615
                self::R_MOH,
616
                self::R_DIALPLAN,
617
            ],
618
        ];
619
620
        $tables[] = [
621
            'settingName' => [
622
                LanInterfaces::class,
623
            ],
624
            'functions' => [
625
                self::R_NETWORK,
626
                self::R_IAX,
627
                self::R_SIP,
628
                self::R_ADVICES,
629
            ],
630
        ];
631
632
        $tables[] = [
633
            'settingName' => [
634
                SipHosts::class,
635
            ],
636
            'functions' => [
637
                self::R_FIREWALL,
638
                self::R_SIP,
639
            ],
640
        ];
641
642
        $tables[] = [
643
            'settingName' => [
644
                NetworkFilters::class,
645
            ],
646
            'functions' => [
647
                self::R_FIREWALL,
648
                self::R_SIP,
649
                self::R_MANAGERS,
650
                self::R_ADVICES,
651
            ],
652
        ];
653
654
        $this->modelsDependencyTable = $tables;
655
    }
656
657
    /**
658
     * Timeout handles
659
     */
660
    public function timeoutHandler(): void
661
    {
662
        $this->last_change = time() - $this->timeout;
663
        $this->startReload();
664
    }
665
666
667
    /**
668
     * Starts the reload process if there are modified tables.
669
     *
670
     * @return void
671
     */
672
    private function startReload(): void
673
    {
674
        // Check if there are any modified tables
675
        if (count($this->modified_tables) === 0) {
676
            return;
677
        }
678
679
        // Check if enough time has passed since the last change
680
        $delta = time() - $this->last_change;
681
        if ($delta < $this->timeout) {
682
            return;
683
        }
684
685
        // Process changes for each method in priority order
686
        foreach ($this->PRIORITY_R as $method_name) {
687
            $action = $this->modified_tables[$method_name] ?? null;
688
            $parameters = $this->modified_tables['parameters'][$method_name] ?? null;
689
690
            // Skip if there is no change for this method
691
            if ($action === null) {
692
                continue;
693
            }
694
695
            // Call the method if it exists
696
            if (method_exists($this, $method_name)) {
697
                Util::sysLogMsg(__METHOD__, "Process changes by {$method_name}", LOG_DEBUG);
698
                if ($parameters === null) {
699
                    $this->$method_name();
700
                } else {
701
                    $this->$method_name($parameters);
702
                }
703
            }
704
        }
705
706
        // Send information about models changes to additional modules bulky without any details
707
        PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_NEED_RELOAD, [$this->modified_tables]);
708
709
        // Reset the modified tables array
710
        $this->modified_tables = [];
711
    }
712
713
    /**
714
     * Processes model changes received from the Beanstalk queue.
715
     *
716
     * @param BeanstalkClient $message The message received from the Beanstalk queue.
717
     * @return void
718
     */
719
    public function processModelChanges(BeanstalkClient $message): void
720
    {
721
        try {
722
            // Decode the received message
723
            $receivedMessage = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
724
725
            // Check the source of the message and perform actions accordingly
726
            if ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION
727
                && in_array($receivedMessage['action'], $this->PRIORITY_R, true)) {
728
                // Store the modified table and its parameters
729
                $this->modified_tables[$receivedMessage['action']] = true;
730
                $this->modified_tables['parameters'][$receivedMessage['action']] = $receivedMessage['parameters'];
731
            } elseif ($receivedMessage['source'] === BeanstalkConnectionModelsProvider::SOURCE_MODELS_CHANGED) {
732
733
                // Fill the modified tables array with the changes from the received message
734
                $this->fillModifiedTables($receivedMessage);
735
            }
736
737
            // Start the reload process if there are modified tables
738
            $this->startReload();
739
740
            if (!$receivedMessage) {
741
                return;
742
            }
743
744
            // Send information about model changes to additional modules with changed data details
745
            PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::MODELS_EVENT_CHANGE_DATA, [$receivedMessage]);
746
        } catch (Throwable $exception) {
747
            $this->needRestart = true;
748
            CriticalErrorsHandler::handleExceptionWithSyslog($exception);
749
        }
750
    }
751
752
    /**
753
     * Fills the modified tables array with changes based on the received data.
754
     *
755
     * @param array $data The data containing the changes.
756
     * @return void
757
     */
758
    private function fillModifiedTables(array $data): void
759
    {
760
        $count_changes = count($this->modified_tables);
761
        $called_class = $data['model'] ?? '';
762
        Util::sysLogMsg(__METHOD__, "New changes " . json_encode($data), LOG_DEBUG);
763
764
        // Clear cache for the called class
765
        ModelsBase::clearCache($called_class);
766
767
        // Get new settings for dependent modules
768
        $this->getNewSettingsForDependentModules($called_class);
769
770
        // Fill modified tables from models
771
        $this->fillModifiedTablesFromModels($called_class);
772
773
        // Fill modified tables from PBX settings data
774
        $this->fillModifiedTablesFromPbxSettingsData($called_class, $data['recordId']);
775
776
        // Fill modified tables from PBX extension modules
777
        $this->fillModifiedTablesFromPbxExtensionModules($called_class, $data['recordId']);
778
779
        // Start counting time when the first task is received
780
        if ($count_changes === 0 && count($this->modified_tables) > 0) {
781
            $this->last_change = time();
782
        }
783
    }
784
785
    /**
786
     * Retrieves new settings for dependent modules based on the called class.
787
     *
788
     * @param string $called_class The called class for which to retrieve settings.
789
     * @return void
790
     */
791
    private function getNewSettingsForDependentModules(string $called_class): void
792
    {
793
        foreach ($this->arrAsteriskConfObjects as $configClassObj) {
794
            try {
795
                $dependencies = call_user_func([$configClassObj, AsteriskConfigInterface::GET_DEPENDENCE_MODELS]);
796
797
                // Check if the called class is a dependency and the config class has the GET_SETTINGS method
798
                if (in_array($called_class, $dependencies, true)
799
                    && method_exists($configClassObj, AsteriskConfigInterface::GET_SETTINGS)
800
                ) {
801
                    // Retrieve the new settings for the config class
802
                    call_user_func([$configClassObj, AsteriskConfigInterface::GET_SETTINGS]);
803
                }
804
            } catch (Throwable $e) {
805
                CriticalErrorsHandler::handleExceptionWithSyslog($e);
806
                continue;
807
            }
808
        }
809
    }
810
811
    /**
812
     * Fills the modified tables array based on the models dependency table and the called class.
813
     *
814
     * @param string $called_class The called class.
815
     * @return void
816
     */
817
    private function fillModifiedTablesFromModels(string $called_class): void
818
    {
819
        foreach ($this->modelsDependencyTable as $dependencyData) {
820
            if (!in_array($called_class, $dependencyData['settingName'], true)) {
821
                continue;
822
            }
823
            foreach ($dependencyData['functions'] as $function) {
824
                $this->modified_tables[$function] = true;
825
            }
826
        }
827
    }
828
829
    /**
830
     * Fills the modified tables array based on the PBX settings data, the called class, and the record ID.
831
     *
832
     * @param string $called_class The called class.
833
     * @param string $recordId The record ID.
834
     * @return void
835
     */
836
    private function fillModifiedTablesFromPbxSettingsData(string $called_class, string $recordId): void
837
    {
838
        // Check if the called class is not PbxSettings
839
        if (PbxSettings::class !== $called_class) {
840
            return;
841
        }
842
843
        // Clear cache for PbxSettings
844
        PbxSettings::clearCache(PbxSettings::class);
845
846
        // Find the PbxSettings record
847
        /** @var PbxSettings $pbxSettings */
848
        $pbxSettings = PbxSettings::findFirstByKey($recordId);
849
        if ($pbxSettings === null) {
850
            return;
851
        }
852
        $settingName = $pbxSettings->key;
853
854
        // Iterate through the PBX settings dependency table and update the modified tables array
855
        foreach ($this->pbxSettingsDependencyTable as $data) {
856
            $additionalConditions = (isset($data['strPosKey']) && strpos($settingName, $data['strPosKey']) !== false);
857
858
            // Check additional conditions and the setting name
859
            if (!$additionalConditions && !in_array($settingName, $data['settingName'], true)) {
860
                continue;
861
            }
862
863
            // Update the modified tables array for each function
864
            foreach ($data['functions'] as $function) {
865
                $this->modified_tables[$function] = true;
866
            }
867
        }
868
    }
869
870
    /**
871
     * Fills the modified tables array based on the PBX extension modules data, the called class, and the record ID.
872
     *
873
     * @param string $called_class The called class.
874
     * @param string $recordId The record ID.
875
     * @return void
876
     */
877
    private function fillModifiedTablesFromPbxExtensionModules(string $called_class, string $recordId): void
878
    {
879
        // Check if the called class is not PbxExtensionModules
880
        if (PbxExtensionModules::class !== $called_class) {
881
            return;
882
        }
883
884
        // Find the module settings record
885
        $moduleSettings = PbxExtensionModules::findFirstById($recordId);
886
        if ($moduleSettings !== null) {
887
888
            // Invoke the action for the PBX module state with the module settings data
889
            self::invokeAction(self::R_PBX_MODULE_STATE, $moduleSettings->toArray(), 50);
890
        }
891
    }
892
893
    /**
894
     * Invokes an action by publishing a job to the Beanstalk queue.
895
     *
896
     * @param string $action The action to invoke.
897
     * @param array $parameters The parameters for the action.
898
     * @param int $priority The priority of the job.
899
     * @return void
900
     */
901
    public static function invokeAction(string $action, array $parameters = [], int $priority = 0): void
902
    {
903
        $di = Di::getDefault();
904
        if (!$di) {
905
            return;
906
        }
907
        /** @var BeanstalkClient $queue */
908
        $queue = $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
909
910
        // Prepare the job data
911
        $jobData = json_encode(
912
            [
913
                'source' => BeanstalkConnectionModelsProvider::SOURCE_INVOKE_ACTION,
914
                'action' => $action,
915
                'parameters' => $parameters,
916
                'model' => ''
917
            ]
918
        );
919
920
        // Publish the job to the Beanstalk queue
921
        $queue->publish(
922
            $jobData,
923
            self::class,
924
            $priority,
925
            PheanstalkInterface::DEFAULT_DELAY,
926
            3600
927
        );
928
    }
929
930
    /**
931
     * Restarts gnats queue server daemon
932
     */
933
    public function reloadNats(): void
934
    {
935
        $natsConf = new NatsConf();
936
        $natsConf->reStart();
937
    }
938
939
    /**
940
     * Reloads Asterisk dialplan
941
     */
942
    public function reloadDialplan(): void
943
    {
944
        PBX::dialplanReload();
945
    }
946
947
    /**
948
     * Reloads res_parking
949
     * @return void
950
     */
951
    public function reloadParking(): void
952
    {
953
        $parkingConf = new ResParkingConf();
954
        $parkingConf->generateConfig();
955
        $asteriskPath = Util::which('asterisk');
956
        Processes::mwExec("$asteriskPath -rx 'module reload res_parking'", $arr_out);
957
    }
958
959
    /**
960
     * Reloads Asterisk manager interface module
961
     */
962
    public function reloadManager(): void
963
    {
964
        PBX::managerReload();
965
    }
966
967
    /**
968
     * Generates queue.conf and restart asterisk queue module
969
     */
970
    public function reloadQueues(): void
971
    {
972
        QueueConf::queueReload();
973
    }
974
975
    /**
976
     * Updates custom changes in config files
977
     */
978
    public function updateCustomFiles(): void
979
    {
980
        System::updateCustomFiles();
981
    }
982
983
    /**
984
     * Applies iptables settings and restart firewall
985
     */
986
    public function reloadFirewall(): void
987
    {
988
        IptablesConf::updateFirewallRules();
989
        IptablesConf::reloadFirewall();
990
    }
991
992
    public function pbxCoreReload(): void
993
    {
994
        PBX::coreRestart();
995
    }
996
997
    /**
998
     *  Refreshes networks configs and restarts network daemon
999
     */
1000
    public function networkReload(): void
1001
    {
1002
        System::networkReload();
1003
    }
1004
1005
    /**
1006
     * Refreshes IAX configs and reload iax2 module
1007
     */
1008
    public function reloadIax(): void
1009
    {
1010
        PBX::iaxReload();
1011
    }
1012
1013
    /**
1014
     * Reloads MOH file list in Asterisk.
1015
     */
1016
    public function reloadMoh(): void
1017
    {
1018
        PBX::mohReload();
1019
    }
1020
1021
    /**
1022
     * Refreshes SIP configs and reload PJSIP module
1023
     */
1024
    public function reloadSip(): void
1025
    {
1026
        PBX::sipReload();
1027
    }
1028
1029
    /**
1030
     * Update RTP config file.
1031
     */
1032
    public function rtpReload(): void
1033
    {
1034
        PBX::rtpReload();
1035
    }
1036
1037
    /**
1038
     *  Refreshes features configs and reload features module
1039
     */
1040
    public function reloadFeatures(): void
1041
    {
1042
        PBX::featuresReload();
1043
    }
1044
1045
    /**
1046
     * Restarts CROND daemon
1047
     */
1048
    public function reloadCron(): void
1049
    {
1050
        $cron = new CronConf();
1051
        $cron->reStart();
1052
    }
1053
1054
    /**
1055
     * Restarts NTP daemon
1056
     */
1057
    public function reloadNtp(): void
1058
    {
1059
        NTPConf::configure();
1060
    }
1061
1062
    /**
1063
     * Update record save period
1064
     */
1065
    public function updateRecordSavePeriod(): void
1066
    {
1067
        PBX::updateSavePeriod();
1068
    }
1069
1070
    /**
1071
     * Restarts Nginx daemon
1072
     */
1073
    public function reloadNginx(): void
1074
    {
1075
        $nginxConf = new NginxConf();
1076
        $nginxConf->generateConf();
1077
        $nginxConf->reStart();
1078
    }
1079
1080
    /**
1081
     * Applies modules locations changes and restarts Nginx daemon
1082
     */
1083
    public function reloadNginxConf(): void
1084
    {
1085
        $nginxConf = new NginxConf();
1086
        $nginxConf->generateModulesConfigs();
1087
        $nginxConf->reStart();
1088
    }
1089
1090
    /**
1091
     * Restarts Fail2Ban daemon
1092
     */
1093
    public function reloadFail2BanConf(): void
1094
    {
1095
        Fail2BanConf::reloadFail2ban();
1096
    }
1097
1098
    /**
1099
     * Restarts PHP-FPM daemon
1100
     */
1101
    public function reloadPHPFPM(): void
1102
    {
1103
        PHPConf::reStart();
1104
    }
1105
1106
    /**
1107
     * Configures SSH settings
1108
     */
1109
    public function reloadSSH(): void
1110
    {
1111
        $sshConf = new SSHConf();
1112
        $sshConf->configure();
1113
    }
1114
1115
    /**
1116
     * Reconfigures TomeZone settings
1117
     */
1118
    public function updateTomeZone(): void
1119
    {
1120
        System::timezoneConfigure();
1121
    }
1122
1123
    /**
1124
     * Restarts rsyslog daemon
1125
     */
1126
    public function restartSyslogD(): void
1127
    {
1128
        $syslogConf = new SyslogConf();
1129
        $syslogConf->reStart();
1130
    }
1131
1132
    /**
1133
     *  Reloads Asterisk voicemail module
1134
     */
1135
    public function reloadVoicemail(): void
1136
    {
1137
        PBX::voicemailReload();
1138
    }
1139
1140
    /**
1141
     *  Reloads WorkerApiCommands worker
1142
     */
1143
    public function reloadRestAPIWorker(): void
1144
    {
1145
        Processes::processPHPWorker(WorkerApiCommands::class);
1146
    }
1147
1148
    /**
1149
     *  Reloads WorkerCallEvents worker
1150
     */
1151
    public function reloadWorkerCallEvents(): void
1152
    {
1153
        Processes::processPHPWorker(WorkerCallEvents::class);
1154
    }
1155
1156
    /**
1157
     * Process after PBXExtension state changes
1158
     *
1159
     * @param array $pbxModuleRecord
1160
     */
1161
    public function afterModuleStateChanged(array $pbxModuleRecord): void
1162
    {
1163
        // Recreate modules array
1164
        PBXConfModulesProvider::recreateModulesProvider();
1165
1166
        // Recreate database connections
1167
        ModulesDBConnectionsProvider::recreateModulesDBConnections();
1168
1169
        // Hook module methods if they change system configs
1170
        $className = str_replace('Module', '', $pbxModuleRecord['uniqid']);
1171
        $configClassName = "Modules\\{$pbxModuleRecord['uniqid']}\\Lib\\{$className}Conf";
1172
        if (class_exists($configClassName)) {
1173
            $configClassObj = new $configClassName();
1174
1175
            // Reconfigure fail2ban and restart iptables
1176
            if (method_exists($configClassObj, SystemConfigInterface::GENERATE_FAIL2BAN_JAILS)
1177
                && !empty(call_user_func([$configClassObj, SystemConfigInterface::GENERATE_FAIL2BAN_JAILS]))) {
1178
                $this->modified_tables[self::R_FAIL2BAN_CONF] = true;
1179
            }
1180
1181
            // Refresh Nginx conf if module has any locations
1182
            if (method_exists($configClassObj, SystemConfigInterface::CREATE_NGINX_LOCATIONS)
1183
                && !empty(call_user_func([$configClassObj, SystemConfigInterface::CREATE_NGINX_LOCATIONS]))) {
1184
                $this->modified_tables[self::R_NGINX_CONF] = true;
1185
            }
1186
1187
            // Refresh crontab rules if module has any for it
1188
            if (method_exists($configClassObj, SystemConfigInterface::CREATE_CRON_TASKS)) {
1189
                $tasks = [];
1190
                call_user_func_array([$configClassObj, SystemConfigInterface::CREATE_CRON_TASKS], [&$tasks]);
1191
                if (!empty($tasks)) {
1192
                    $this->modified_tables[self::R_CRON] = true;
1193
                }
1194
            }
1195
1196
            // Reconfigure asterisk manager interface
1197
            if (method_exists($configClassObj, AsteriskConfigInterface::GENERATE_MANAGER_CONF)
1198
                && !empty(call_user_func([$configClassObj, AsteriskConfigInterface::GENERATE_MANAGER_CONF]))) {
1199
                $this->modified_tables[self::R_MANAGERS] = true;
1200
            }
1201
1202
            // Hook modules AFTER_ methods
1203
            if ($pbxModuleRecord['disabled'] === '1'
1204
                && method_exists($configClassObj, SystemConfigInterface::ON_AFTER_MODULE_DISABLE)) {
1205
                call_user_func([$configClassObj, SystemConfigInterface::ON_AFTER_MODULE_DISABLE]);
1206
            } elseif ($pbxModuleRecord['disabled'] === '0'
1207
                && method_exists($configClassObj, SystemConfigInterface::ON_AFTER_MODULE_ENABLE)) {
1208
                call_user_func([$configClassObj, SystemConfigInterface::ON_AFTER_MODULE_ENABLE]);
1209
            }
1210
        }
1211
    }
1212
1213
    /**
1214
     * Cleanup advices cache
1215
     * @return void
1216
     */
1217
    private function cleanupAdvicesCache(): void
1218
    {
1219
        WorkerPrepareAdvices::afterChangePBXSettings();
1220
    }
1221
1222
    /**
1223
     * Configure the sentry error logger
1224
     * @return void
1225
     */
1226
    private function reloadSentry(): void
1227
    {
1228
        $sentryConf = new SentryConf();
1229
        $sentryConf->configure();
1230
    }
1231
}
1232
1233
/**
1234
 * The start point
1235
 */
1236
WorkerModelsEvents::startWorker($argv ?? []);