|
17 | 17 | use MikoPBX\Modules\PbxExtensionUtils; |
18 | 18 | use MikoPBX\PBXCoreREST\Controllers\Modules\ModulesControllerBase; |
19 | 19 | use MikoPBX\Common\Library\Text; |
| 20 | +use Modules\ModuleAutoprovision\Lib\RestAPI\Firmware\Repository as FirmwareRepository; |
20 | 21 | use Modules\ModuleAutoprovision\Lib\Transliterate; |
| 22 | +use Modules\ModuleAutoprovision\Models\ModuleAutoprovisionDevice; |
21 | 23 | use Modules\ModuleAutoprovision\Models\OtherPBX; |
22 | 24 | use Modules\ModuleAutoprovision\Models\Templates; |
23 | 25 | use Modules\ModuleAutoprovision\Models\TemplatesUri; |
@@ -160,6 +162,25 @@ public function getConfigStatic(...$args):void |
160 | 162 | if(!empty($resultSip)){ |
161 | 163 | $data = $resultSip[0]; |
162 | 164 | } |
| 165 | + |
| 166 | + // Firmware URL placeholder. Vendor is sniffed from the User-Agent |
| 167 | + // (every supported phone family includes its brand there) and the |
| 168 | + // exact model comes from the device row keyed by MAC. Missing data |
| 169 | + // resolves to empty string, which lets templates wrap the line in |
| 170 | + // a vendor-specific conditional if needed. |
| 171 | + $vendor = $this->detectVendorFromUserAgent((string)$userAgent); |
| 172 | + $model = null; |
| 173 | + if ($mac !== '') { |
| 174 | + $device = ModuleAutoprovisionDevice::findFirst([ |
| 175 | + 'mac = :mac:', |
| 176 | + 'bind' => ['mac' => $mac], |
| 177 | + ]); |
| 178 | + if ($device !== null && !empty($device->manufacturer_model)) { |
| 179 | + $model = $this->extractModel((string)$device->manufacturer_model); |
| 180 | + } |
| 181 | + } |
| 182 | + $data['{FIRMWARE_URL}'] = FirmwareRepository::resolveFirmwareUrl($vendor, $model); |
| 183 | + |
163 | 184 | $config = str_replace(array_keys($data), array_values($data), $result[0]['template']); |
164 | 185 | $this->response->setHeader('Content-Description', "config file"); |
165 | 186 | $this->response->setHeader('Content-Disposition', "attachment; filename=".basename($uri)); |
@@ -372,6 +393,57 @@ private function echoPhoneBookYealink($uri):void |
372 | 393 | echo $phoneBook; |
373 | 394 | } |
374 | 395 |
|
| 396 | + /** |
| 397 | + * Best-effort vendor key derived from the User-Agent string sent by the phone. |
| 398 | + * |
| 399 | + * Every supported phone family identifies itself in User-Agent (Yealink, Snom, |
| 400 | + * Fanvil, Grandstream, Htek). Returns '' if no known brand matches — callers |
| 401 | + * then resolve {FIRMWARE_URL} to an empty string instead of guessing. |
| 402 | + */ |
| 403 | + private function detectVendorFromUserAgent(string $userAgent): string |
| 404 | + { |
| 405 | + $ua = strtolower($userAgent); |
| 406 | + foreach (['yealink', 'snom', 'fanvil', 'grandstream', 'htek'] as $vendor) { |
| 407 | + if (str_contains($ua, $vendor)) { |
| 408 | + return $vendor; |
| 409 | + } |
| 410 | + } |
| 411 | + return ''; |
| 412 | + } |
| 413 | + |
| 414 | + /** |
| 415 | + * Extracts the model name from ModuleAutoprovisionDevice.manufacturer_model. |
| 416 | + * |
| 417 | + * PnP populates the field as "Vendor / Model" (e.g. "Fanvil / X3SP V2") and |
| 418 | + * some models contain spaces, so we can't just take the last whitespace token |
| 419 | + * — that would shrink "X3SP V2" to "V2". When a "/" separator is present we |
| 420 | + * take everything after it; otherwise we drop a leading vendor word that |
| 421 | + * matches one of the known vendors and keep the rest. Returns null when the |
| 422 | + * field is empty. |
| 423 | + */ |
| 424 | + private function extractModel(string $manufacturerModel): ?string |
| 425 | + { |
| 426 | + $raw = trim($manufacturerModel); |
| 427 | + if ($raw === '') { |
| 428 | + return null; |
| 429 | + } |
| 430 | + |
| 431 | + $slash = strpos($raw, '/'); |
| 432 | + if ($slash !== false) { |
| 433 | + $tail = trim(substr($raw, $slash + 1)); |
| 434 | + return $tail === '' ? null : $tail; |
| 435 | + } |
| 436 | + |
| 437 | + // No separator: strip a leading known-vendor token if present. |
| 438 | + $parts = preg_split('/\s+/', $raw) ?: []; |
| 439 | + if ($parts !== [] && in_array(strtolower($parts[0]), ['yealink', 'snom', 'fanvil', 'grandstream', 'htek'], true)) { |
| 440 | + array_shift($parts); |
| 441 | + } |
| 442 | + $tail = implode(' ', $parts); |
| 443 | + $tail = trim($tail); |
| 444 | + return $tail === '' ? null : $tail; |
| 445 | + } |
| 446 | + |
375 | 447 | private function camelize(string $inputString): string |
376 | 448 | { |
377 | 449 | $inputString = preg_replace('/\s+/', '-', $inputString); |
|
0 commit comments