Passed
Pull Request — master (#23)
by Nikolay
09:42 queued 03:08
created

SIPConf::getTechnology()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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, 10 2020
7
 */
8
9
namespace MikoPBX\Core\Asterisk\Configs;
10
11
use MikoPBX\Common\Models\{Codecs,
12
    ExtensionForwardingRights,
13
    Extensions,
14
    NetworkFilters,
15
    OutgoingRoutingTable,
16
    PbxSettings,
17
    Sip,
18
    Users};
19
use MikoPBX\Core\Asterisk\AstDB;
20
use MikoPBX\Modules\Config\ConfigClass;
21
use MikoPBX\Core\System\{MikoPBXConfig, Network, Util};
22
use MikoPBX\Core\Utilities\SubnetCalculator;
23
use Phalcon\Di;
24
25
class SIPConf extends ConfigClass
26
{
27
    public const TYPE_PJSIP = 'PJSIP';
28
29
    protected $data_peers;
30
    protected $data_providers;
31
    protected $data_rout;
32
    protected string $technology;
33
    protected array  $contexts_data;
34
35
    protected string $description = 'pjsip.conf';
36
37
    /**
38
     *
39
     * @return array
40
     */
41
    public function dependenceModels(): array
42
    {
43
        return [Sip::class, Users::class];
44
    }
45
46
    /**
47
     * Генератор sip.conf
48
     *
49
     * @return bool|void
50
     */
51
    protected function generateConfigProtected(): void
52
    {
53
        $conf = '';
54
        $conf .= $this->generateGeneralPj();
55
        $conf .= $this->generateProvidersPj();
56
        $conf .= $this->generatePeersPj();
57
58
        Util::fileWriteContent($this->config->path('asterisk.astetcdir') . '/pjsip.conf', $conf);
59
        $pjConf = '[log_mappings]'."\n".
60
            'type=log_mappings'."\n".
61
            'asterisk_error = 0'."\n".
62
            'asterisk_warning = 2'."\n".
63
            'asterisk_debug = 1,3,4,5,6'."\n\n";
64
65
        file_put_contents($this->config->path('asterisk.astetcdir') . '/pjproject.conf', $pjConf);
66
        file_put_contents($this->config->path('asterisk.astetcdir') . '/sorcery.conf', '');
67
68
        $db = new AstDB();
69
        foreach ($this->data_peers as $peer) {
70
            // Помещаем в AstDB сведения по маршуртизации.
71
            $ringlength = ($peer['ringlength'] === '0') ? '' : trim($peer['ringlength']);
72
            $db->databasePut('FW_TIME', $peer['extension'], $ringlength);
73
            $db->databasePut('FW', $peer['extension'], trim($peer['forwarding']));
74
            $db->databasePut('FW_BUSY', $peer['extension'], trim($peer['forwardingonbusy']));
75
            $db->databasePut('FW_UNAV', $peer['extension'], trim($peer['forwardingonunavailable']));
76
        }
77
    }
78
79
    /**
80
     * Генератора секции general pjsip.conf
81
     *
82
     *
83
     * @return string
84
     */
85
    private function generateGeneralPj(): string
86
    {
87
        $lang    = $this->generalSettings['PBXLanguage'];
88
        [$topology, $extipaddr, $exthostname, $subnets] = $this->getTopologyData();
89
90
        $codecs = $this->getCodecs();
91
        $codecConf = '';
92
        foreach ($codecs as $codec){
93
            $codecConf.= "allow = {$codec}\n";
94
        }
95
96
        $pbxVersion = PbxSettings::getValueByKey('PBXVersion');
97
        $natConf = '';
98
        if ($topology === 'private') {
99
            foreach ($subnets as $net) {
100
                $natConf .= "local_net={$net}\n";
101
            }
102
            if ( ! empty($exthostname)) {
103
                $parts = explode(':', $exthostname);
104
                $natConf  .= 'external_media_address=' . $parts[0] . "\n";
105
                $natConf  .= 'external_signaling_address=' . $parts[0] . "\n";
106
                $natConf  .= 'external_signaling_port=' . ($parts[1] ?? '5060');
107
            } elseif ( ! empty($extipaddr)) {
108
                $parts = explode(':', $extipaddr);
109
                $natConf  .= 'external_media_address=' . $parts[0] . "\n";
110
                $natConf  .= 'external_signaling_address=' . $parts[0] . "\n";
111
                $natConf  .= 'external_signaling_port=' . ($parts[1] ?? '5060');
112
            }
113
        }
114
115
        $conf = "[general] \n" .
116
            "disable_multi_domain=on\n" .
117
            "transport = udp \n\n" .
118
119
            "[global] \n" .
120
            "type = global\n" .
121
            "endpoint_identifier_order=username,ip,anonymous\n" .
122
            "user_agent = mikopbx-{$pbxVersion}\n\n" .
123
124
            "[anonymous]\n" .
125
            "type = endpoint\n" .
126
            $codecConf.
127
            "language={$lang}\n".
128
            "timers = no\n" .
129
            "context = public-direct-dial\n\n".
130
131
            "[transport-udp]\n" .
132
            "type = transport\n" .
133
            "protocol = udp\n" .
134
            "bind=0.0.0.0:{$this->generalSettings['SIPPort']}\n".
135
            "{$natConf}\n\n".
136
137
            "[transport-tcp]\n" .
138
            "type = transport\n" .
139
            "protocol = tcp\n" .
140
            "bind=0.0.0.0:{$this->generalSettings['SIPPort']}\n".
141
            "{$natConf}\n\n".
142
            '';
143
144
        $varEtcDir = $this->config->path('core.varEtcDir');
145
        file_put_contents($varEtcDir . '/topology_hash', md5($topology . $exthostname . $extipaddr. $this->generalSettings['SIPPort']));
146
        $conf .= "\n";
147
148
        return $conf;
149
    }
150
151
    /**
152
     * Проверка ключевых параметров.
153
     * Если параметры изменены, то необходим рестарт Asterisk процесса.
154
     * @return bool
155
     */
156
    public function needAsteriskRestart():bool{
157
        $di     = Di::getDefault();
158
        if ($di === null) {
159
            return false;
160
        }
161
        $mikoPBXConfig  = new MikoPBXConfig();
162
        [$topology, $extipaddr, $exthostname] = $this->getTopologyData();
163
164
        $generalSettings = $mikoPBXConfig->getGeneralSettings();
165
        $now_hadh = md5($topology . $exthostname . $extipaddr. $generalSettings['SIPPort']);
166
        $old_hash   = '';
167
        $varEtcDir = $di->getShared('config')->path('core.varEtcDir');
168
        if (file_exists($varEtcDir . '/topology_hash')) {
169
            $old_hash = file_get_contents($varEtcDir . '/topology_hash');
170
        }
171
172
        return $old_hash !== $now_hadh;
173
    }
174
175
    /**
176
     * @return array
177
     */
178
    private function getTopologyData():array{
179
        $network = new Network();
180
181
        $topology    = 'public';
182
        $extipaddr   = '';
183
        $exthostname = '';
184
        $networks    = $network->getEnabledLanInterfaces();
185
        $subnets     = [];
186
        foreach ($networks as $if_data) {
187
            $lan_config = $network->getInterface($if_data['interface']);
188
            if (empty($lan_config['ipaddr']) || empty($lan_config['subnet'])) {
189
                continue;
190
            }
191
            try {
192
                $sub = new SubnetCalculator($lan_config['ipaddr'], $lan_config['subnet']);
193
            }catch (\Throwable $e){
194
                Util::sysLogMsg(self::class, $e->getMessage());
195
                continue;
196
            }
197
            $net = $sub->getNetworkPortion() . '/' . $lan_config['subnet'];
198
            if ($if_data['topology'] === 'private' && in_array($net, $subnets, true) === false) {
199
                $subnets[] = $net;
200
            }
201
            if (trim($if_data['internet']) === '1') {
202
                $topology    = trim($if_data['topology']);
203
                $extipaddr   = trim($if_data['extipaddr']);
204
                $exthostname = trim($if_data['exthostname']);
205
            }
206
        }
207
208
        $networks = NetworkFilters::find('local_network=1');
209
        foreach ($networks as $net) {
210
            if (in_array($net->permit, $subnets, true) === false) {
211
                $subnets[] = $net->permit;
212
            }
213
        }
214
215
        return array(
216
            $topology,
217
            $extipaddr,
218
            $exthostname,
219
            $subnets,
220
        );
221
222
    }
223
224
    /**
225
     * Генератор секции провайдеров в sip.conf
226
     *
227
     *
228
     * @return string
229
     */
230
    private function generateProvidersPj(): string
231
    {
232
        $conf        = '';
233
        $reg_strings = '';
234
        $prov_config = '';
235
        if ($this->data_providers===null){
236
            $this->getSettings();
237
        }
238
        $additionalModules = $this->di->getShared('pbxConfModules');
239
        foreach ($this->data_providers as $provider) {
240
            $manual_attributes = Util::parseIniSettings(base64_decode($provider['manualattributes'] ?? ''));
241
            $provider['port']  = (trim($provider['port']) === '') ? '5060' : $provider['port'];
242
243
            $reg_strings .= $this->generateProviderRegistrationAuth($provider, $additionalModules, $manual_attributes);
244
            $reg_strings .= $this->generateProviderRegistration($provider, $additionalModules, $manual_attributes);
245
            $prov_config .= $this->generateProviderOutAuth($provider, $additionalModules, $manual_attributes);
246
247
            $prov_config .= $this->generateProviderAor($provider, $additionalModules, $manual_attributes);
248
            $prov_config .= $this->generateProviderIdentify($provider, $additionalModules, $manual_attributes);
249
            $prov_config .= $this->generateProviderEndpoint($provider, $additionalModules, $manual_attributes);
250
        }
251
252
        $conf .= $reg_strings;
253
        $conf .= $prov_config;
254
        return $conf;
255
    }
256
257
    /**
258
     * Генерация Auth для секции Registration провайдера.
259
     * @param array $provider
260
     * @param array $additionalModules
261
     * @param array $manual_attributes
262
     * @return string
263
     */
264
    private function generateProviderRegistrationAuth(array $provider, array $additionalModules, array $manual_attributes): string{
265
        $conf = '';
266
        if($provider['noregister'] === '1'){
267
            return $conf;
268
        }
269
        $options     = [
270
            'type'     => 'registration-auth',
271
            'username' => $provider['username'],
272
            'password' => $provider['secret'],
273
        ];
274
        foreach ($additionalModules as $Object) {
275
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
276
        }
277
        $options['type'] = 'auth';
278
        $conf .= "[REG-AUTH-{$provider['uniqid']}]\n";
279
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'registration-auth');
280
        return $conf;
281
    }
282
283
    /**
284
     * Генерация Registration провайдера.
285
     * @param array $provider
286
     * @param array $additionalModules
287
     * @param array $manual_attributes
288
     * @return string
289
     */
290
    private function generateProviderRegistration(array $provider, array $additionalModules, array $manual_attributes): string{
291
        $conf = '';
292
        if($provider['noregister'] === '1'){
293
            return $conf;
294
        }
295
        $options     = [
296
            'type'                        => 'registration',
297
            'outbound_auth'               => "REG-AUTH-{$provider['uniqid']}",
298
            'contact_user'                => $provider['username'],
299
            'retry_interval'              => '30',
300
            'max_retries'                 => '100',
301
            'forbidden_retry_interval'    => '300',
302
            'fatal_retry_interval'        => '300',
303
            'expiration'                  => $this->generalSettings['SIPDefaultExpiry'],
304
            'server_uri'                  => "sip:{$provider['host']}:{$provider['port']}",
305
            'client_uri'                  => "sip:{$provider['username']}@{$provider['host']}:{$provider['port']}",
306
        ];
307
        foreach ($additionalModules as $Object) {
308
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
309
        }
310
        $conf .= "[REG-{$provider['uniqid']}] \n";
311
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'registration');
312
        return $conf;
313
    }
