Passed
Push — develop ( 8c504f...3c95f7 )
by Портнов
04:25
created

SIPConf::getSipHosts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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