Skip to content

Commit 24affc9

Browse files
authored
Merge pull request #13 from rtckit/v0.4.0
v0.4.0
2 parents 637beb1 + 4eff339 commit 24affc9

File tree

9 files changed

+673
-25
lines changed

9 files changed

+673
-25
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "rtckit/sip",
33
"description": "Parser/Renderer for SIP protocol written in PHP",
4-
"version": "0.3.3",
4+
"version": "0.4.0",
55
"type": "library",
66
"keywords": [
77
"sip",

src/Header/AuthHeader.php

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
<?php
2+
/**
3+
* RTCKit\SIP\AuthHeader Class
4+
*/
5+
declare(strict_types = 1);
6+
7+
namespace RTCKit\SIP\Header;
8+
9+
use RTCKit\SIP\Response;
10+
use RTCKit\SIP\Exception\InvalidHeaderLineException;
11+
use RTCKit\SIP\Exception\InvalidHeaderParameter;
12+
use RTCKit\SIP\Exception\InvalidHeaderValue;
13+
14+
/**
15+
* Authentication/Authorization Header Class
16+
*/
17+
class AuthHeader
18+
{
19+
public const DIGEST_SCHEME = 'digest';
20+
21+
/** @var list<AuthValue> Authentication/Authorization value(s) */
22+
public array $values = [];
23+
24+
final public function __construct() {}
25+
26+
/**
27+
* Authentication/Authorization field value parser
28+
*
29+
* @param list<string> $hbody Header body
30+
* @throws InvalidHeaderLineException
31+
* @throws InvalidHeaderParameter
32+
* @return AuthHeader
33+
*/
34+
public static function parse(array $hbody): AuthHeader
35+
{
36+
$ret = new static;
37+
38+
foreach ($hbody as $hline) {
39+
$hparts = explode(' ', trim($hline), 2);
40+
41+
if (count($hparts) !== 2) {
42+
throw new InvalidHeaderLineException('Invalid Auth header, missing scheme', Response::BAD_REQUEST);
43+
}
44+
45+
$val = new AuthValue;
46+
$val->scheme = $hparts[0];
47+
48+
$params = ltrim($hparts[1]);
49+
50+
if (strtolower($val->scheme) === self::DIGEST_SCHEME) {
51+
while (strlen($params)) {
52+
$pos = strpos($params, '=');
53+
54+
if ($pos === false) {
55+
throw new InvalidHeaderLineException('Invalid Auth header, valueless parameter', Response::BAD_REQUEST);
56+
}
57+
58+
$pk = rtrim(substr($params, 0, $pos));
59+
$pv = '';
60+
$params = ltrim(substr($params, $pos + 1));
61+
62+
if (!isset($params[0])) {
63+
throw new InvalidHeaderLineException('Invalid Auth header, valueless parameter', Response::BAD_REQUEST);
64+
}
65+
66+
if ($params[0] === '"') {
67+
$offset = 1;
68+
$escQuotes = false;
69+
70+
while (true) {
71+
$pos = strpos($params, '"', $offset);
72+
73+
if ($pos === false) {
74+
throw new InvalidHeaderLineException('Invalid Auth header, unmatched parameter value enclosing', Response::BAD_REQUEST);
75+
}
76+
77+
if ($params[$pos - 1] !== '\\') {
78+
break;
79+
}
80+
81+
$escQuotes = true;
82+
$offset = $pos + 1;
83+
}
84+
85+
$pv = substr($params, 1, $pos - 1);
86+
87+
if ($escQuotes) {
88+
$pv = str_replace('\"', '"', $pv);
89+
}
90+
91+
$params = ltrim(substr($params, $pos + 1));
92+
93+
if (isset($params[0])) {
94+
if ($params[0] !== ',') {
95+
throw new InvalidHeaderLineException('Invalid Auth header, invalid parameter value enclosing', Response::BAD_REQUEST);
96+
}
97+
98+
$params = ltrim(substr($params, 1));
99+
}
100+
} else {
101+
$pos = strpos($params, ',');
102+
103+
if ($pos !== false) {
104+
$pv = rtrim(substr($params, 0, $pos));
105+
$params = ltrim(substr($params, $pos + 1));
106+
} else {
107+
$pv = rtrim($params);
108+
$params = '';
109+
}
110+
}
111+
112+
switch ($pk) {
113+
case 'username':
114+
$val->username = $pv;
115+
break;
116+
117+
case 'realm':
118+
$val->realm = $pv;
119+
break;
120+
121+
case 'domain':
122+
$val->domain = $pv;
123+
break;
124+
125+
case 'nonce':
126+
$val->nonce = $pv;
127+
break;
128+
129+
case 'uri':
130+
$val->uri = $pv;
131+
break;
132+
133+
case 'response':
134+
$val->response = $pv;
135+
break;
136+
137+
case 'stale':
138+
$pv = strtolower($pv);
139+
140+
if ($pv === 'true') {
141+
$val->stale = true;
142+
} else if ($pv === 'false') {
143+
$val->stale = false;
144+
} else {
145+
throw new InvalidHeaderParameter('Invalid Auth header, non-boolean stale parameter', Response::BAD_REQUEST);
146+
}
147+
break;
148+
149+
case 'algorithm':
150+
$val->algorithm = $pv;
151+
break;
152+
153+
case 'cnonce':
154+
$val->cnonce = $pv;
155+
break;
156+
157+
case 'qop':
158+
$val->qop = $pv;
159+
break;
160+
161+
case 'nc':
162+
if (!ctype_digit($pv)) {
163+
throw new InvalidHeaderParameter('Invalid Auth header, non-integer nc parameter', Response::BAD_REQUEST);
164+
}
165+
166+
$val->nc = (int)$pv;
167+
break;
168+
169+
case 'opaque':
170+
$val->opaque = $pv;
171+
break;
172+
173+
default:
174+
$val->params[$pk] = $pv;
175+
break;
176+
}
177+
}
178+
} else {
179+
$val->credentials = $params;
180+
}
181+
182+
$ret->values[] = $val;
183+
}
184+
185+
return $ret;
186+
}
187+
188+
/**
189+
* Authentication/Authorization header field value renderer
190+
*
191+
* @param string $hname Header field name
192+
* @throws InvalidHeaderValue
193+
* @return string
194+
*/
195+
public function render(string $hname): string
196+
{
197+
$ret = '';
198+
199+
foreach ($this->values as $key => $value) {
200+
if (!isset($value->scheme)) {
201+
throw new InvalidHeaderValue('Malformed auth header, missing scheme');
202+
}
203+
204+
$params = [];
205+
206+
if (strtolower($value->scheme) === self::DIGEST_SCHEME) {
207+
if (isset($value->username)) {
208+
$params[] = "username=\"{$value->username}\"";
209+
}
210+
211+
if (isset($value->realm)) {
212+
$params[] = "realm=\"{$value->realm}\"";
213+
}
214+
215+
if (isset($value->domain)) {
216+
$params[] = "domain=\"{$value->domain}\"";
217+
}
218+
219+
if (isset($value->nonce)) {
220+
$params[] = "nonce=\"{$value->nonce}\"";
221+
}
222+
223+
if (isset($value->uri)) {
224+
$params[] = "uri=\"{$value->uri}\"";
225+
}
226+
227+
if (isset($value->response)) {
228+
$params[] = "response=\"{$value->response}\"";
229+
}
230+
231+
if (isset($value->stale)) {
232+
$params[] = 'stale=' . ($value->stale ? 'TRUE' : 'FALSE');
233+
}
234+
235+
if (isset($value->algorithm)) {
236+
$params[] = "algorithm=\"{$value->algorithm}\"";
237+
}
238+
239+
if (isset($value->cnonce)) {
240+
$params[] = "cnonce=\"{$value->cnonce}\"";
241+
}
242+
243+
if (isset($value->qop)) {
244+
$params[] = "qop=\"{$value->qop}\"";
245+
}
246+
247+
if (isset($value->nc)) {
248+
$params[] = sprintf('ns=%08d', $value->nc);
249+
}
250+
251+
if (isset($value->opaque)) {
252+
$params[] = "opaque=\"{$value->opaque}\"";
253+
}
254+
} else {
255+
if (!isset($value->credentials)) {
256+
throw new InvalidHeaderValue('Malformed auth header, missing credentials');
257+
}
258+
259+
$params[] = $value->credentials;
260+
}
261+
262+
foreach ($value->params as $pk => $pv) {
263+
$params[] = "{$pk}={$pv}";
264+
}
265+
266+
$paramStr = implode(',', $params);
267+
268+
$ret .= "{$hname}: {$value->scheme} {$paramStr}\r\n";
269+
}
270+
271+
return $ret;
272+
}
273+
}

src/Header/AuthValue.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* RTCKit\SIP\AuthValue Class
4+
*/
5+
declare(strict_types = 1);
6+
7+
namespace RTCKit\SIP\Header;
8+
9+
/**
10+
* Authentication/Authorization Header Field Value Class
11+
*/
12+
class AuthValue
13+
{
14+
/** @var string Authentication scheme */
15+
public string $scheme;
16+
17+
/** @var string Authentication user name */
18+
public string $username;
19+
20+
/** @var string Authentication realm */
21+
public string $realm;
22+
23+
/** @var string SIP domain */
24+
public string $domain;
25+
26+
/** @var string Server's number once */
27+
public string $nonce;
28+
29+
/** @var string SIP URI */
30+
public string $uri;
31+
32+
/** @var string Response hash */
33+
public string $response;
34+
35+
/** @var bool Stale response flag */
36+
public bool $stale;
37+
38+
/** @var string Digest algorithm */
39+
public string $algorithm;
40+
41+
/** @var string Client's number once */
42+
public string $cnonce;
43+
44+
/** @var string Quality of protection */
45+
public string $qop;
46+
47+
/** @var int Number once count */
48+
public int $nc;
49+
50+
/** @var string Server's opaque data blob */
51+
public string $opaque;
52+
53+
/** @var string Generic credentials when attributes aren't used (e.g. Basic) */
54+
public string $credentials;
55+
56+
/** @var array<string, string> Additional parameters */
57+
public array $params = [];
58+
}

0 commit comments

Comments
 (0)