1use std::ops::Range;
16
17use bytes::Bytes;
18
19use crate::error::HeaderError;
20use crate::headers::grammar::{
21 self, HeaderParam, find_param_start, is_token_char, quoted_string_end, skip_ws, trim,
22};
23use crate::message::TypedHeader;
24use crate::name::HeaderName;
25use crate::uri::Uri;
26
27#[derive(Debug, Clone)]
29pub struct Address {
30 pub display_name: Option<Vec<u8>>,
32 pub uri: Uri,
34 pub params: Vec<HeaderParam>,
36}
37
38impl Address {
39 pub fn parse(value: &[u8], header: &'static str) -> Result<Self, HeaderError> {
41 parse_spanned(value, header).map(|parsed| parsed.address)
42 }
43
44 pub fn parse_list(value: &[u8], header: &'static str) -> Result<Vec<Self>, HeaderError> {
50 grammar::split_list(value, header)?
51 .into_iter()
52 .map(|part| Self::parse(part, header))
53 .collect()
54 }
55
56 #[must_use]
58 pub fn param(&self, name: &str) -> Option<&[u8]> {
59 grammar::param(&self.params, name).and_then(|p| p.value.as_deref())
60 }
61
62 #[must_use]
64 pub fn tag(&self) -> Option<&[u8]> {
65 self.param("tag")
66 }
67}
68
69#[derive(Debug)]
70struct ParsedAddress {
71 address: Address,
72 presentation: Range<usize>,
73 uri: Range<usize>,
74}
75
76fn parse_spanned(value: &[u8], header: &'static str) -> Result<ParsedAddress, HeaderError> {
77 let outer = trimmed_range(value);
78 let value = value.get(outer.clone()).unwrap_or(&[]);
79 let mut i = skip_ws(value, 0);
80 let presentation_start = i;
81
82 let mut display_name = None;
85 if value.get(i) == Some(&b'"') {
86 let end =
87 quoted_string_end(value, i).ok_or(HeaderError::UnterminatedQuotedString { header })?;
88 let raw = value.get(i + 1..end.saturating_sub(1)).unwrap_or(&[]);
89 display_name = Some(unescape(raw));
90 i = skip_ws(value, end);
91 if value.get(i) != Some(&b'<') {
92 return Err(HeaderError::Syntax { header });
93 }
94 } else if let Some(angle) = find_angle(value, i) {
95 let raw = trim(value.get(i..angle).unwrap_or(&[]));
96 if !raw.is_empty() {
97 if !raw
101 .iter()
102 .all(|&b| is_token_char(b) || matches!(b, b' ' | b'\t'))
103 {
104 return Err(HeaderError::Syntax { header });
105 }
106 display_name = Some(raw.to_vec());
107 }
108 i = angle;
109 }
110
111 let (uri_span, presentation_span, params_tail) = if value.get(i) == Some(&b'<') {
112 let close = find_closing_angle(value, i).ok_or(HeaderError::Syntax { header })?;
113 (
114 i + 1..close,
115 presentation_start..close.saturating_add(1),
116 value.get(close + 1..).unwrap_or(&[]),
117 )
118 } else {
119 let rest = value.get(i..).unwrap_or(&[]);
121 let param_start = find_param_start(rest);
122 let candidate = match param_start {
123 Some(semi) => i..i.checked_add(semi).ok_or(HeaderError::Syntax { header })?,
124 None => i..value.len(),
125 };
126 let candidate_bytes = value.get(candidate.clone()).unwrap_or(&[]);
127 let trimmed = trimmed_range(candidate_bytes);
128 let uri_span = candidate
129 .start
130 .checked_add(trimmed.start)
131 .zip(candidate.start.checked_add(trimmed.end))
132 .map(|(start, end)| start..end)
133 .ok_or(HeaderError::Syntax { header })?;
134 let params_tail = if let Some(semi) = param_start {
135 rest.get(semi..).unwrap_or(&[])
136 } else {
137 &[][..]
138 };
139 (uri_span.clone(), uri_span, params_tail)
140 };
141
142 let uri_bytes = value.get(uri_span.clone()).unwrap_or(&[]);
143 if uri_bytes.is_empty() {
144 return Err(HeaderError::Syntax { header });
145 }
146
147 if value.get(i) != Some(&b'<') && uri_bytes.contains(&b'?') {
151 return Err(HeaderError::Syntax { header });
152 }
153 let parsed_uri = Uri::parse(Bytes::copy_from_slice(uri_bytes))
154 .map_err(|source| HeaderError::Uri { header, source })?;
155
156 let params = grammar::parse_params(trim(params_tail), header)?;
157 let uri_span = add_offset(uri_span, outer.start, header)?;
158 let presentation_span = add_offset(presentation_span, outer.start, header)?;
159
160 Ok(ParsedAddress {
161 address: Address {
162 display_name,
163 uri: parsed_uri,
164 params,
165 },
166 presentation: presentation_span,
167 uri: uri_span,
168 })
169}
170
171#[derive(Debug, Clone)]
173pub(crate) struct AddressValueSpan {
174 pub(crate) part: Range<usize>,
176 pub(crate) item: Range<usize>,
178 pub(crate) presentation: Range<usize>,
180 pub(crate) uri: Range<usize>,
182}
183
184pub(crate) fn value_spans(
189 value: &[u8],
190 header: &'static str,
191 is_list: bool,
192) -> Result<Vec<AddressValueSpan>, HeaderError> {
193 let parts = if is_list {
194 grammar::split_list_spans(value, header)?
195 } else {
196 std::iter::once(0..value.len()).collect()
197 };
198
199 parts
200 .into_iter()
201 .map(|part| {
202 let bytes = value.get(part.clone()).unwrap_or(&[]);
203 let parsed = parse_spanned(bytes, header)?;
204 let item = trimmed_range(bytes);
205 Ok(AddressValueSpan {
206 part: part.clone(),
207 item: add_offset(item, part.start, header)?,
208 presentation: add_offset(parsed.presentation, part.start, header)?,
209 uri: add_offset(parsed.uri, part.start, header)?,
210 })
211 })
212 .collect()
213}
214
215fn trimmed_range(value: &[u8]) -> Range<usize> {
216 let mut start = 0usize;
217 while matches!(value.get(start), Some(b' ' | b'\t')) {
218 start += 1;
219 }
220 let mut end = value.len();
221 while end > start && matches!(value.get(end - 1), Some(b' ' | b'\t')) {
222 end -= 1;
223 }
224 start..end
225}
226
227fn add_offset(
228 range: Range<usize>,
229 offset: usize,
230 header: &'static str,
231) -> Result<Range<usize>, HeaderError> {
232 offset
233 .checked_add(range.start)
234 .zip(offset.checked_add(range.end))
235 .map(|(start, end)| start..end)
236 .ok_or(HeaderError::Syntax { header })
237}
238
239#[must_use]
244fn find_angle(value: &[u8], from: usize) -> Option<usize> {
245 let mut i = from;
246 while i < value.len() {
247 match value.get(i) {
248 Some(b'"') => i = quoted_string_end(value, i)?,
249 Some(b'<') => return Some(i),
250 Some(_) => i += 1,
251 None => break,
252 }
253 }
254 None
255}
256
257#[must_use]
258fn find_closing_angle(value: &[u8], open: usize) -> Option<usize> {
259 value
260 .get(open + 1..)?
261 .iter()
262 .position(|&b| b == b'>')
263 .map(|p| p + open + 1)
264}
265
266#[must_use]
267fn unescape(raw: &[u8]) -> Vec<u8> {
268 let mut out = Vec::with_capacity(raw.len());
269 let mut i = 0usize;
270 while let Some(&b) = raw.get(i) {
271 if b == b'\\'
272 && let Some(&next) = raw.get(i + 1)
273 {
274 out.push(next);
275 i += 2;
276 continue;
277 }
278 out.push(b);
279 i += 1;
280 }
281 out
282}
283
284macro_rules! address_type {
285 ($(#[$meta:meta])* $type:ident) => {
286 $(#[$meta])*
287 #[derive(Debug, Clone)]
288 pub struct $type(pub Address);
289
290 impl std::ops::Deref for $type {
291 type Target = Address;
292 fn deref(&self) -> &Address {
293 &self.0
294 }
295 }
296 };
297}
298
299macro_rules! single_address_header {
302 ($(#[$meta:meta])* $type:ident => $variant:ident, $label:literal) => {
303 address_type!($(#[$meta])* $type);
304
305 impl TypedHeader for $type {
306 const NAME: HeaderName = HeaderName::$variant;
307
308 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
309 Address::parse(value, $label).map(Self)
310 }
311 }
312 };
313}
314
315macro_rules! address_list_header {
317 ($(#[$meta:meta])* $type:ident => $variant:ident, $label:literal) => {
318 address_type!($(#[$meta])* $type);
319
320 impl $type {
321 pub fn parse_list(value: &[u8]) -> Result<Vec<Self>, HeaderError> {
323 Address::parse_list(value, $label).map(|list| list.into_iter().map(Self).collect())
324 }
325 }
326
327 impl TypedHeader for $type {
328 const NAME: HeaderName = HeaderName::$variant;
329
330 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
333 let parts = grammar::split_list(value, $label)?;
334 let first = parts.first().copied().unwrap_or(&[]);
335 Address::parse(first, $label).map(Self)
336 }
337
338 fn decode_list(value: &[u8]) -> Result<Vec<Self>, HeaderError> {
339 Self::parse_list(value)
340 }
341 }
342 };
343}
344
345single_address_header!(
346 From => From, "From"
348);
349single_address_header!(
350 To => To, "To"
352);
353address_list_header!(
354 Contact => Contact, "Contact"
359);
360address_list_header!(
361 Route => Route, "Route"
363);
364address_list_header!(
365 RecordRoute => RecordRoute, "Record-Route"
367);
368address_list_header!(
369 Path => Path, "Path"
377);
378address_list_header!(
379 ServiceRoute => ServiceRoute, "Service-Route"
386);
387
388#[derive(Debug, Clone)]
394pub enum ContactValue {
395 Wildcard,
397 Address(Address),
399}
400
401impl TypedHeader for ContactValue {
402 const NAME: HeaderName = HeaderName::Contact;
403
404 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
407 if trim(value) == b"*" {
408 return Ok(Self::Wildcard);
409 }
410 Contact::decode(value).map(|c| Self::Address(c.0))
411 }
412
413 fn decode_list(value: &[u8]) -> Result<Vec<Self>, HeaderError> {
414 if trim(value) == b"*" {
417 return Ok(vec![Self::Wildcard]);
418 }
419 Address::parse_list(value, "Contact")
420 .map(|list| list.into_iter().map(Self::Address).collect())
421 }
422}
423
424#[cfg(test)]
425#[allow(
426 clippy::unwrap_used,
427 clippy::expect_used,
428 clippy::panic,
429 clippy::indexing_slicing
430)]
431mod tests {
432 use super::*;
433
434 fn addr(value: &[u8]) -> Address {
435 Address::parse(value, "To").unwrap_or_else(|e| panic!("{value:?} should parse: {e}"))
436 }
437
438 #[test]
439 fn parses_a_bare_addr_spec() {
440 let a = addr(b"sip:j.user@example.com");
441 assert!(a.display_name.is_none());
442 assert_eq!(
443 a.uri.to_bytes(),
444 Bytes::from_static(b"sip:j.user@example.com")
445 );
446 assert!(a.params.is_empty());
447 }
448
449 #[test]
450 fn parses_a_quoted_display_name_with_escapes() {
451 let a = addr(br#""J Rosenberg \\\"" <sip:jdrosen@example.com>;tag=98asjd8"#);
453 assert_eq!(a.display_name.as_deref(), Some(&br#"J Rosenberg \""#[..]));
454 assert_eq!(a.tag(), Some(&b"98asjd8"[..]));
455 }
456
457 #[test]
458 fn parses_an_unquoted_token_display_name() {
459 let a = addr(b"J Rosenberg <sip:jdrosen@example.com>");
460 assert_eq!(a.display_name.as_deref(), Some(&b"J Rosenberg"[..]));
461 }
462
463 #[test]
465 fn parses_a_display_name_abutting_the_angle_bracket() {
466 let a = addr(br#""caller"<sip:caller@example.com>;tag=323"#);
467 assert_eq!(a.display_name.as_deref(), Some(&b"caller"[..]));
468 assert_eq!(a.tag(), Some(&b"323"[..]));
469 }
470
471 #[test]
473 fn semicolons_bind_to_the_header_without_brackets_and_to_the_uri_within_them() {
474 let bare = addr(b"sip:a@b.com;tag=1");
475 assert_eq!(bare.uri.to_bytes(), Bytes::from_static(b"sip:a@b.com"));
476 assert_eq!(bare.tag(), Some(&b"1"[..]));
477
478 let bracketed = addr(b"<sip:a@b.com;tag=1>");
479 assert_eq!(
480 bracketed.uri.to_bytes(),
481 Bytes::from_static(b"sip:a@b.com;tag=1")
482 );
483 assert!(bracketed.tag().is_none());
484 }
485
486 #[test]
489 fn a_quoted_parameter_value_may_contain_an_angle_bracket() {
490 let a = addr(br#"sip:a@b.com;x="<y>""#);
491 assert_eq!(a.uri.to_bytes(), Bytes::from_static(b"sip:a@b.com"));
492 assert_eq!(a.param("x"), Some(&b"<y>"[..]));
493
494 let a = addr(br#"sip:a@b.com;note="hi <there>""#);
495 assert_eq!(a.uri.to_bytes(), Bytes::from_static(b"sip:a@b.com"));
496 assert_eq!(a.param("note"), Some(&b"hi <there>"[..]));
497
498 let a = addr(br#"sip:a@b.com;x="a\"<b""#);
500 assert_eq!(a.param("x"), Some(&br#"a"<b"#[..]));
501 }
502
503 #[test]
506 fn rejects_an_unquoted_comma_in_a_display_name() {
507 let err = Address::parse(b"Bell, Alexander <sip:a.g.bell@example.com>", "From");
508 assert!(matches!(err, Err(HeaderError::Syntax { header: "From" })));
509 }
510
511 #[test]
513 fn rejects_an_unterminated_quoted_display_name() {
514 let err = Address::parse(br#""Mr. J. User <sip:j.user@example.com>"#, "To");
515 assert!(matches!(
516 err,
517 Err(HeaderError::UnterminatedQuotedString { header: "To" })
518 ));
519 }
520
521 #[test]
523 fn rejects_spaces_within_the_addr_spec() {
524 let err = Address::parse(br#""Watson, Thomas" < sip:t.watson@example.org >"#, "To");
525 assert!(matches!(err, Err(HeaderError::Uri { .. })));
526 }
527
528 #[test]
531 fn rejects_an_unbracketed_question_mark_for_every_uri_scheme() {
532 for bare in [
533 b"sip:user@example.com?Route=%3Csip:sip.example.com%3E".as_slice(),
534 b"tel:+12015550123?x=y",
535 b"mailto:alice@example.com?subject=hello",
536 ] {
537 assert!(matches!(
538 Address::parse(bare, "Contact"),
539 Err(HeaderError::Syntax { header: "Contact" })
540 ));
541 }
542
543 for bracketed in [
545 b"<sip:user@example.com?Route=%3Csip:sip.example.com%3E>".as_slice(),
546 b"<mailto:alice@example.com?subject=hello>",
547 ] {
548 assert!(Address::parse(bracketed, "Contact").is_ok());
549 }
550 }
551
552 #[test]
555 fn decodes_the_first_address_of_a_comma_separated_row() {
556 let c = Contact::decode(b"<sip:a@b.com>, <sip:c@d.com>").unwrap();
557 assert_eq!(c.uri.to_bytes(), Bytes::from_static(b"sip:a@b.com"));
558
559 let r = Route::decode(b"<sip:p1.example.com;lr>,<sip:p2.example.com;lr>").unwrap();
560 assert_eq!(
561 r.uri.to_bytes(),
562 Bytes::from_static(b"sip:p1.example.com;lr")
563 );
564 }
565
566 #[test]
567 fn parses_every_address_of_a_comma_separated_row() {
568 let list = Address::parse_list(
569 br#""Bell, Alexander" <sip:a@b.com>;q=0.7, <sip:c@d.com>"#,
570 "Contact",
571 )
572 .unwrap();
573 assert_eq!(list.len(), 2);
574 assert_eq!(
575 list[0].display_name.as_deref(),
576 Some(&b"Bell, Alexander"[..])
577 );
578 assert_eq!(list[0].param("q"), Some(&b"0.7"[..]));
579 assert_eq!(list[1].uri.to_bytes(), Bytes::from_static(b"sip:c@d.com"));
580
581 assert!(Address::parse_list(b"<sip:a@b.com>, not a uri", "Contact").is_err());
583 }
584
585 #[test]
586 fn wildcard_and_addresses_both_come_out_of_a_contact_list() {
587 let all = ContactValue::decode_list(b"*").unwrap();
588 assert!(matches!(all.as_slice(), [ContactValue::Wildcard]));
589
590 let all = ContactValue::decode_list(b"<sip:a@b.com>, <sip:c@d.com>").unwrap();
591 assert_eq!(all.len(), 2);
592 assert!(all.iter().all(|v| matches!(v, ContactValue::Address(_))));
593 }
594
595 #[test]
596 fn parses_the_wildcard_contact() {
597 assert!(matches!(
598 ContactValue::decode(b"*"),
599 Ok(ContactValue::Wildcard)
600 ));
601 assert!(matches!(
602 ContactValue::decode(b"<sip:a@b>"),
603 Ok(ContactValue::Address(_))
604 ));
605 }
606}