Total Complexity | 124 |
Total Lines | 1046 |
Duplicated Lines | 0 % |
Changes | 13 | ||
Bugs | 0 | Features | 0 |
Complex classes like SIPConf often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SIPConf, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class SIPConf extends CoreConfigClass |
||
41 | { |
||
42 | public const TYPE_PJSIP = 'PJSIP'; |
||
43 | private const TOPOLOGY_HASH_FILE = '/topology_hash'; |
||
44 | |||
45 | protected $data_peers; |
||
46 | protected $data_providers; |
||
47 | protected $data_rout; |
||
48 | protected array $dataSipHosts; |
||
49 | |||
50 | protected string $technology; |
||
51 | protected array $contexts_data; |
||
52 | |||
53 | protected string $description = 'pjsip.conf'; |
||
54 | |||
55 | /** |
||
56 | * |
||
57 | * @return array |
||
58 | */ |
||
59 | public function getDependenceModels(): array |
||
60 | { |
||
61 | return [Sip::class, Users::class, SipHosts::class]; |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * Проверка ключевых параметров. |
||
66 | * Если параметры изменены, то необходим рестарт Asterisk процесса. |
||
67 | * |
||
68 | * @return bool |
||
69 | */ |
||
70 | public function needAsteriskRestart(): bool |
||
71 | { |
||
72 | $di = Di::getDefault(); |
||
73 | if ($di === null) { |
||
74 | return false; |
||
75 | } |
||
76 | $mikoPBXConfig = new MikoPBXConfig(); |
||
77 | [$topology, $extIpAddress, $externalHostName, $subnets] = $this->getTopologyData(); |
||
78 | |||
79 | $generalSettings = $mikoPBXConfig->getGeneralSettings(); |
||
80 | $now_hash = md5($topology . $externalHostName . $extIpAddress . $generalSettings['SIPPort']. $generalSettings['TLS_PORT'] . implode('',$subnets)); |
||
81 | $old_hash = ''; |
||
82 | $varEtcDir = $di->getShared('config')->path('core.varEtcDir'); |
||
83 | if (file_exists($varEtcDir . self::TOPOLOGY_HASH_FILE)) { |
||
84 | $old_hash = file_get_contents($varEtcDir . self::TOPOLOGY_HASH_FILE); |
||
85 | } |
||
86 | |||
87 | return $old_hash !== $now_hash; |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * @return array |
||
92 | */ |
||
93 | private function getTopologyData(): array |
||
94 | { |
||
95 | $network = new Network(); |
||
96 | |||
97 | $topology = 'public'; |
||
98 | $extipaddr = ''; |
||
99 | $exthostname = ''; |
||
100 | $networks = $network->getEnabledLanInterfaces(); |
||
101 | $subnets = ['127.0.0.1/32']; |
||
102 | foreach ($networks as $if_data) { |
||
103 | $lan_config = $network->getInterface($if_data['interface']); |
||
104 | if (empty($lan_config['ipaddr']) || empty($lan_config['subnet'])) { |
||
105 | continue; |
||
106 | } |
||
107 | try { |
||
108 | $sub = new SubnetCalculator($lan_config['ipaddr'], $lan_config['subnet']); |
||
109 | } catch (Throwable $e) { |
||
110 | Util::sysLogMsg(self::class, $e->getMessage(), LOG_ERR); |
||
111 | continue; |
||
112 | } |
||
113 | $net = $sub->getNetworkPortion() . '/' . $lan_config['subnet']; |
||
114 | if ($if_data['topology'] === 'private' && in_array($net, $subnets, true) === false) { |
||
115 | $subnets[] = $net; |
||
116 | } |
||
117 | if (trim($if_data['internet']) === '1') { |
||
118 | $topology = trim($if_data['topology']); |
||
119 | $extipaddr = trim($if_data['extipaddr']); |
||
120 | $exthostname = trim($if_data['exthostname']); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | $networks = NetworkFilters::find('local_network=1'); |
||
125 | foreach ($networks as $net) { |
||
126 | if (in_array($net->permit, $subnets, true) === false) { |
||
127 | $subnets[] = $net->permit; |
||
128 | } |
||
129 | } |
||
130 | |||
131 | return [ |
||
132 | $topology, |
||
133 | $extipaddr, |
||
134 | $exthostname, |
||
135 | $subnets, |
||
136 | ]; |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Генератор extension для контекста peers. |
||
141 | * |
||
142 | * @return string |
||
143 | */ |
||
144 | public function extensionGenContexts(): string |
||
145 | { |
||
146 | if ($this->data_peers === null) { |
||
147 | $this->getSettings(); |
||
148 | } |
||
149 | // Генерация внутреннего номерного плана. |
||
150 | $conf = ''; |
||
151 | |||
152 | $contexts = []; |
||
153 | // Входящие контексты. |
||
154 | foreach ($this->data_providers as $provider) { |
||
155 | $contextsData = $this->contexts_data[$provider['context_id']]; |
||
156 | if (count($contextsData) === 1) { |
||
157 | $conf .= IncomingContexts::generate($provider['uniqid'], $provider['username']); |
||
158 | } elseif ( ! in_array($provider['context_id'], $contexts, true)) { |
||
159 | $conf .= IncomingContexts::generate( |
||
160 | $contextsData, |
||
161 | null, |
||
162 | $provider['context_id'] |
||
163 | ); |
||
164 | $contexts[] = $provider['context_id']; |
||
165 | } |
||
166 | } |
||
167 | |||
168 | return $conf; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Получение настроек. |
||
173 | */ |
||
174 | public function getSettings(): void |
||
175 | { |
||
176 | $this->contexts_data = []; |
||
177 | // Настройки для текущего класса. |
||
178 | $this->data_peers = $this->getPeers(); |
||
179 | $this->data_providers = $this->getProviders(); |
||
180 | $this->data_rout = $this->getOutRoutes(); |
||
181 | $this->technology = self::getTechnology(); |
||
182 | $this->dataSipHosts = self::getSipHosts(); |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Получение данных по SIP пирам. |
||
187 | * |
||
188 | * @return array |
||
189 | */ |
||
190 | private function getPeers(): array |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Возвращает доступные пиру кодеки. |
||
247 | * |
||
248 | * @return array |
||
249 | */ |
||
250 | private function getCodecs(): array |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Получение данных по SIP провайдерам. |
||
267 | * |
||
268 | * @return array |
||
269 | */ |
||
270 | private function getProviders(): array |
||
271 | { |
||
272 | /** @var Sip $sip_peer */ |
||
273 | /** @var NetworkFilters $network_filter */ |
||
274 | // Получим настройки всех аккаунтов. |
||
275 | $data = []; |
||
276 | $db_data = Sip::find("type = 'friend' AND ( disabled <> '1')"); |
||
277 | foreach ($db_data as $sip_peer) { |
||
278 | $arr_data = $sip_peer->toArray(); |
||
279 | $network_filter = NetworkFilters::findFirst($sip_peer->networkfilterid); |
||
280 | $arr_data['permit'] = ($network_filter === null) ? '' : $network_filter->permit; |
||
281 | $arr_data['deny'] = ($network_filter === null) ? '' : $network_filter->deny; |
||
282 | // Получим используемые кодеки. |
||
283 | $arr_data['codecs'] = $this->getCodecs(); |
||
284 | $context_id = self::getContextId($sip_peer->host.$sip_peer->port); |
||
285 | if ( ! isset($this->contexts_data[$context_id])) { |
||
286 | $this->contexts_data[$context_id] = []; |
||
287 | } |
||
288 | $this->contexts_data[$context_id][$sip_peer->uniqid] = $sip_peer->username; |
||
289 | $arr_data['context_id'] = $context_id; |
||
290 | if(empty($arr_data['registration_type'])){ |
||
291 | if($sip_peer->noregister === '0'){ |
||
292 | $arr_data['registration_type'] = Sip::REG_TYPE_OUTBOUND; |
||
293 | }else{ |
||
294 | $arr_data['registration_type'] = Sip::REG_TYPE_NONE; |
||
295 | } |
||
296 | } |
||
297 | $arr_data['port'] = (trim($arr_data['port']) === '') ? '5060' : $arr_data['port']; |
||
298 | $data[] = $arr_data; |
||
299 | } |
||
300 | |||
301 | return $data; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Генератор исходящих контекстов для пиров. |
||
306 | * |
||
307 | * @return array |
||
308 | */ |
||
309 | private function getOutRoutes(): array |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * Returns PJSIP ot SIP uses at PBX |
||
339 | * |
||
340 | * @return string |
||
341 | */ |
||
342 | public static function getTechnology(): string |
||
343 | { |
||
344 | return self::TYPE_PJSIP; |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Возвращает массив хостов. |
||
349 | * |
||
350 | * @return array |
||
351 | */ |
||
352 | public static function getSipHosts(): array |
||
353 | { |
||
354 | $dataSipHosts = []; |
||
355 | /** @var SipHosts $sipHosts */ |
||
356 | /** @var SipHosts $hostData */ |
||
357 | $sipHosts = SipHosts::find(); |
||
358 | foreach ($sipHosts as $hostData) { |
||
359 | if ( ! isset($dataSipHosts[$hostData->provider_id])) { |
||
360 | $dataSipHosts[$hostData->provider_id] = []; |
||
361 | } |
||
362 | $dataSipHosts[$hostData->provider_id][] = str_replace(PHP_EOL, '', $hostData->address); |
||
363 | } |
||
364 | |||
365 | return $dataSipHosts; |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * Генерация хинтов. |
||
370 | * |
||
371 | * @return string |
||
372 | */ |
||
373 | public function extensionGenHints(): string |
||
374 | { |
||
375 | if ($this->data_peers === null) { |
||
376 | $this->getSettings(); |
||
377 | } |
||
378 | $conf = ''; |
||
379 | foreach ($this->data_peers as $peer) { |
||
380 | $hint = "{$this->technology}/{$peer['extension']}"; |
||
381 | if($this->generalSettings['UseWebRTC'] === '1') { |
||
382 | $hint.="&{$this->technology}/{$peer['extension']}-WS"; |
||
383 | } |
||
384 | $conf .= "exten => {$peer['extension']},hint,$hint&Custom:{$peer['extension']} \n"; |
||
385 | } |
||
386 | return $conf; |
||
387 | } |
||
388 | |||
389 | public function extensionGenInternal(): string |
||
390 | { |
||
391 | if ($this->data_peers === null) { |
||
392 | $this->getSettings(); |
||
393 | } |
||
394 | // Генерация внутреннего номерного плана. |
||
395 | $conf = ''; |
||
396 | foreach ($this->data_peers as $peer) { |
||
397 | $conf .= "exten => {$peer['extension']},1,Goto(internal-users,{$peer['extension']},1) \n"; |
||
398 | } |
||
399 | $conf .= "\n"; |
||
400 | |||
401 | return $conf; |
||
402 | } |
||
403 | |||
404 | public function extensionGenInternalTransfer(): string |
||
405 | { |
||
406 | if ($this->data_peers === null) { |
||
407 | $this->getSettings(); |
||
408 | } |
||
409 | // Генерация внутреннего номерного плана. |
||
410 | $conf = ''; |
||
411 | foreach ($this->data_peers as $peer) { |
||
412 | $conf .= "exten => {$peer['extension']},1,Set(__ISTRANSFER=transfer_) \n"; |
||
413 | $conf .= " same => n,Goto(internal-users,{$peer['extension']},1) \n"; |
||
414 | } |
||
415 | $conf .= "\n"; |
||
416 | |||
417 | return $conf; |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * Генератор sip.conf |
||
422 | * |
||
423 | * @return void |
||
424 | */ |
||
425 | protected function generateConfigProtected(): void |
||
426 | { |
||
427 | $conf = $this->generateGeneralPj(); |
||
428 | $conf .= $this->generateProvidersPj(); |
||
429 | $conf .= $this->generatePeersPj(); |
||
430 | |||
431 | $astEtcDir = $this->config->path('asterisk.astetcdir'); |
||
432 | |||
433 | Util::fileWriteContent($astEtcDir . '/pjsip.conf', $conf); |
||
434 | $pjConf = '[log_mappings]' . "\n" . |
||
435 | 'type=log_mappings' . "\n" . |
||
436 | 'asterisk_error = 0' . "\n" . |
||
437 | 'asterisk_warning = 2' . "\n" . |
||
438 | 'asterisk_debug = 1,3,4,5,6' . "\n\n"; |
||
439 | file_put_contents($astEtcDir.'/pjproject.conf', $pjConf); |
||
440 | file_put_contents($astEtcDir.'/sorcery.conf', ''); |
||
441 | $this->updateAsteriskDatabase(); |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * Обновление маршрутизации в AstDB |
||
446 | * @return bool |
||
447 | */ |
||
448 | public function updateAsteriskDatabase():bool |
||
449 | { |
||
450 | if ($this->data_peers === null) { |
||
451 | $this->getSettings(); |
||
452 | } |
||
453 | $warError = false; |
||
454 | $db = new AstDB(); |
||
455 | foreach ($this->data_peers as $peer) { |
||
456 | // Помещаем в AstDB сведения по маршуртизации. |
||
457 | $ringLength = ($peer['ringlength'] === '0') ? '' : trim($peer['ringlength']); |
||
458 | $warError |= !$db->databasePut('FW_TIME', $peer['extension'], $ringLength); |
||
459 | $warError |= !$db->databasePut('FW', $peer['extension'], trim($peer['forwarding'])); |
||
460 | $warError |= !$db->databasePut('FW_BUSY', $peer['extension'], trim($peer['forwardingonbusy'])); |
||
461 | $warError |= !$db->databasePut('FW_UNAV', $peer['extension'], trim($peer['forwardingonunavailable'])); |
||
462 | } |
||
463 | |||
464 | return !$warError; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Генератора секции general pjsip.conf |
||
469 | * |
||
470 | * |
||
471 | * @return string |
||
472 | */ |
||
473 | private function generateGeneralPj(): string |
||
474 | { |
||
475 | $lang = $this->generalSettings['PBXLanguage']; |
||
476 | [$topology, $extIpAddress, $externalHostName, $subnets] = $this->getTopologyData(); |
||
477 | |||
478 | $codecs = $this->getCodecs(); |
||
479 | $codecConf = ''; |
||
480 | foreach ($codecs as $codec) { |
||
481 | $codecConf .= "allow = $codec\n"; |
||
482 | } |
||
483 | $pbxVersion = $this->generalSettings['PBXVersion']; |
||
484 | $sipPort = $this->generalSettings['SIPPort']; |
||
485 | $tlsPort = $this->generalSettings['TLS_PORT']; |
||
486 | $natConf = ''; |
||
487 | $tlsNatConf = ''; |
||
488 | |||
489 | $resolveOk = Processes::mwExec("timeout 1 getent hosts '$externalHostName'") === 0; |
||
490 | if(!empty($externalHostName) && !$resolveOk){ |
||
491 | Util::sysLogMsg('DNS', "ERROR: DNS $externalHostName not resolved, It will not be used in SIP signaling."); |
||
492 | } |
||
493 | if ($topology === 'private') { |
||
494 | foreach ($subnets as $net) { |
||
495 | $natConf .= "local_net=$net\n"; |
||
496 | } |
||
497 | if ( !empty($externalHostName) && $resolveOk ) { |
||
498 | $parts = explode(':', $externalHostName); |
||
499 | $natConf .= 'external_media_address=' . $parts[0] . "\n"; |
||
500 | $natConf .= 'external_signaling_address=' . $parts[0] . "\n"; |
||
501 | $tlsNatConf = "{$natConf}external_signaling_port=$tlsPort"; |
||
502 | $natConf .= 'external_signaling_port=' . ($parts[1] ?? $sipPort); |
||
503 | } elseif ( ! empty($extIpAddress)) { |
||
504 | $parts = explode(':', $extIpAddress); |
||
505 | $natConf .= 'external_media_address=' . $parts[0] . "\n"; |
||
506 | $natConf .= 'external_signaling_address=' . $parts[0] . "\n"; |
||
507 | $tlsNatConf = "{$natConf}external_signaling_port=$tlsPort"; |
||
508 | $natConf .= 'external_signaling_port=' . ($parts[1] ?? $sipPort); |
||
509 | } |
||
510 | } |
||
511 | |||
512 | $typeTransport = 'type = transport'; |
||
513 | $conf = "[global] \n" . |
||
514 | "type = global\n" . |
||
515 | "disable_multi_domain=yes\n" . |
||
516 | "endpoint_identifier_order=username,ip,anonymous\n" . |
||
517 | "user_agent = mikopbx-$pbxVersion\n\n" . |
||
518 | |||
519 | "[transport-udp]\n" . |
||
520 | "$typeTransport\n" . |
||
521 | "protocol = udp\n" . |
||
522 | "bind=0.0.0.0:{$this->generalSettings['SIPPort']}\n" . |
||
523 | "$natConf\n\n" . |
||
524 | |||
525 | "[transport-tcp]\n" . |
||
526 | "$typeTransport\n" . |
||
527 | "protocol = tcp\n" . |
||
528 | "bind=0.0.0.0:{$this->generalSettings['SIPPort']}\n" . |
||
529 | "$natConf\n\n" . |
||
530 | |||
531 | "[transport-tls]\n" . |
||
532 | "$typeTransport\n" . |
||
533 | "protocol = tls\n" . |
||
534 | "bind=0.0.0.0:{$tlsPort}\n" . |
||
535 | "cert_file=/etc/asterisk/keys/ajam.pem\n" . |
||
536 | "priv_key_file=/etc/asterisk/keys/ajam.pem\n" . |
||
537 | // "ca_list_file=/etc/ssl/certs/ca-certificates.crt\n". |
||
538 | // "verify_server=yes\n". |
||
539 | "method=sslv23\n" . |
||
540 | "$tlsNatConf\n\n" . |
||
541 | |||
542 | "[transport-wss]\n" . |
||
543 | "$typeTransport\n" . |
||
544 | "protocol = wss\n" . |
||
545 | "bind=0.0.0.0:{$this->generalSettings['SIPPort']}\n" . |
||
546 | "$natConf\n\n"; |
||
547 | |||
548 | $allowGuestCalls = PbxSettings::getValueByKey('PBXAllowGuestCalls'); |
||
549 | if ($allowGuestCalls === '1') { |
||
550 | $conf .= "[anonymous]\n" . |
||
551 | "type = endpoint\n" . |
||
552 | $codecConf . |
||
553 | "language=$lang\n" . |
||
554 | "timers = no\n" . |
||
555 | "context = public-direct-dial\n\n"; |
||
556 | } |
||
557 | |||
558 | $varEtcDir = $this->config->path('core.varEtcDir'); |
||
559 | $hash = md5($topology . $externalHostName . $extIpAddress . $this->generalSettings['SIPPort']. $this->generalSettings['TLS_PORT'] . implode('',$subnets)); |
||
560 | file_put_contents($varEtcDir.self::TOPOLOGY_HASH_FILE, $hash); |
||
561 | $conf .= "\n"; |
||
562 | return $conf; |
||
563 | } |
||
564 | |||
565 | /** |
||
566 | * Генератор секции провайдеров в sip.conf |
||
567 | * |
||
568 | * |
||
569 | * @return string |
||
570 | */ |
||
571 | private function generateProvidersPj(): string |
||
572 | { |
||
573 | $conf = ''; |
||
574 | $reg_strings = ''; |
||
575 | $prov_config = ''; |
||
576 | if ($this->data_providers === null) { |
||
577 | $this->getSettings(); |
||
578 | } |
||
579 | foreach ($this->data_providers as $provider) { |
||
580 | $manual_attributes = Util::parseIniSettings(base64_decode($provider['manualattributes'] ?? '')); |
||
581 | |||
582 | if($provider['registration_type'] === Sip::REG_TYPE_OUTBOUND){ |
||
583 | $reg_strings .= $this->generateProviderRegistrationAuth($provider, $manual_attributes); |
||
584 | $reg_strings .= $this->generateProviderRegistration($provider, $manual_attributes); |
||
585 | } |
||
586 | if($provider['registration_type'] !== Sip::REG_TYPE_NONE){ |
||
587 | $prov_config .= $this->generateProviderAuth($provider, $manual_attributes); |
||
588 | } |
||
589 | if($provider['registration_type'] !== Sip::REG_TYPE_INBOUND) { |
||
590 | $prov_config .= $this->generateProviderIdentify($provider, $manual_attributes); |
||
591 | } |
||
592 | $prov_config .= $this->generateProviderAor($provider, $manual_attributes); |
||
593 | $prov_config .= $this->generateProviderEndpoint($provider, $manual_attributes); |
||
594 | } |
||
595 | |||
596 | $conf .= $reg_strings; |
||
597 | $conf .= $prov_config; |
||
598 | |||
599 | return $conf; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Генерация Auth для секции Registration провайдера. |
||
604 | * |
||
605 | * @param array $provider |
||
606 | * @param array $manual_attributes |
||
607 | * |
||
608 | * @return string |
||
609 | */ |
||
610 | private function generateProviderRegistrationAuth(array $provider, array $manual_attributes): string { |
||
611 | $conf = ''; |
||
612 | $options = [ |
||
613 | 'type' => 'registration-auth', |
||
614 | 'username' => $provider['username'], |
||
615 | 'password' => $provider['secret'], |
||
616 | ]; |
||
617 | $options = $this->overridePJSIPOptionsFromModules( |
||
618 | $provider['uniqid'], |
||
619 | $options, |
||
620 | CoreConfigClass::OVERRIDE_PROVIDER_PJSIP_OPTIONS |
||
621 | ); |
||
622 | $options['type'] = 'auth'; |
||
623 | $conf .= "[REG-AUTH-{$provider['uniqid']}]\n"; |
||
624 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'registration-auth'); |
||
625 | return $conf; |
||
626 | } |
||
627 | |||
628 | /** |
||
629 | * Calls an overridePJSIPOptions function from additional modules |
||
630 | * |
||
631 | * @param $extensionOrId |
||
632 | * @param $options |
||
633 | * @param $method |
||
634 | * |
||
635 | * @return array |
||
636 | */ |
||
637 | private function overridePJSIPOptionsFromModules($extensionOrId, $options, $method): array |
||
638 | { |
||
639 | $configClassObj = new ConfigClass(); |
||
640 | $modulesOverridingArrays = $configClassObj->hookModulesMethodWithArrayResult($method, [$extensionOrId, $options]); |
||
641 | foreach ($modulesOverridingArrays as $newOptionsSet) { |
||
642 | // How to make some order of overrides? |
||
643 | $options = $newOptionsSet; |
||
644 | } |
||
645 | return $options; |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * Генерация Registration провайдера. |
||
650 | * |
||
651 | * @param array $provider |
||
652 | * @param array $manual_attributes |
||
653 | * |
||
654 | * @return string |
||
655 | */ |
||
656 | private function generateProviderRegistration(array $provider, array $manual_attributes): string { |
||
657 | $conf = ''; |
||
658 | $options = [ |
||
659 | 'type' => 'registration', |
||
660 | 'outbound_auth' => "REG-AUTH-{$provider['uniqid']}", |
||
661 | 'contact_user' => $provider['username'], |
||
662 | 'retry_interval' => '30', |
||
663 | 'max_retries' => '100', |
||
664 | 'forbidden_retry_interval' => '300', |
||
665 | 'fatal_retry_interval' => '300', |
||
666 | 'expiration' => $this->generalSettings['SIPDefaultExpiry'], |
||
667 | 'server_uri' => "sip:{$provider['host']}:{$provider['port']}", |
||
668 | 'client_uri' => "sip:{$provider['username']}@{$provider['host']}:{$provider['port']}", |
||
669 | ]; |
||
670 | |||
671 | if(!empty($provider['transport'])){ |
||
672 | $options['transport'] = "transport-{$provider['transport']}"; |
||
673 | } |
||
674 | if(!empty($provider['outbound_proxy'])){ |
||
675 | $options['outbound_proxy'] = "sip:{$provider['outbound_proxy']}\;lr"; |
||
676 | } |
||
677 | $options = $this->overridePJSIPOptionsFromModules( |
||
678 | $provider['uniqid'], |
||
679 | $options, |
||
680 | CoreConfigClass::OVERRIDE_PROVIDER_PJSIP_OPTIONS |
||
681 | ); |
||
682 | $conf .= "[REG-{$provider['uniqid']}] \n"; |
||
683 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'registration'); |
||
684 | |||
685 | return $conf; |
||
686 | } |
||
687 | |||
688 | /** |
||
689 | * Генерация Auth провайдера. |
||
690 | * |
||
691 | * @param array $provider |
||
692 | * @param array $manual_attributes |
||
693 | * |
||
694 | * @return string |
||
695 | */ |
||
696 | private function generateProviderAuth(array $provider, array $manual_attributes): string { |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Генерация AOR для Endpoint. |
||
717 | * |
||
718 | * @param array $provider |
||
719 | * @param array $manual_attributes |
||
720 | * |
||
721 | * @return string |
||
722 | */ |
||
723 | private function generateProviderAor(array $provider, array $manual_attributes): string |
||
724 | { |
||
725 | $conf = ''; |
||
726 | $contact = ''; |
||
727 | if($provider['registration_type'] === Sip::REG_TYPE_OUTBOUND){ |
||
728 | $contact = "sip:{$provider['username']}@{$provider['host']}:{$provider['port']}"; |
||
729 | }elseif($provider['registration_type'] === Sip::REG_TYPE_NONE) { |
||
730 | $contact = "sip:{$provider['host']}:{$provider['port']}"; |
||
731 | } |
||
732 | $options = [ |
||
733 | 'type' => 'aor', |
||
734 | 'max_contacts' => '1', |
||
735 | 'maximum_expiration' => $this->generalSettings['SIPMaxExpiry'], |
||
736 | 'minimum_expiration' => $this->generalSettings['SIPMinExpiry'], |
||
737 | 'default_expiration' => $this->generalSettings['SIPDefaultExpiry'], |
||
738 | ]; |
||
739 | if(!empty($contact)){ |
||
740 | $options['contact'] = $contact; |
||
741 | } |
||
742 | |||
743 | if ($provider['qualify'] === '1') { |
||
744 | $options['qualify_frequency'] = $provider['qualifyfreq']; |
||
745 | $options['qualify_timeout'] = '3.0'; |
||
746 | } |
||
747 | if(!empty($provider['outbound_proxy'])){ |
||
748 | $options['outbound_proxy'] = "sip:{$provider['outbound_proxy']}\;lr"; |
||
749 | } |
||
750 | $options = $this->overridePJSIPOptionsFromModules( |
||
751 | $provider['uniqid'], |
||
752 | $options, |
||
753 | CoreConfigClass::OVERRIDE_PROVIDER_PJSIP_OPTIONS |
||
754 | ); |
||
755 | $conf .= "[{$provider['uniqid']}]\n"; |
||
756 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'aor'); |
||
757 | |||
758 | return $conf; |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * Генерация AOR для Endpoint. |
||
763 | * |
||
764 | * @param array $provider |
||
765 | * @param array $manual_attributes |
||
766 | * |
||
767 | * @return string |
||
768 | */ |
||
769 | private function generateProviderIdentify(array $provider, array $manual_attributes): string { |
||
792 | } |
||
793 | |||
794 | /** |
||
795 | * Генерация Endpoint провайдера. |
||
796 | * |
||
797 | * @param array $provider |
||
798 | * @param array $manual_attributes |
||
799 | * |
||
800 | * @return string |
||
801 | */ |
||
802 | private function generateProviderEndpoint(array $provider, array $manual_attributes): string { |
||
803 | $conf = ''; |
||
804 | $fromdomain = (trim($provider['fromdomain']) === '') ? $provider['host'] : $provider['fromdomain']; |
||
805 | $from = (trim($provider['fromuser']) === '') ? "{$provider['username']}; username" : "{$provider['fromuser']}; fromuser"; |
||
806 | |||
807 | if($provider['disablefromuser'] === '1'){ |
||
808 | $from_user = null; |
||
809 | $contactUser = trim($provider['username']??''); |
||
810 | }else{ |
||
811 | $from_user = $from; |
||
812 | $contactUser = $from; |
||
813 | } |
||
814 | |||
815 | $language = $this->generalSettings['PBXLanguage']; |
||
816 | |||
817 | if (count($this->contexts_data[$provider['context_id']]) === 1) { |
||
818 | $context_id = $provider['uniqid']; |
||
819 | } else { |
||
820 | $context_id = $provider['context_id']; |
||
821 | } |
||
822 | $dtmfmode = ($provider['dtmfmode'] === 'rfc2833') ? 'rfc4733' : $provider['dtmfmode']; |
||
823 | $options = [ |
||
824 | 'type' => 'endpoint', |
||
825 | '100rel' => "no", |
||
826 | 'context' => "{$context_id}-incoming", |
||
827 | 'dtmf_mode' => $dtmfmode, |
||
828 | 'disallow' => 'all', |
||
829 | 'allow' => $provider['codecs'], |
||
830 | 'rtp_symmetric' => 'yes', |
||
831 | 'force_rport' => 'yes', |
||
832 | 'rewrite_contact' => 'yes', |
||
833 | 'ice_support' => 'no', |
||
834 | 'direct_media' => 'no', |
||
835 | 'from_user' => $from_user, |
||
836 | 'from_domain' => $fromdomain, |
||
837 | 'contact_user' => $contactUser, |
||
838 | 'sdp_session' => 'mikopbx', |
||
839 | 'language' => $language, |
||
840 | 'aors' => $provider['uniqid'], |
||
841 | 'timers' => ' no', |
||
842 | ]; |
||
843 | |||
844 | if(!empty($provider['transport'])){ |
||
845 | $options['transport'] = "transport-{$provider['transport']}"; |
||
846 | if($provider['transport'] === Sip::TRANSPORT_TLS){ |
||
847 | $options['media_encryption'] = 'sdes'; |
||
848 | } |
||
849 | } |
||
850 | if(!empty($provider['outbound_proxy'])){ |
||
851 | $options['outbound_proxy'] = "sip:{$provider['outbound_proxy']}\;lr"; |
||
852 | } |
||
853 | if ($provider['registration_type'] === Sip::REG_TYPE_OUTBOUND) { |
||
854 | $options['outbound_auth'] = "{$provider['uniqid']}-AUTH"; |
||
855 | }elseif ($provider['registration_type'] === Sip::REG_TYPE_INBOUND){ |
||
856 | $options['auth'] = "{$provider['uniqid']}-AUTH"; |
||
857 | } |
||
858 | self::getToneZone($options, $language); |
||
859 | $options = $this->overridePJSIPOptionsFromModules( |
||
860 | $provider['uniqid'], |
||
861 | $options, |
||
862 | CoreConfigClass::OVERRIDE_PROVIDER_PJSIP_OPTIONS |
||
863 | ); |
||
864 | $conf .= "[{$provider['uniqid']}]".PHP_EOL; |
||
865 | $conf .= 'set_var=contextID='.$provider['context_id'].PHP_EOL; |
||
866 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'endpoint'); |
||
867 | |||
868 | return $conf; |
||
869 | } |
||
870 | |||
871 | /** |
||
872 | * Возвращает имя контекста без спецсимволовю |
||
873 | * @param $name |
||
874 | * @return string |
||
875 | */ |
||
876 | public static function getContextId(string $name = ''):string |
||
877 | { |
||
878 | return preg_replace("/[^a-z\d]/iu", '', $name).'-incoming'; |
||
879 | } |
||
880 | |||
881 | /** |
||
882 | * @param array $options |
||
883 | * @param string $lang |
||
884 | */ |
||
885 | public static function getToneZone(array &$options, string $lang): void |
||
905 | } |
||
906 | } |
||
907 | |||
908 | /** |
||
909 | * Генератор сеции пиров для sip.conf |
||
910 | * |
||
911 | * |
||
912 | * @return string |
||
913 | */ |
||
914 | public function generatePeersPj(): string |
||
915 | { |
||
916 | if ($this->data_peers === null) { |
||
917 | $this->getSettings(); |
||
918 | } |
||
919 | $lang = $this->generalSettings['PBXLanguage']; |
||
920 | $conf = ''; |
||
921 | |||
922 | foreach ($this->data_peers as $peer) { |
||
923 | $manual_attributes = Util::parseIniSettings($peer['manualattributes'] ?? ''); |
||
924 | $conf .= $this->generatePeerAuth($peer, $manual_attributes); |
||
925 | $conf .= $this->generatePeerAor($peer, $manual_attributes); |
||
926 | $conf .= $this->generatePeerEndpoint($lang, $peer, $manual_attributes); |
||
927 | } |
||
928 | |||
929 | $conf .= $this->hookModulesMethod(CoreConfigClass::GENERATE_PEERS_PJ); |
||
930 | |||
931 | return $conf; |
||
932 | } |
||
933 | |||
934 | /** |
||
935 | * Генерация AOR для Endpoint. |
||
936 | * |
||
937 | * @param array $peer |
||
938 | * @param array $manual_attributes |
||
939 | * |
||
940 | * @return string |
||
941 | */ |
||
942 | private function generatePeerAuth(array $peer, array $manual_attributes): string |
||
943 | { |
||
944 | $conf = ''; |
||
945 | $options = [ |
||
946 | 'type' => 'auth', |
||
947 | 'username' => $peer['extension'], |
||
948 | 'password' => $peer['secret'], |
||
949 | ]; |
||
950 | $options = $this->overridePJSIPOptionsFromModules( |
||
951 | $peer['extension'], |
||
952 | $options, |
||
953 | CoreConfigClass::OVERRIDE_PJSIP_OPTIONS |
||
954 | ); |
||
955 | $conf .= "[{$peer['extension']}] \n"; |
||
956 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'auth'); |
||
957 | |||
958 | return $conf; |
||
959 | } |
||
960 | |||
961 | /** |
||
962 | * Генерация AOR для Endpoint. |
||
963 | * |
||
964 | * @param array $peer |
||
965 | * @param array $manual_attributes |
||
966 | * |
||
967 | * @return string |
||
968 | */ |
||
969 | private function generatePeerAor(array $peer, array $manual_attributes): string |
||
970 | { |
||
971 | $conf = ''; |
||
972 | $options = [ |
||
973 | 'type' => 'aor', |
||
974 | 'qualify_frequency' => '60', |
||
975 | 'qualify_timeout' => '5', |
||
976 | 'max_contacts' => '5', |
||
977 | ]; |
||
978 | $options = $this->overridePJSIPOptionsFromModules( |
||
979 | $peer['extension'], |
||
980 | $options, |
||
981 | CoreConfigClass::OVERRIDE_PJSIP_OPTIONS |
||
982 | ); |
||
983 | $conf .= "[{$peer['extension']}]\n"; |
||
984 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'aor'); |
||
985 | |||
986 | if($this->generalSettings['UseWebRTC'] === '1'){ |
||
987 | $conf .= "[{$peer['extension']}-WS]\n"; |
||
988 | $conf .= Util::overrideConfigurationArray($options, $manual_attributes, 'aor'); |
||
989 | } |
||
990 | |||
991 | return $conf; |
||
992 | } |
||
993 | |||
994 | /** |
||
995 | * Генерация endpoint. |
||
996 | * |
||
997 | * @param $lang |
||
998 | * @param array $peer |
||
999 | * @param array $manual_attributes |
||
1000 | * |
||
1001 | * @return string |
||
1002 | */ |
||
1003 | private function generatePeerEndpoint( |
||
1086 | } |
||
1087 | |||
1088 | } |