mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-04-17 19:58:27 +00:00
92 lines
No EOL
3.4 KiB
PHP
92 lines
No EOL
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Rules;
|
|
|
|
use Closure;
|
|
use Illuminate\Contracts\Validation\ValidationRule;
|
|
|
|
class SipUri implements ValidationRule
|
|
{
|
|
/**
|
|
* Run the validation rule.
|
|
* Validates a SIP/SIPS URI against RFC 3261 §19.1 ABNF grammar.
|
|
*/
|
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
|
{
|
|
if (! is_string($value) || ! preg_match($this->buildPattern(), $value)) {
|
|
$fail('The :attribute must be a valid SIP URI.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the RFC 3261 §19.1 compliant regex pattern.
|
|
* Generated using Claude
|
|
*/
|
|
private function buildPattern(): string
|
|
{
|
|
// unreserved = alphanum / mark (mark = - _ . ! ~ * ' ( ))
|
|
$unreserved = '[A-Za-z0-9\-_.!~*\'()]';
|
|
|
|
// escaped = "%" HEXDIG HEXDIG
|
|
$escaped = '%[0-9A-Fa-f]{2}';
|
|
|
|
// user-unreserved = & = + $ , ; ? /
|
|
$userUnreserved = '[&=+$,;?\/]';
|
|
|
|
// user = 1*( unreserved / escaped / user-unreserved )
|
|
$user = "(?:{$unreserved}|{$escaped}|{$userUnreserved})+";
|
|
|
|
// password = *( unreserved / escaped / & = + $ , )
|
|
$password = "(?:{$unreserved}|{$escaped}|[&=+$,])*";
|
|
|
|
// userinfo = user [ ":" password ] "@"
|
|
$userinfo = "(?:{$user})(?::{$password})?@";
|
|
|
|
// domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum
|
|
$domainLabel = '[A-Za-z0-9](?:[A-Za-z0-9\-]*[A-Za-z0-9])?';
|
|
|
|
// toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
|
|
$topLabel = '[A-Za-z](?:[A-Za-z0-9\-]*[A-Za-z0-9])?';
|
|
|
|
// hostname = *( domainlabel "." ) toplabel [ "." ]
|
|
$hostname = "(?:{$domainLabel}\.)*{$topLabel}\.?";
|
|
|
|
// IPv4address = 1*3DIGIT "." x3
|
|
$ipv4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
|
|
|
|
// IPv6 building blocks (RFC 5954)
|
|
$h16 = '[0-9A-Fa-f]{1,4}';
|
|
$ls32 = "(?:{$h16}:{$h16}|{$ipv4})";
|
|
$ipv6 = "(?:"
|
|
. "(?:{$h16}:){6}{$ls32}"
|
|
. "|::(?:{$h16}:){5}{$ls32}"
|
|
. "|{$h16}?::(?:{$h16}:){4}{$ls32}"
|
|
. "|(?:{$h16}:)?{$h16}::(?:{$h16}:){3}{$ls32}"
|
|
. "|(?:{$h16}:){0,2}{$h16}::(?:{$h16}:){2}{$ls32}"
|
|
. "|(?:{$h16}:){0,3}{$h16}::{$h16}:{$ls32}"
|
|
. "|(?:{$h16}:){0,4}{$h16}::{$ls32}"
|
|
. "|(?:{$h16}:){0,5}{$h16}::{$h16}"
|
|
. "|(?:{$h16}:){0,6}{$h16}::"
|
|
. ")";
|
|
|
|
// IPv6reference = "[" IPv6address "]"
|
|
$ipv6ref = "\[{$ipv6}\]";
|
|
|
|
// host = IPv6reference / IPv4address / hostname (order matters)
|
|
$host = "(?:{$ipv6ref}|{$ipv4}|{$hostname})";
|
|
|
|
// hostport = host [ ":" port ]
|
|
$hostport = "{$host}(?::\d+)?";
|
|
|
|
// uri-parameters: *( ";" pname [ "=" pvalue ] )
|
|
$paramChar = "(?:{$unreserved}|{$escaped}|[\\[\\]\/:&+$])";
|
|
$uriParams = "(?:;{$paramChar}*(?:={$paramChar}*)?)*";
|
|
|
|
// headers: "?" hname "=" hvalue *( "&" hname "=" hvalue )
|
|
$hnvUnreserved = '[\\[\\]\/?:+$]';
|
|
$headerChar = "(?:{$unreserved}|{$escaped}|{$hnvUnreserved})";
|
|
$headers = "(?:\\?{$headerChar}+={$headerChar}*(?:&{$headerChar}+={$headerChar}*)*)?";
|
|
|
|
return '/^sips?:(?:' . $userinfo . ')?' . $hostport . $uriParams . $headers . '$/i';
|
|
}
|
|
} |