From 745e9307d7ef97a12f4dbc9de69d4d98def0ed1a Mon Sep 17 00:00:00 2001 From: sorcier Date: Thu, 12 Nov 2026 02:35:46 +0100 Subject: [PATCH] fix(ucp): v2.6.2 regle stricte host/port GroundWire (NAT > VIP HA > IP locale + port local) --- src/UcpManager.php | 71 ++++++++++++++++++++++++++++++++-------------- version.json | 3 +- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/UcpManager.php b/src/UcpManager.php index 9632dbc..23321ee 100644 --- a/src/UcpManager.php +++ b/src/UcpManager.php @@ -888,29 +888,57 @@ class UcpManager } } catch (\Throwable) {} - $sipHost = trim((string)($settings['external_ip'] ?? '')); - if ($sipHost === '') { - $sipHost = trim((string)($settings['letsencrypt_domain'] ?? '')); - } - if ($sipHost === '' && $haVip !== '') { - $sipHost = $haVip; - } - if ($sipHost === '') { - $rawHttpHost = trim(explode(',', (string)($_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'] ?? ''))[0] ?? ''); - if ($rawHttpHost !== '') { - $parsedHost = parse_url("http://{$rawHttpHost}", PHP_URL_HOST); - $sipHost = is_string($parsedHost) && $parsedHost !== '' ? $parsedHost : $rawHttpHost; - } - } - if ($sipHost === '') { - $sipHost = trim((string)($settings['domain'] ?? '')) ?: '127.0.0.1'; - } - $isTls = (($extRow['transport'] ?? 'udp') === 'tls'); $transport = $isTls ? 'tls' : 'udp'; - $sipPort = $isTls - ? (((int)($settings['external_tls_port'] ?? 0) > 0) ? (int)$settings['external_tls_port'] : ((int)($settings['tls_port'] ?? 5061) ?: 5061)) - : (((int)($settings['external_udp_port'] ?? 0) > 0) ? (int)$settings['external_udp_port'] : ((int)($settings['udp_port'] ?? 5060) ?: 5060)); + $localPort = $isTls ? ((int)($settings['tls_port'] ?? 5061) ?: 5061) : ((int)($settings['udp_port'] ?? 5060) ?: 5060); + + $natHost = trim((string)($settings['external_ip'] ?? '')); + $natHost = preg_replace('#^(?:https?|sips?)://#i', '', $natHost); + $natHost = trim((string)$natHost, "/ \t\n\r\0\x0B"); + + if ($natHost !== '') { + // Mode NAT renseigné : adresse NAT + port externe NAT (ou port interne par défaut) + $sipHost = $natHost; + $sipPort = $isTls + ? (((int)($settings['external_tls_port'] ?? 0) > 0) ? (int)$settings['external_tls_port'] : $localPort) + : (((int)($settings['external_udp_port'] ?? 0) > 0) ? (int)$settings['external_udp_port'] : $localPort); + $hostPort = str_contains($sipHost, ':') ? $sipHost : "{$sipHost}:{$sipPort}"; + } else { + // Mode local : VIP HA si cluster actif, sinon IP locale du PBX + port d'écoute local + $sipHost = ''; + if ($haVip !== '') { + $sipHost = $haVip; + } + if ($sipHost === '') { + $rawHttpHost = trim(explode(',', (string)($_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'] ?? ''))[0] ?? ''); + if ($rawHttpHost !== '') { + $parsedHost = parse_url("http://{$rawHttpHost}", PHP_URL_HOST); + if (is_string($parsedHost) && $parsedHost !== '' && !in_array($parsedHost, ['127.0.0.1', '::1', 'localhost'], true)) { + $sipHost = $parsedHost; + } + } + } + if ($sipHost === '') { + $serverAddr = trim((string)($_SERVER['SERVER_ADDR'] ?? '')); + if ($serverAddr !== '' && !in_array($serverAddr, ['127.0.0.1', '::1'], true)) { + $sipHost = $serverAddr; + } + } + if ($sipHost === '') { + $ips = preg_split('/\s+/', trim((string)@shell_exec('hostname -I 2>/dev/null'))) ?: []; + foreach ($ips as $ip) { + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && !str_starts_with($ip, '127.')) { + $sipHost = $ip; + break; + } + } + } + if ($sipHost === '') { + $sipHost = trim((string)($settings['domain'] ?? '')) ?: '127.0.0.1'; + } + + $hostPort = "{$sipHost}:{$localPort}"; + } $encMode = (int)($extRow['encryption'] ?? 0); $sdes = ($isTls || $encMode > 0) ? 'enabled' : 'disabled'; @@ -928,7 +956,6 @@ class UcpManager $extNum = (string)$extRow['extension']; $lineName = trim((string)($extRow['name'] ?? '')) ?: $extNum; $password = (string)($extRow['secret'] ?? ''); - $hostPort = "{$sipHost}:{$sipPort}"; $xml = "\n" . "\n" diff --git a/version.json b/version.json index e35d6a7..ea88a57 100644 --- a/version.json +++ b/version.json @@ -1,9 +1,10 @@ { - "version": "2.6.1", + "version": "2.6.2", "codename": "Pulsar UCP", "release_date": "2026-11-12", "min_php_version": "8.2", "changelog": [ + "Correctif v2.6.2 : sélection stricte de GroundWire (NAT si renseigné, sinon VIP HA si cluster actif, sinon IP locale + port d'écoute local)", "UI v2.6.1 : suppression de l'icône smartphone sur les éléments GroundWire et refonte des boutons d'action du tableau Postes (Redémarrer / Modifier / Supprimer)", "Nouveauté v2.6.0 : provisionnement dynamique XML Acrobits GroundWire par poste (option Admin « GroundWire XML », deep link mobile provlinkbs:, téléchargement XML signé HMAC sans session, liens App Store / Google Play dans l'UCP)", "UI v2.5.7 : journal d'appels UCP en cartes style iOS sur mobile (<768px) tout en conservant le tableau 6 colonnes sur ordinateur",