314
315
    /**
316
     * Генерация Auth провайдера.
317
     * @param array $provider
318
     * @param array $additionalModules
319
     * @param array $manual_attributes
320
     * @return string
321
     */
322
    private function generateProviderOutAuth(array $provider, array $additionalModules, array $manual_attributes): string{
323
        $conf = '';
324
        if ('1' === $provider['receive_calls_without_auth']) {
325
            return $conf;
326
        }
327
        $options     = [
328
            'type'     => 'endpoint-auth',
329
            'username' => $provider['username'],
330
            'password' => $provider['secret'],
331
        ];
332
        foreach ($additionalModules as $Object) {
333
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
334
        }
335
        $options['type'] = 'auth';
336
        $conf .= "[{$provider['uniqid']}-OUT]\n";
337
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'endpoint-auth');
338
        return $conf;
339
    }
340
341
    /**
342
     * Генерация AOR для Endpoint.
343
     * @param array $provider
344
     * @param array $additionalModules
345
     * @param array $manual_attributes
346
     * @return string
347
     */
348
    private function generateProviderAor(array $provider, array $additionalModules, array $manual_attributes): string{
349
        $conf = '';
350
        $defaultuser = (trim($provider['defaultuser']) === '') ? $provider['username'] : $provider['defaultuser'];
351
        if ( ! empty($defaultuser) && '1' !== $provider['receive_calls_without_auth']) {
352
            $contact = "sip:$defaultuser@{$provider['host']}:{$provider['port']}";
353
        } else {
354
            $contact = "sip:{$provider['host']}:{$provider['port']}";
355
        }
356
        $options     = [
357
            'type'               => 'aor',
358
            'max_contacts'       => '1',
359
            'contact'            => $contact,
360
            'maximum_expiration' => $this->generalSettings['SIPMaxExpiry'],
361
            'minimum_expiration' => $this->generalSettings['SIPMinExpiry'],
362
            'default_expiration' => $this->generalSettings['SIPDefaultExpiry'],
363
        ];
364
        if($provider['qualify'] === '1'){
365
            $options['qualify_frequency'] = $provider['qualifyfreq'];
366
            $options['qualify_timeout']   = '3.0';
367
        }
368
        foreach ($additionalModules as $Object) {
369
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
370
        }
371
        $conf .= "[{$provider['uniqid']}]\n";
372
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'aor');
373
        return $conf;
