1use bytes::Bytes;
4
5use crate::error::HeaderError;
6use crate::escape;
7use crate::headers::address::Address;
8use crate::headers::grammar::{self, HeaderParam, is_token_char, trim};
9use crate::message::TypedHeader;
10use crate::name::HeaderName;
11use crate::params::Param;
12use crate::uri::Uri;
13
14const REASON: &str = "Reason";
15const HISTORY_INFO: &str = "History-Info";
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct ReasonValue {
20 protocol: Vec<u8>,
21 cause: u16,
22 text: Option<Vec<u8>>,
23 extensions: Vec<HeaderParam>,
24}
25
26impl ReasonValue {
27 #[must_use]
29 pub fn sip(cause: crate::message::StatusCode, text: Option<Vec<u8>>) -> Self {
30 Self {
31 protocol: b"SIP".to_vec(),
32 cause: cause.code(),
33 text,
34 extensions: Vec::new(),
35 }
36 }
37
38 #[must_use]
40 pub fn q850(cause: u8, text: Option<Vec<u8>>) -> Self {
41 Self {
42 protocol: b"Q.850".to_vec(),
43 cause: u16::from(cause),
44 text,
45 extensions: Vec::new(),
46 }
47 }
48
49 #[must_use]
51 pub fn protocol(&self) -> &[u8] {
52 &self.protocol
53 }
54
55 #[must_use]
57 pub fn cause(&self) -> u16 {
58 self.cause
59 }
60
61 #[must_use]
63 pub fn text(&self) -> Option<&[u8]> {
64 self.text.as_deref()
65 }
66
67 #[must_use]
69 pub fn to_bytes(&self) -> Bytes {
70 let mut out = self.protocol.clone();
71 out.extend_from_slice(b";cause=");
72 out.extend_from_slice(self.cause.to_string().as_bytes());
73 if let Some(text) = &self.text {
74 out.extend_from_slice(b";text=\"");
75 write_quoted(text, &mut out);
76 out.push(b'"');
77 }
78 for parameter in &self.extensions {
79 write_parameter(parameter, &mut out);
80 }
81 Bytes::from(out)
82 }
83
84 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
85 let value = trim(value);
86 let semi = value
87 .iter()
88 .position(|&b| b == b';')
89 .ok_or(HeaderError::Syntax { header: REASON })?;
90 let protocol = trim(value.get(..semi).unwrap_or(&[]));
91 if protocol.is_empty() || !protocol.iter().all(|&b| is_token_char(b)) {
92 return Err(HeaderError::Syntax { header: REASON });
93 }
94 let params = grammar::parse_params(value.get(semi..).unwrap_or(&[]), REASON)?;
95 let causes: Vec<_> = params.iter().filter(|p| p.is("cause")).collect();
96 let texts: Vec<_> = params.iter().filter(|p| p.is("text")).collect();
97 if causes.len() != 1 || texts.len() > 1 {
98 return Err(HeaderError::Syntax { header: REASON });
99 }
100 let cause_bytes = causes
101 .first()
102 .and_then(|p| p.value.as_deref())
103 .ok_or(HeaderError::Syntax { header: REASON })?;
104 let cause_u64 = grammar::parse_u64(cause_bytes, REASON)?;
105 let cause =
106 u16::try_from(cause_u64).map_err(|_| HeaderError::OutOfRange { header: REASON })?;
107 if protocol.eq_ignore_ascii_case(b"SIP") && !(100..=699).contains(&cause) {
108 return Err(HeaderError::OutOfRange { header: REASON });
109 }
110 if protocol.eq_ignore_ascii_case(b"Q.850") && cause > 127 {
111 return Err(HeaderError::OutOfRange { header: REASON });
112 }
113 let text = texts.first().and_then(|p| p.value.clone());
114 let extensions = params
115 .into_iter()
116 .filter(|p| !p.is("cause") && !p.is("text"))
117 .collect();
118 Ok(Self {
119 protocol: protocol.to_vec(),
120 cause,
121 text,
122 extensions,
123 })
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct Reason(pub Vec<ReasonValue>);
130
131impl Reason {
132 #[must_use]
134 pub fn to_bytes(&self) -> Bytes {
135 join(self.0.iter().map(ReasonValue::to_bytes))
136 }
137}
138
139impl From<ReasonValue> for Reason {
140 fn from(value: ReasonValue) -> Self {
141 Self(vec![value])
142 }
143}
144
145impl TypedHeader for Reason {
146 const NAME: HeaderName = HeaderName::Reason;
147
148 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
149 grammar::split_list(value, REASON)?
150 .into_iter()
151 .map(ReasonValue::decode)
152 .collect::<Result<Vec<_>, _>>()
153 .and_then(|values| {
154 (!values.is_empty())
155 .then_some(Self(values))
156 .ok_or(HeaderError::Syntax { header: REASON })
157 })
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
163pub struct HistoryIndex(Vec<u32>);
164
165impl HistoryIndex {
166 #[must_use]
168 pub fn first() -> Self {
169 Self(vec![1])
170 }
171
172 #[must_use]
174 pub fn forwarded(&self) -> Self {
175 let mut components = self.0.clone();
176 components.push(1);
177 Self(components)
178 }
179
180 #[must_use]
182 pub fn gap(&self) -> Self {
183 let mut components = self.0.clone();
184 components.push(0);
185 Self(components)
186 }
187
188 #[must_use]
190 pub fn to_bytes(&self) -> Bytes {
191 let mut out = Vec::new();
192 for (position, component) in self.0.iter().enumerate() {
193 if position != 0 {
194 out.push(b'.');
195 }
196 out.extend_from_slice(component.to_string().as_bytes());
197 }
198 Bytes::from(out)
199 }
200
201 fn parse(value: &[u8]) -> Result<Self, HeaderError> {
202 let mut components = Vec::new();
203 for component in value.split(|&b| b == b'.') {
204 if component.is_empty() || (component.len() > 1 && component.first() == Some(&b'0')) {
205 return Err(HeaderError::Syntax {
206 header: HISTORY_INFO,
207 });
208 }
209 let number = grammar::parse_u64(component, HISTORY_INFO)?;
210 components.push(u32::try_from(number).map_err(|_| HeaderError::OutOfRange {
211 header: HISTORY_INFO,
212 })?);
213 }
214 (!components.is_empty())
215 .then_some(Self(components))
216 .ok_or(HeaderError::Syntax {
217 header: HISTORY_INFO,
218 })
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, Eq)]
224pub enum TargetChange {
225 Rc(HistoryIndex),
227 Mp(HistoryIndex),
229 Np(HistoryIndex),
231}
232
233impl TargetChange {
234 fn index(&self) -> &HistoryIndex {
235 match self {
236 Self::Rc(index) | Self::Mp(index) | Self::Np(index) => index,
237 }
238 }
239
240 fn name(&self) -> &'static [u8] {
241 match self {
242 Self::Rc(_) => b"rc",
243 Self::Mp(_) => b"mp",
244 Self::Np(_) => b"np",
245 }
246 }
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum TargetChangeKind {
252 Rc,
254 Mp,
256 Np,
258}
259
260impl TargetChangeKind {
261 fn with(self, index: HistoryIndex) -> TargetChange {
262 match self {
263 Self::Rc => TargetChange::Rc(index),
264 Self::Mp => TargetChange::Mp(index),
265 Self::Np => TargetChange::Np(index),
266 }
267 }
268}
269
270#[derive(Debug, Clone)]
272pub struct HistoryEntry {
273 pub target: Uri,
275 pub index: HistoryIndex,
277 pub change: Option<TargetChange>,
279 extensions: Vec<HeaderParam>,
280}
281
282impl HistoryEntry {
283 #[must_use]
285 pub fn new(target: Uri, index: HistoryIndex, change: Option<TargetChange>) -> Self {
286 Self {
287 target,
288 index,
289 change,
290 extensions: Vec::new(),
291 }
292 }
293
294 pub fn reasons(&self) -> Result<Vec<ReasonValue>, HeaderError> {
296 let Some(headers) = self.target.headers() else {
297 return Ok(Vec::new());
298 };
299 let mut reasons = Vec::new();
300 for header in headers.iter().filter(|p| p.has_name("Reason")) {
301 let encoded = header
302 .value()
303 .ok_or(HeaderError::Syntax { header: REASON })?;
304 let decoded = escape::decode(encoded).ok_or(HeaderError::Syntax { header: REASON })?;
305 reasons.extend(Reason::decode(&decoded)?.0);
306 }
307 Ok(reasons)
308 }
309
310 fn embed_reason(&mut self, reason: &ReasonValue) {
311 let encoded = percent_encode(&reason.to_bytes());
312 let _ = self.target.push_header(Param::new(
313 Bytes::from_static(b"Reason"),
314 Bytes::from(encoded),
315 ));
316 }
317
318 fn wants_privacy(&self) -> Result<bool, HeaderError> {
319 let Some(headers) = self.target.headers() else {
320 return Ok(false);
321 };
322 for header in headers.iter().filter(|p| p.has_name("Privacy")) {
323 let encoded = header.value().ok_or(HeaderError::Syntax {
324 header: HISTORY_INFO,
325 })?;
326 let decoded = escape::decode(encoded).ok_or(HeaderError::Syntax {
327 header: HISTORY_INFO,
328 })?;
329 if decoded.eq_ignore_ascii_case(b"history") {
330 return Ok(true);
331 }
332 }
333 Ok(false)
334 }
335
336 fn anonymize(&mut self) -> Result<(), HeaderError> {
337 self.target = Uri::parse(Bytes::from_static(b"sip:anonymous@anonymous.invalid")).map_err(
338 |source| HeaderError::Uri {
339 header: HISTORY_INFO,
340 source,
341 },
342 )?;
343 self.extensions.clear();
344 Ok(())
345 }
346
347 #[must_use]
349 pub fn to_bytes(&self) -> Bytes {
350 let mut out = Vec::new();
351 out.push(b'<');
352 out.extend_from_slice(&self.target.to_bytes());
353 out.extend_from_slice(b">;index=");
354 out.extend_from_slice(&self.index.to_bytes());
355 if let Some(change) = &self.change {
356 out.push(b';');
357 out.extend_from_slice(change.name());
358 out.push(b'=');
359 out.extend_from_slice(&change.index().to_bytes());
360 }
361 for parameter in &self.extensions {
362 write_parameter(parameter, &mut out);
363 }
364 Bytes::from(out)
365 }
366
367 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
368 let address = Address::parse(value, HISTORY_INFO)?;
369 let indices: Vec<_> = address.params.iter().filter(|p| p.is("index")).collect();
370 if indices.len() != 1 {
371 return Err(HeaderError::Syntax {
372 header: HISTORY_INFO,
373 });
374 }
375 let index = HistoryIndex::parse(indices.first().and_then(|p| p.value.as_deref()).ok_or(
376 HeaderError::Syntax {
377 header: HISTORY_INFO,
378 },
379 )?)?;
380 let changes: Vec<_> = address
381 .params
382 .iter()
383 .filter(|p| p.is("rc") || p.is("mp") || p.is("np"))
384 .collect();
385 if changes.len() > 1 {
386 return Err(HeaderError::Syntax {
387 header: HISTORY_INFO,
388 });
389 }
390 let change = changes
391 .first()
392 .map(|parameter| {
393 let referenced = HistoryIndex::parse(parameter.value.as_deref().ok_or(
394 HeaderError::Syntax {
395 header: HISTORY_INFO,
396 },
397 )?)?;
398 Ok(if parameter.is("rc") {
399 TargetChange::Rc(referenced)
400 } else if parameter.is("mp") {
401 TargetChange::Mp(referenced)
402 } else {
403 TargetChange::Np(referenced)
404 })
405 })
406 .transpose()?;
407 let extensions = address
408 .params
409 .into_iter()
410 .filter(|p| !p.is("index") && !p.is("rc") && !p.is("mp") && !p.is("np"))
411 .collect();
412 Ok(Self {
413 target: address.uri,
414 index,
415 change,
416 extensions,
417 })
418 }
419}
420
421#[derive(Debug, Clone, Default)]
423pub struct HistoryInfo(pub Vec<HistoryEntry>);
424
425impl HistoryInfo {
426 pub fn from_headers(headers: &crate::message::Headers) -> Option<Result<Self, HeaderError>> {
432 let mut joined = Vec::new();
433 for header in headers.get_all(&HeaderName::HistoryInfo) {
434 if !joined.is_empty() {
435 joined.extend_from_slice(b", ");
436 }
437 joined.extend_from_slice(&header.value());
438 }
439 (!joined.is_empty()).then(|| Self::decode(&joined))
440 }
441
442 #[must_use]
444 pub fn initial(target: Uri) -> Self {
445 Self(vec![HistoryEntry::new(target, HistoryIndex::first(), None)])
446 }
447
448 #[must_use]
453 pub fn retargeted(
454 mut self,
455 previous: Uri,
456 next: Uri,
457 reason: &ReasonValue,
458 kind: TargetChangeKind,
459 ) -> Self {
460 if self.0.is_empty() {
461 self = Self::initial(previous.clone());
462 }
463 let last_matches = self
464 .0
465 .last()
466 .is_some_and(|entry| entry.target.equivalent(&previous));
467 if !last_matches {
468 let gap_index = self
469 .0
470 .last()
471 .map_or_else(HistoryIndex::first, |entry| entry.index.gap());
472 self.0.push(HistoryEntry::new(previous, gap_index, None));
473 }
474 let previous_index = self
475 .0
476 .last()
477 .map_or_else(HistoryIndex::first, |entry| entry.index.clone());
478 if let Some(entry) = self.0.last_mut() {
479 entry.embed_reason(reason);
480 }
481 self.0.push(HistoryEntry::new(
482 next,
483 previous_index.forwarded(),
484 Some(kind.with(previous_index)),
485 ));
486 self
487 }
488
489 pub fn apply_privacy(&mut self, message_privacy: bool) -> Result<(), HeaderError> {
491 for entry in &mut self.0 {
492 if message_privacy || entry.wants_privacy()? {
493 entry.anonymize()?;
494 }
495 }
496 Ok(())
497 }
498
499 pub fn apply_message_privacy(
502 &mut self,
503 headers: &crate::message::Headers,
504 ) -> Result<(), HeaderError> {
505 self.apply_privacy(message_requests_history_privacy(headers)?)
506 }
507
508 #[must_use]
510 pub fn to_bytes(&self) -> Bytes {
511 join(self.0.iter().map(HistoryEntry::to_bytes))
512 }
513}
514
515pub(crate) fn for_response(
521 request: &crate::message::Request,
522 status: crate::message::StatusCode,
523) -> Option<Bytes> {
524 if status.code() == 100 {
525 return None;
526 }
527 let mut history = if let Some(parsed) = HistoryInfo::from_headers(&request.headers) {
528 parsed.ok()?
529 } else {
530 let supported = request
531 .headers
532 .typed_all::<crate::headers::Supported>()
533 .filter_map(Result::ok)
534 .any(|tags| tags.contains("histinfo"));
535 if !supported {
536 return None;
537 }
538 HistoryInfo::initial(request.uri.clone())
539 };
540 let message_privacy = message_requests_history_privacy(&request.headers).ok()?;
541 history.apply_privacy(message_privacy).ok()?;
542 Some(history.to_bytes())
543}
544
545fn message_requests_history_privacy(
546 headers: &crate::message::Headers,
547) -> Result<bool, HeaderError> {
548 let mut requested = false;
549 for privacy in headers.typed_all::<crate::headers::Privacy>() {
550 let privacy = privacy?;
551 requested |= privacy.is(&crate::headers::PrivacyValue::History)
552 || privacy.is(&crate::headers::PrivacyValue::Header);
553 }
554 Ok(requested)
555}
556
557impl TypedHeader for HistoryInfo {
558 const NAME: HeaderName = HeaderName::HistoryInfo;
559
560 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
561 let entries = grammar::split_list(value, HISTORY_INFO)?
562 .into_iter()
563 .map(HistoryEntry::decode)
564 .collect::<Result<Vec<_>, _>>()?;
565 if entries.is_empty() {
566 return Err(HeaderError::Syntax {
567 header: HISTORY_INFO,
568 });
569 }
570 if entries
571 .first()
572 .is_none_or(|entry| entry.index != HistoryIndex::first())
573 {
574 return Err(HeaderError::Syntax {
575 header: HISTORY_INFO,
576 });
577 }
578 for (position, entry) in entries.iter().enumerate() {
579 if let Some(change) = &entry.change {
580 let prior = entries
581 .get(..position)
582 .unwrap_or(&[])
583 .iter()
584 .any(|candidate| candidate.index == *change.index());
585 if !prior {
586 return Err(HeaderError::Syntax {
587 header: HISTORY_INFO,
588 });
589 }
590 }
591 }
592 Ok(Self(entries))
593 }
594}
595
596fn join(values: impl Iterator<Item = Bytes>) -> Bytes {
597 let mut out = Vec::new();
598 for (position, value) in values.enumerate() {
599 if position != 0 {
600 out.extend_from_slice(b", ");
601 }
602 out.extend_from_slice(&value);
603 }
604 Bytes::from(out)
605}
606
607fn write_parameter(parameter: &HeaderParam, out: &mut Vec<u8>) {
608 out.push(b';');
609 out.extend_from_slice(¶meter.name);
610 if let Some(value) = ¶meter.value {
611 out.push(b'=');
612 if value.iter().all(|&b| is_token_char(b)) {
613 out.extend_from_slice(value);
614 } else {
615 out.push(b'"');
616 write_quoted(value, out);
617 out.push(b'"');
618 }
619 }
620}
621
622fn write_quoted(value: &[u8], out: &mut Vec<u8>) {
623 for &byte in value {
624 if matches!(byte, b'\\' | b'"') {
625 out.push(b'\\');
626 }
627 out.push(byte);
628 }
629}
630
631fn percent_encode(value: &[u8]) -> Vec<u8> {
632 const HEX: &[u8; 16] = b"0123456789ABCDEF";
633 let mut out = Vec::with_capacity(value.len());
634 for &byte in value {
635 if byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~') {
636 out.push(byte);
637 } else {
638 out.push(b'%');
639 out.push(*HEX.get(usize::from(byte >> 4)).unwrap_or(&b'0'));
640 out.push(*HEX.get(usize::from(byte & 0x0f)).unwrap_or(&b'0'));
641 }
642 }
643 out
644}
645
646#[cfg(test)]
647#[allow(
648 clippy::unwrap_used,
649 clippy::expect_used,
650 clippy::panic,
651 clippy::indexing_slicing
652)]
653mod tests {
654 use super::*;
655 use crate::message::StatusCode;
656
657 fn uri(value: &'static [u8]) -> Uri {
658 Uri::parse(Bytes::from_static(value)).unwrap()
659 }
660
661 #[test]
662 fn a_retargeted_request_carries_the_previous_target_and_the_reason_it_moved() {
663 let history = HistoryInfo::initial(uri(b"sip:alice@example.test")).retargeted(
664 uri(b"sip:alice@example.test"),
665 uri(b"sip:bob@example.test"),
666 &ReasonValue::sip(StatusCode::new(302).unwrap(), None),
667 TargetChangeKind::Mp,
668 );
669 assert_eq!(
670 history.to_bytes(),
671 Bytes::from_static(
672 b"<sip:alice@example.test?Reason=SIP%3Bcause%3D302>;index=1, <sip:bob@example.test>;index=1.1;mp=1"
673 )
674 );
675 assert_eq!(history.0[0].reasons().unwrap()[0].cause(), 302);
676 }
677
678 #[test]
679 fn a_missing_previous_target_gets_a_visible_zero_index() {
680 let history = HistoryInfo::initial(uri(b"sip:first@example.test")).retargeted(
681 uri(b"sip:hidden@example.test"),
682 uri(b"sip:last@example.test"),
683 &ReasonValue::sip(StatusCode::new(302).unwrap(), None),
684 TargetChangeKind::Mp,
685 );
686 assert_eq!(history.0[1].index.to_bytes(), Bytes::from_static(b"1.0"));
687 assert_eq!(history.0[2].index.to_bytes(), Bytes::from_static(b"1.0.1"));
688 }
689
690 #[test]
691 fn history_privacy_keeps_indices_and_hides_targets() {
692 let mut history = HistoryInfo::initial(uri(b"sip:alice@example.test")).retargeted(
693 uri(b"sip:alice@example.test"),
694 uri(b"sip:bob@example.test"),
695 &ReasonValue::sip(StatusCode::new(302).unwrap(), None),
696 TargetChangeKind::Mp,
697 );
698 history.apply_privacy(true).unwrap();
699 assert_eq!(
700 history.to_bytes(),
701 Bytes::from_static(
702 b"<sip:anonymous@anonymous.invalid>;index=1, <sip:anonymous@anonymous.invalid>;index=1.1;mp=1"
703 )
704 );
705 }
706
707 #[test]
708 fn typed_history_rejects_a_forward_target_reference() {
709 assert!(HistoryInfo::decode(b"<sip:a@b>;index=1;mp=1.1, <sip:c@d>;index=1.1").is_err());
710 }
711
712 #[test]
713 fn typed_history_requires_the_first_index_to_be_one() {
714 assert!(HistoryInfo::decode(b"<sip:a@b>;index=2").is_err());
715 assert!(HistoryInfo::decode(b"<sip:a@b>;index=1").is_ok());
716 }
717
718 #[test]
719 fn a_tel_target_does_not_receive_a_uri_reason_component() {
720 let history = HistoryInfo::initial(uri(b"tel:+12015550123")).retargeted(
721 uri(b"tel:+12015550123"),
722 uri(b"sip:bob@example.test"),
723 &ReasonValue::sip(StatusCode::new(302).unwrap(), None),
724 TargetChangeKind::Mp,
725 );
726 assert_eq!(
727 history.0[0].target.to_bytes(),
728 Bytes::from_static(b"tel:+12015550123")
729 );
730 assert!(history.0[0].reasons().unwrap().is_empty());
731 }
732
733 #[test]
734 fn reason_validates_protocol_specific_ranges() {
735 assert!(Reason::decode(b"SIP;cause=99").is_err());
736 assert!(Reason::decode(b"Q.850;cause=128").is_err());
737 assert_eq!(
738 Reason::decode(b"SIP;cause=486;text=Busy").unwrap().0[0].cause(),
739 486
740 );
741 }
742}