374
    }
375
376
    /**
377
     * Генерация Endpoint провайдера.
378
     * @param array $provider
379
     * @param array $additionalModules
380
     * @param array $manual_attributes
381
     * @return string
382
     */
383
    private function generateProviderEndpoint(array $provider, array $additionalModules, array $manual_attributes): string{
384
        $conf = '';
385
        $fromdomain = (trim($provider['fromdomain']) === '') ? $provider['host'] : $provider['fromdomain'];
386
        $from       = (trim(
387
                $provider['fromuser']
388
            ) === '') ? "{$provider['username']}; username" : "{$provider['fromuser']}; fromuser";
389
        $from_user  = ($provider['disablefromuser'] === '1') ? null : $from;
390
        $language   = $this->generalSettings['PBXLanguage'];
391
392
        if (count($this->contexts_data[$provider['context_id']]) === 1) {
393
            $context_id = $provider['uniqid'];
394
        } else {
395
            $context_id = $provider['context_id'];
396
        }
397
        $dtmfmode = ($provider['dtmfmode'] === 'rfc2833') ? 'rfc4733' : $provider['dtmfmode'];
398
        $options  = [
399
            'type'            => 'endpoint',
400
            '100rel'          => "no",
401
            'context'         => "{$context_id}-incoming",
402
            'dtmf_mode'       => $dtmfmode,
403
            'disallow'        => 'all',
404
            'allow'           => $provider['codecs'],
405
            'rtp_symmetric'   => 'yes',
406
            'force_rport'     => 'yes',
407
            'rewrite_contact' => 'yes',
408
            'ice_support'     => 'no',
409
            'direct_media'    => 'no',
410
            'from_user'       => $from_user,
411
            'from_domain'     => $fromdomain,
412
            'sdp_session'     => 'mikopbx',
413
            'language'        => $language,
414
            'aors'            => $provider['uniqid'],
415
            'timers'          => ' no',
416
        ];
417
        if ('1' !== $provider['receive_calls_without_auth']) {
418
            $options['outbound_auth'] = "{$provider['uniqid']}-OUT";
419
        }
420
        self::getToneZone($options, $language);
421
        foreach ($additionalModules as $Object) {
422
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
423
        }
424
        $conf .= "[{$provider['uniqid']}]\n";
425
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'endpoint');
426
        return $conf;
427
    }
428
429
430
    /**
431
     * Генерация AOR для Endpoint.
432
     * @param array $provider
433
     * @param array $additionalModules
434
     * @param array $manual_attributes
435
     * @return string
436
     */
437
    private function generateProviderIdentify(array $provider, array $additionalModules, array $manual_attributes): string{
438
        $conf = '';
439
        $options     = [
440
            'type'     => 'identify',
441
            'endpoint' => $provider['uniqid'],
442
            'match'    => $provider['host'],
443
        ];
444
        foreach ($additionalModules as $Object) {
445
            $options = $Object->overridePJSIPOptions($provider['uniqid'], $options);
446
        }
447
        $conf .= "[{$provider['uniqid']}]\n";
448
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'identify');
449
        return $conf;
450
    }
451
452
453
    /**
454
     * @param array  $options
455
     * @param string $lang
456
     */
457
    public static function getToneZone(array &$options, string $lang):void {
458
        $settings = [
459
            'ru-ru' => 'ru',
460
            'en-gb' => 'uk',
461
            'de-de' => 'de',
462
            'da-dk' => 'dk',
463
            'es-es' => 'es',
464
            'fr-ca' => 'fr',
465
            'it-it' => 'it',
466
            'ja-jp' => 'jp',
467
            'nl-nl' => 'nl',
468
            'pl-pl' => 'pl',
469
            'pt-br' => 'pt',
470
        ];
471
        $toneZone = $settings[$lang]??'';
472
        if(!empty($toneZone)){
473
            $options['inband_progress'] = 'yes';
474
            $options['tone_zone'] = $toneZone;
475
        }
476
    }
477
478
    /**
479
     * Генератор сеции пиров для sip.conf
480
     *
481
     *
482
     * @return string
483
     */
484
    public function generatePeersPj(): string
485
    {
486
        if ($this->data_peers===null){
487
            $this->getSettings();
488
        }
489
        $lang              = $this->generalSettings['PBXLanguage'];
490
        $additionalModules = $this->di->getShared('pbxConfModules');
491
        $conf              = '';
492
493
        foreach ($this->data_peers as $peer) {
494
            $manual_attributes = Util::parseIniSettings($peer['manualattributes'] ?? '');
495
            $conf .= $this->generatePeerAuth($peer, $additionalModules, $manual_attributes);
496
            $conf .= $this->generatePeerAor($peer, $additionalModules, $manual_attributes);
497
            $conf .= $this->generatePeerEndpoint($lang, $peer, $additionalModules, $manual_attributes);
498
        }
499
500
        $conf .= $this->generatePeersAdditional($additionalModules);
501
502
        return $conf;
503
    }
504
505
    /**
506
     * Генерация AOR для Endpoint.
507
     * @param       $additionalModules
508
     * @return string
509
     */
510
    private function generatePeersAdditional($additionalModules): string{
511
        $conf = '';
512
        foreach ($additionalModules as $Object) {
513
            // Prevent cycling, skip current class
514
            if (is_a($Object, __CLASS__)) {
515
                continue;
516
            }
517
            $conf .= $Object->generatePeersPj();
518
        }
519
        return $conf;
520
    }
521
522
    /**
523
     * Генерация AOR для Endpoint.
524
     * @param       $peer
525
     * @param       $additionalModules
526
     * @param array $manual_attributes
527
     * @return string
528
     */
529
    private function generatePeerAuth($peer, $additionalModules, array $manual_attributes): string{
530
        $conf = '';
531
        $options = [
532
            'type'     => 'auth',
533
            'username' => $peer['extension'],
534
            'password' => $peer['secret'],
535
        ];
536
        foreach ($additionalModules as $Object) {
537
            $options = $Object->overridePJSIPOptions($peer['extension'], $options);
538
        }
539
        $conf    .= "[{$peer['extension']}] \n";
540
        $conf    .= Util::overrideConfigurationArray($options, $manual_attributes, 'auth');
541
        return $conf;
542
    }
543
544
    /**
545
     * Генерация AOR для Endpoint.
546
     * @param       $peer
547
     * @param       $additionalModules
548
     * @param array $manual_attributes
549
     * @return string
550
     */
551
    private function generatePeerAor($peer, $additionalModules, array $manual_attributes): string{
552
        $conf = '';
553
        $options = [
554
            'type'              => 'aor',
555
            'qualify_frequency' => '60',
556
            'qualify_timeout'   => '5',
557
            'max_contacts'      => '5',
558
        ];
559
        foreach ($additionalModules as $Object) {
560
            $options = $Object->overridePJSIPOptions($peer['extension'], $options);
561
        }
562
        $conf .= "[{$peer['extension']}] \n";
563
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'aor');
564
        return $conf;
565
    }
566
567
    /**
568
     * Генерация endpoint.
569
     * @param        $lang
570
     * @param        $peer
571
     * @param        $additionalModules
572
     * @param array  $manual_attributes
573
     * @return string
574
     */
575
    private function generatePeerEndpoint($lang, $peer, $additionalModules, array $manual_attributes): string{
576
        $conf = '';
577
        $language = str_replace('_', '-', strtolower($lang));
578
        $language = (trim($language) === '') ? 'en-en' : $language;
579
580
        $calleridname = (trim($peer['calleridname']) === '') ? $peer['extension'] : $peer['calleridname'];
581
        $busylevel = (trim($peer['busylevel']) === '') ? '1' : '' . $peer['busylevel'];
582
583
        $dtmfmode = ($peer['dtmfmode'] === 'rfc2833') ? 'rfc4733' : $peer['dtmfmode'];
584
        $options = [
585
            'type' => 'endpoint',
586
            'context' => 'all_peers',
587
            'dtmf_mode' => $dtmfmode,
588
            'disallow' => 'all',
589
            'allow' => $peer['codecs'],
590
            'rtp_symmetric' => 'yes',
591
            'force_rport' => 'yes',
592
            'rewrite_contact' => 'yes',
593
            'ice_support' => 'no',
594
            'direct_media' => 'no',
595
            'callerid' => "{$calleridname} <{$peer['extension']}>",
596
            'send_pai' => 'yes',
597
            'call_group' => '1',
598
            'pickup_group' => '1',
599
            'sdp_session' => 'mikopbx',
600
            'language' => $language,
601
            'mailboxes' => 'admin@voicemailcontext',
602
            'device_state_busy_at' => $busylevel,
603
            'aors' => $peer['extension'],
604
            'auth' => $peer['extension'],
605
            'outbound_auth' => $peer['extension'],
606
            'acl' => "acl_{$peer['extension']}",
607
            'timers' => ' no',
608
        ];
609
        self::getToneZone($options, $language);
610
        foreach ($additionalModules as $Object) {
611
            $options = $Object->overridePJSIPOptions($peer['extension'], $options);
612
        }
613
        $conf .= "[{$peer['extension']}] \n";
614
        $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'endpoint');
615
        foreach ($additionalModules as $Object) {
616
            $conf .= $Object->generatePeerPjAdditionalOptions($peer);
617
        }
618
        return $conf;
619
    }
620
621
622
    /**
623
     * Получение настроек.
624
     */
625
    public function getSettings(): void
626
    {
627
        $this->contexts_data  = [];
628
        // Настройки для текущего класса.
629
        $this->data_peers     = $this->getPeers();
630
        $this->data_providers = $this->getProviders();
631
        $this->data_rout      = $this->getOutRoutes();
632
        $this->technology     = self::getTechnology();
633
    }
634
635
    /**
636
     * Получение данных по SIP пирам.
637
     *
638
     * @return array
639
     */
640
    private function getPeers(): array
641
    {
642
        /** @var NetworkFilters $network_filter */
643
        /** @var Sip $sip_peer */
644
        /** @var Extensions $extension */
645
        /** @var Users $user */
646
        /** @var ExtensionForwardingRights $extensionForwarding */
647
648
        $data    = [];
649
        $db_data = Sip::find("type = 'peer' AND ( disabled <> '1')");
650
        foreach ($db_data as $sip_peer) {
651
            $arr_data       = $sip_peer->toArray();
652
            $network_filter = null;
653
            if (!empty($sip_peer->networkfilterid)) {
654
                $network_filter = NetworkFilters::findFirst($sip_peer->networkfilterid);
655
            }
656
            $arr_data['permit'] = ($network_filter === null) ? '' : $network_filter->permit;
657
            $arr_data['deny']   = ($network_filter === null) ? '' : $network_filter->deny;
658
659
            // Получим используемые кодеки.
660
            $arr_data['codecs'] = $this->getCodecs();
661
662
            // Имя сотрудника.
663
            $extension = Extensions::findFirst("number = '{$sip_peer->extension}'");
664
            if (null === $extension) {
665
                $arr_data['publicaccess'] = false;
666
                $arr_data['language']     = '';
667
                $arr_data['calleridname'] = $sip_peer->extension;
668
            } else {
669
                $arr_data['publicaccess'] = $extension->public_access;
670
                $arr_data['calleridname'] = $extension->callerid;
671
                $user                     = Users::findFirst($extension->userid);
672
                if (null !== $user) {
673
                    $arr_data['language'] = $user->language;
674
                    $arr_data['user_id']  = $user->id;
675
                }
676
            }
677
            $extensionForwarding = ExtensionForwardingRights::findFirst("extension = '{$sip_peer->extension}'");
678
            if (null === $extensionForwarding) {
679
                $arr_data['ringlength']              = '';
680
                $arr_data['forwarding']              = '';
681
                $arr_data['forwardingonbusy']        = '';
682
                $arr_data['forwardingonunavailable'] = '';
683
            } else {
684
                $arr_data['ringlength']              = $extensionForwarding->ringlength;
685
                $arr_data['forwarding']              = $extensionForwarding->forwarding;
686
                $arr_data['forwardingonbusy']        = $extensionForwarding->forwardingonbusy;
687
                $arr_data['forwardingonunavailable'] = $extensionForwarding->forwardingonunavailable;
688
            }
689
            $data[] = $arr_data;
690
        }
691
692
        return $data;
693
    }
694
695
    /**
696
     * Возвращает доступные пиру кодеки.
697
     *
698
     * @return array
699
     */
700
    private function getCodecs(): array
701
    {
702
        $arr_codecs = [];
703
        $filter     = [
704
            'conditions'=>'disabled="0"',
705
            'order' => 'type, priority',
706
        ];
707
        $codecs     = Codecs::find($filter);
708
        foreach ($codecs as $codec_data) {
709
            $arr_codecs[] = $codec_data->name;
710
        }
711
712
        return $arr_codecs;
713
    }
714
715
    /**
716
     * Получение данных по SIP провайдерам.
717
     *
718
     * @return array
719
     */
720
    private function getProviders(): array
721
    {
722
        /** @var Sip $sip_peer */
723
        /** @var NetworkFilters $network_filter */
724
        // Получим настройки всех аккаунтов.
725
        $data    = [];
726
        $db_data = Sip::find("type = 'friend' AND ( disabled <> '1')");
727
        foreach ($db_data as $sip_peer) {
728
            $arr_data                               = $sip_peer->toArray();
729
            $arr_data['receive_calls_without_auth'] = $sip_peer->receive_calls_without_auth;
730
            $network_filter                         = NetworkFilters::findFirst($sip_peer->networkfilterid);
731
            $arr_data['permit']                     = ($network_filter === null) ? '' : $network_filter->permit;
732
            $arr_data['deny']                       = ($network_filter === null) ? '' : $network_filter->deny;
733
734
            // Получим используемые кодеки.
735
            $arr_data['codecs'] = $this->getCodecs();
736
737
            $context_id = preg_replace("/[^a-z\d]/iu", '', $sip_peer->host . $sip_peer->port);
738
            if ( ! isset($this->contexts_data[$context_id])) {
739
                $this->contexts_data[$context_id] = [];
740
            }
741
            $this->contexts_data[$context_id][$sip_peer->uniqid] = $sip_peer->username;
742
743
            $arr_data['context_id'] = $context_id;
744
            $data[]                 = $arr_data;
745
        }
746
747
        return $data;
748
    }
749
750
    /**
751
     * Генератор исходящих контекстов для пиров.
752
     *
753
     * @return array
754
     */
755
    private function getOutRoutes(): array
756
    {
757
        if ($this->data_peers===null){
758
            $this->getSettings();
759
        }
760
        /** @var OutgoingRoutingTable $rout */
761
        /** @var OutgoingRoutingTable $routs */
762
        /** @var Sip $db_data */
763
        /** @var Sip $sip_peer */
764
765
        $data    = [];
766
        $routs   = OutgoingRoutingTable::find(['order' => 'priority']);
767
        $db_data = Sip::find("type = 'friend' AND ( disabled <> '1')");
768
        foreach ($routs as $rout) {
769
            foreach ($db_data as $sip_peer) {
770
                if ($sip_peer->uniqid !== $rout->providerid) {
771
                    continue;
772
                }
773
                $arr_data                = $rout->toArray();
774
                $arr_data['description'] = $sip_peer->description;
775
                $arr_data['uniqid']      = $sip_peer->uniqid;
776
                $data[]                  = $arr_data;
777
            }
778
        }
779
780
        return $data;
781
    }
782
783
784
785
    /**
786
     * Генератор extension для контекста peers.
787
     *
788
     * @return string
789
     */
790
    public function extensionGenContexts(): string
791
    {
792
        if ($this->data_peers===null){
793
            $this->getSettings();
794
        }
795
        // Генерация внутреннего номерного плана.
796
        $conf = '';
797
798
        foreach ($this->data_peers as $peer) {
799
            $conf .= "[peer_{$peer['extension']}] \n";
800
            $conf .= "include => internal \n";
801
            $conf .= "include => outgoing \n";
802
        }
803
804
        $contexts = [];
805
        // Входящие контексты.
806
        foreach ($this->data_providers as $provider) {
807
            $contexts_data = $this->contexts_data[$provider['context_id']];
808
            if (count($contexts_data) === 1) {
809
                $conf .= ExtensionsConf::generateIncomingContextPeers($provider['uniqid'], $provider['username'], '');
810
            } elseif ( ! in_array($provider['context_id'], $contexts, true)) {
811
                $conf       .= ExtensionsConf::generateIncomingContextPeers(
812
                    $contexts_data,
813
                    null,
814
                    $provider['context_id']
815
                );
816
                $contexts[] = $provider['context_id'];
817
            }
818
        }
819
820
        return $conf;
821
    }
822
823
    /**
824
     * Генерация хинтов.
825
     *
826
     * @return string
827
     */
828
    public function extensionGenHints(): string
829
    {
830
        if ($this->data_peers===null){
831
            $this->getSettings();
832
        }
833
        $conf = '';
834
        foreach ($this->data_peers as $peer) {
835
            $conf .= "exten => {$peer['extension']},hint,{$this->technology}/{$peer['extension']} \n";
836
        }
837
838
        return $conf;
839
    }
840
841
    public function extensionGenInternal(): string
842
    {
843
        if ($this->data_peers===null){
844
            $this->getSettings();
845
        }
846
        // Генерация внутреннего номерного плана.
847
        $conf = '';
848
        foreach ($this->data_peers as $peer) {
849
            $conf .= "exten => {$peer['extension']},1,Goto(internal-users,{$peer['extension']},1) \n";
850
        }
851
        $conf .= "\n";
852
853
        return $conf;
854
    }
855
856
    public function extensionGenInternalTransfer(): string
857
    {
858
        if ($this->data_peers===null){
859
            $this->getSettings();
860
        }
861
        // Генерация внутреннего номерного плана.
862
        $conf = '';
863
        foreach ($this->data_peers as $peer) {
864
            $conf .= "exten => {$peer['extension']},1,Set(__ISTRANSFER=transfer_) \n";
865
            $conf .= "	same => n,Goto(internal-users,{$peer['extension']},1) \n";
866
        }
867
        $conf .= "\n";
868
869
        return $conf;
870
    }
871
872
    /**
873
     * Returns PJSIP ot SIP uses at PBX
874
     *
875
     * @return string
876
     */
877
    public static function getTechnology(): string
878
    {
879
        return self::TYPE_PJSIP;
880
    }
881
882
}