Skip to main content

sip_header/
header.rs

1//! Standard SIP header names and typed lookup trait (RFC 3261 and extensions).
2//!
3//! Protocol-agnostic catalog of SIP header names with canonical wire casing,
4//! plus a [`SipHeaderLookup`] trait providing typed convenience accessors for
5//! any key-value store that can look up headers by name.
6
7use crate::accept::{SipAccept, SipAcceptError};
8use crate::accept_encoding::{SipAcceptEncoding, SipAcceptEncodingError};
9use crate::accept_language::{SipAcceptLanguage, SipAcceptLanguageError};
10use crate::auth::{SipAuthError, SipAuthValue};
11use crate::contact::ContactValue;
12use crate::header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
13use crate::history_info::{HistoryInfo, HistoryInfoError};
14use crate::replaces::{SipReplaces, SipReplacesError};
15use crate::security::{SipSecurity, SipSecurityError};
16use crate::target_dialog::{SipTargetDialog, SipTargetDialogError};
17use crate::uri_info::{UriInfo, UriInfoError};
18use crate::via::{SipVia, SipViaError};
19use crate::warning::{SipWarning, SipWarningError};
20
21define_header_enum! {
22    tests_mod: sip_header_generated_tests,
23    error_type: ParseSipHeaderError => "unknown SIP header",
24    /// Standard SIP header names with canonical wire casing.
25    ///
26    /// Each variant maps to the header's canonical form as defined in the
27    /// relevant RFC. `FromStr` is case-insensitive; `Display` always emits
28    /// the canonical form.
29    pub enum SipHeader {
30        /// `Accept` (RFC 3261).
31        Accept => "Accept",
32        /// `Accept-Contact`.
33        AcceptContact => "Accept-Contact",
34        /// `Accept-Encoding` (RFC 3261).
35        AcceptEncoding => "Accept-Encoding",
36        /// `Accept-Language` (RFC 3261).
37        AcceptLanguage => "Accept-Language",
38        /// `Accept-Resource-Priority`.
39        AcceptResourcePriority => "Accept-Resource-Priority",
40        /// `Additional-Identity`.
41        AdditionalIdentity => "Additional-Identity",
42        /// `Alert-Info` (RFC 3261).
43        AlertInfo => "Alert-Info",
44        /// `AlertMsg-Error`.
45        AlertmsgError => "AlertMsg-Error",
46        /// `Allow` (RFC 3261).
47        Allow => "Allow",
48        /// `Allow-Events` (RFC 6665).
49        AllowEvents => "Allow-Events",
50        /// `Answer-Mode`.
51        AnswerMode => "Answer-Mode",
52        /// `Attestation-Info`.
53        AttestationInfo => "Attestation-Info",
54        /// `Authentication-Info`.
55        AuthenticationInfo => "Authentication-Info",
56        /// `Authorization` (RFC 3261).
57        Authorization => "Authorization",
58        /// `Call-ID` (RFC 3261).
59        CallId => "Call-ID",
60        /// `Call-Info` (RFC 3261).
61        CallInfo => "Call-Info",
62        /// `Cellular-Network-Info`.
63        CellularNetworkInfo => "Cellular-Network-Info",
64        /// `Contact` (RFC 3261).
65        Contact => "Contact",
66        /// `Content-Disposition` (RFC 3261).
67        ContentDisposition => "Content-Disposition",
68        /// `Content-Encoding` (RFC 3261).
69        ContentEncoding => "Content-Encoding",
70        /// `Content-ID`.
71        ContentId => "Content-ID",
72        /// `Content-Language` (RFC 3261).
73        ContentLanguage => "Content-Language",
74        /// `Content-Length` (RFC 3261).
75        ContentLength => "Content-Length",
76        /// `Content-Type` (RFC 3261).
77        ContentType => "Content-Type",
78        /// `CSeq` (RFC 3261).
79        Cseq => "CSeq",
80        /// `Date` (RFC 3261).
81        Date => "Date",
82        /// `DC-Info`.
83        DcInfo => "DC-Info",
84        /// `Encryption` (deprecated in RFC 3261).
85        Encryption => "Encryption",
86        /// `Error-Info` (RFC 3261).
87        ErrorInfo => "Error-Info",
88        /// `Event` (RFC 6665).
89        Event => "Event",
90        /// `Expires` (RFC 3261).
91        Expires => "Expires",
92        /// `Feature-Caps`.
93        FeatureCaps => "Feature-Caps",
94        /// `Flow-Timer`.
95        FlowTimer => "Flow-Timer",
96        /// `From` (RFC 3261).
97        From => "From",
98        /// `Geolocation` (RFC 6442).
99        Geolocation => "Geolocation",
100        /// `Geolocation-Error` (RFC 6442).
101        GeolocationError => "Geolocation-Error",
102        /// `Geolocation-Routing` (RFC 6442).
103        GeolocationRouting => "Geolocation-Routing",
104        /// `Hide` (deprecated in RFC 3261).
105        Hide => "Hide",
106        /// `History-Info` (RFC 7044).
107        HistoryInfo => "History-Info",
108        /// `Identity` (RFC 8224).
109        Identity => "Identity",
110        /// `Identity-Info`.
111        IdentityInfo => "Identity-Info",
112        /// `Info-Package`.
113        InfoPackage => "Info-Package",
114        /// `In-Reply-To` (RFC 3261).
115        InReplyTo => "In-Reply-To",
116        /// `Join` (RFC 3911).
117        Join => "Join",
118        /// `Max-Breadth`.
119        MaxBreadth => "Max-Breadth",
120        /// `Max-Forwards` (RFC 3261).
121        MaxForwards => "Max-Forwards",
122        /// `MIME-Version` (RFC 3261).
123        MimeVersion => "MIME-Version",
124        /// `Min-Expires` (RFC 3261).
125        MinExpires => "Min-Expires",
126        /// `Min-SE` (RFC 4028).
127        MinSe => "Min-SE",
128        /// `Organization` (RFC 3261).
129        Organization => "Organization",
130        /// `Origination-Id`.
131        OriginationId => "Origination-Id",
132        /// `P-Access-Network-Info`.
133        PAccessNetworkInfo => "P-Access-Network-Info",
134        /// `P-Answer-State`.
135        PAnswerState => "P-Answer-State",
136        /// `P-Asserted-Identity` (RFC 3325).
137        PAssertedIdentity => "P-Asserted-Identity",
138        /// `P-Asserted-Service`.
139        PAssertedService => "P-Asserted-Service",
140        /// `P-Associated-URI`.
141        PAssociatedUri => "P-Associated-URI",
142        /// `P-Called-Party-ID`.
143        PCalledPartyId => "P-Called-Party-ID",
144        /// `P-Charge-Info`.
145        PChargeInfo => "P-Charge-Info",
146        /// `P-Charging-Function-Addresses`.
147        PChargingFunctionAddresses => "P-Charging-Function-Addresses",
148        /// `P-Charging-Vector`.
149        PChargingVector => "P-Charging-Vector",
150        /// `P-DCS-Trace-Party-ID`.
151        PDcsTracePartyId => "P-DCS-Trace-Party-ID",
152        /// `P-DCS-OSPS`.
153        PDcsOsps => "P-DCS-OSPS",
154        /// `P-DCS-Billing-Info`.
155        PDcsBillingInfo => "P-DCS-Billing-Info",
156        /// `P-DCS-LAES`.
157        PDcsLaes => "P-DCS-LAES",
158        /// `P-DCS-Redirect`.
159        PDcsRedirect => "P-DCS-Redirect",
160        /// `P-Early-Media`.
161        PEarlyMedia => "P-Early-Media",
162        /// `P-Media-Authorization`.
163        PMediaAuthorization => "P-Media-Authorization",
164        /// `P-Preferred-Identity` (RFC 3325).
165        PPreferredIdentity => "P-Preferred-Identity",
166        /// `P-Preferred-Service`.
167        PPreferredService => "P-Preferred-Service",
168        /// `P-Private-Network-Indication`.
169        PPrivateNetworkIndication => "P-Private-Network-Indication",
170        /// `P-Profile-Key`.
171        PProfileKey => "P-Profile-Key",
172        /// `P-Refused-URI-List`.
173        PRefusedUriList => "P-Refused-URI-List",
174        /// `P-Served-User`.
175        PServedUser => "P-Served-User",
176        /// `P-User-Database`.
177        PUserDatabase => "P-User-Database",
178        /// `P-Visited-Network-ID`.
179        PVisitedNetworkId => "P-Visited-Network-ID",
180        /// `Path` (RFC 3327).
181        Path => "Path",
182        /// `Permission-Missing`.
183        PermissionMissing => "Permission-Missing",
184        /// `Policy-Contact`.
185        PolicyContact => "Policy-Contact",
186        /// `Policy-ID`.
187        PolicyId => "Policy-ID",
188        /// `Priority` (RFC 3261).
189        Priority => "Priority",
190        /// `Priority-Share`.
191        PriorityShare => "Priority-Share",
192        /// `Priority-Verstat`.
193        PriorityVerstat => "Priority-Verstat",
194        /// `Priv-Answer-Mode`.
195        PrivAnswerMode => "Priv-Answer-Mode",
196        /// `Privacy` (RFC 3323).
197        Privacy => "Privacy",
198        /// `Proxy-Authenticate` (RFC 3261).
199        ProxyAuthenticate => "Proxy-Authenticate",
200        /// `Proxy-Authorization` (RFC 3261).
201        ProxyAuthorization => "Proxy-Authorization",
202        /// `Proxy-Require` (RFC 3261).
203        ProxyRequire => "Proxy-Require",
204        /// `RAck`.
205        Rack => "RAck",
206        /// `Reason` (RFC 3326).
207        Reason => "Reason",
208        /// `Reason-Phrase`.
209        ReasonPhrase => "Reason-Phrase",
210        /// `Record-Route` (RFC 3261).
211        RecordRoute => "Record-Route",
212        /// `Recv-Info`.
213        RecvInfo => "Recv-Info",
214        /// `Refer-Events-At`.
215        ReferEventsAt => "Refer-Events-At",
216        /// `Refer-Sub`.
217        ReferSub => "Refer-Sub",
218        /// `Refer-To` (RFC 3515).
219        ReferTo => "Refer-To",
220        /// `Referred-By` (RFC 3892).
221        ReferredBy => "Referred-By",
222        /// `Reject-Contact`.
223        RejectContact => "Reject-Contact",
224        /// `Relayed-Charge`.
225        RelayedCharge => "Relayed-Charge",
226        /// `Replaces` (RFC 3891).
227        Replaces => "Replaces",
228        /// `Reply-To` (RFC 3261).
229        ReplyTo => "Reply-To",
230        /// `Request-Disposition`.
231        RequestDisposition => "Request-Disposition",
232        /// `Require` (RFC 3261).
233        Require => "Require",
234        /// `Resource-Priority`.
235        ResourcePriority => "Resource-Priority",
236        /// `Resource-Share`.
237        ResourceShare => "Resource-Share",
238        /// `Response-Key` (deprecated in RFC 3261).
239        ResponseKey => "Response-Key",
240        /// `Response-Source`.
241        ResponseSource => "Response-Source",
242        /// `Restoration-Info`.
243        RestorationInfo => "Restoration-Info",
244        /// `Retry-After` (RFC 3261).
245        RetryAfter => "Retry-After",
246        /// `Route` (RFC 3261).
247        Route => "Route",
248        /// `RSeq`.
249        Rseq => "RSeq",
250        /// `Security-Client` (RFC 3329).
251        SecurityClient => "Security-Client",
252        /// `Security-Server` (RFC 3329).
253        SecurityServer => "Security-Server",
254        /// `Security-Verify` (RFC 3329).
255        SecurityVerify => "Security-Verify",
256        /// `Server` (RFC 3261).
257        Server => "Server",
258        /// `Service-Interact-Info`.
259        ServiceInteractInfo => "Service-Interact-Info",
260        /// `Service-Route` (RFC 3608).
261        ServiceRoute => "Service-Route",
262        /// `Session-Expires` (RFC 4028).
263        SessionExpires => "Session-Expires",
264        /// `Session-ID`.
265        SessionId => "Session-ID",
266        /// `SIP-ETag`.
267        SipEtag => "SIP-ETag",
268        /// `SIP-If-Match`.
269        SipIfMatch => "SIP-If-Match",
270        /// `Subject` (RFC 3261).
271        Subject => "Subject",
272        /// `Subscription-State` (RFC 6665).
273        SubscriptionState => "Subscription-State",
274        /// `Supported` (RFC 3261).
275        Supported => "Supported",
276        /// `Suppress-If-Match`.
277        SuppressIfMatch => "Suppress-If-Match",
278        /// `Target-Dialog` (RFC 4538).
279        TargetDialog => "Target-Dialog",
280        /// `Timestamp` (RFC 3261).
281        Timestamp => "Timestamp",
282        /// `To` (RFC 3261).
283        To => "To",
284        /// `Trigger-Consent`.
285        TriggerConsent => "Trigger-Consent",
286        /// `Unsupported` (RFC 3261).
287        Unsupported => "Unsupported",
288        /// `User-Agent` (RFC 3261).
289        UserAgent => "User-Agent",
290        /// `User-to-User` (RFC 7433).
291        UserToUser => "User-to-User",
292        /// `Via` (RFC 3261).
293        Via => "Via",
294        /// `Warning` (RFC 3261).
295        Warning => "Warning",
296        /// `WWW-Authenticate` (RFC 3261).
297        WwwAuthenticate => "WWW-Authenticate",
298        // Draft headers — appended after IANA variants to preserve discriminants.
299        /// `Diversion` (draft-levy-sip-diversion-08, superseded by RFC 7044).
300        #[cfg(feature = "draft")]
301        Diversion => "Diversion",
302        /// `Remote-Party-ID` (draft-ietf-sip-privacy-01, superseded by RFC 3325).
303        #[cfg(feature = "draft")]
304        RemotePartyId => "Remote-Party-ID",
305    }
306}
307
308/// RFC 3261 §7.3.3 compact header form mappings.
309///
310/// Includes forms from RFC 3261, RFC 3515, RFC 3841, RFC 3892, RFC 4028,
311/// RFC 4474, and RFC 6665.
312const COMPACT_FORMS: &[(u8, SipHeader)] = &[
313    (b'a', SipHeader::AcceptContact),
314    (b'b', SipHeader::ReferredBy),
315    (b'c', SipHeader::ContentType),
316    (b'd', SipHeader::RequestDisposition),
317    (b'e', SipHeader::ContentEncoding),
318    (b'f', SipHeader::From),
319    (b'i', SipHeader::CallId),
320    (b'j', SipHeader::RejectContact),
321    (b'k', SipHeader::Supported),
322    (b'l', SipHeader::ContentLength),
323    (b'm', SipHeader::Contact),
324    (b'n', SipHeader::IdentityInfo),
325    (b'o', SipHeader::Event),
326    (b'r', SipHeader::ReferTo),
327    (b's', SipHeader::Subject),
328    (b't', SipHeader::To),
329    (b'u', SipHeader::AllowEvents),
330    (b'v', SipHeader::Via),
331    (b'x', SipHeader::SessionExpires),
332    (b'y', SipHeader::Identity),
333];
334
335impl SipHeader {
336    /// Resolve a compact form letter to the corresponding header (RFC 3261 §7.3.3).
337    ///
338    /// Case-insensitive: both `'f'` and `'F'` resolve to [`SipHeader::From`].
339    pub fn from_compact(ch: u8) -> Option<Self> {
340        let lower = ch.to_ascii_lowercase();
341        COMPACT_FORMS
342            .iter()
343            .find(|(c, _)| *c == lower)
344            .map(|(_, h)| *h)
345    }
346
347    /// Return the compact form letter for this header, if one exists.
348    pub fn compact_form(&self) -> Option<char> {
349        COMPACT_FORMS
350            .iter()
351            .find(|(_, h)| h == self)
352            .map(|(c, _)| *c as char)
353    }
354
355    /// Whether this header may appear multiple times in a SIP message.
356    ///
357    /// Headers listed here use comma-separated or repeated-header semantics
358    /// per RFC 3261 §7.3.1 and their defining RFCs.
359    pub fn is_multi_valued(&self) -> bool {
360        if matches!(
361            self,
362            // RFC 3261 core
363            Self::Via
364                | Self::Route
365                | Self::RecordRoute
366                | Self::Contact
367                | Self::Allow
368                | Self::Supported
369                | Self::Require
370                | Self::ProxyRequire
371                | Self::Unsupported
372                | Self::Authorization
373                | Self::ProxyAuthorization
374                | Self::WwwAuthenticate
375                | Self::ProxyAuthenticate
376                | Self::Warning
377                | Self::ErrorInfo
378                | Self::CallInfo
379                | Self::AlertInfo
380                | Self::Accept
381                | Self::AcceptEncoding
382                | Self::AcceptLanguage
383                | Self::ContentEncoding
384                | Self::ContentLanguage
385                | Self::InReplyTo
386                // RFC 3325
387                | Self::PAssertedIdentity
388                | Self::PPreferredIdentity
389                // RFC 6665
390                | Self::AllowEvents
391                // RFC 3329
392                | Self::SecurityClient
393                | Self::SecurityServer
394                | Self::SecurityVerify
395                // RFC 3327
396                | Self::Path
397                // RFC 3608
398                | Self::ServiceRoute
399                // RFC 7044
400                | Self::HistoryInfo
401        ) {
402            return true;
403        }
404
405        #[cfg(feature = "draft")]
406        if matches!(
407            self,
408            // draft-levy-sip-diversion-08
409            Self::Diversion
410                // draft-ietf-sip-privacy-01
411                | Self::RemotePartyId
412        ) {
413            return true;
414        }
415
416        false
417    }
418
419    /// Parse a header name, including RFC 3261 §7.3.3 compact forms.
420    ///
421    /// Tries compact form resolution for single-character input, then
422    /// falls back to case-insensitive canonical name matching.
423    pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
424        if name.len() == 1 {
425            if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
426                return Ok(h);
427            }
428        }
429        name.parse()
430    }
431}
432
433/// Trait for looking up standard SIP headers from any key-value store.
434///
435/// Implementors provide `sip_header_str()` and get all typed accessors as
436/// default implementations.
437///
438/// # Example
439///
440/// ```
441/// use std::collections::HashMap;
442/// use sip_header::{SipHeaderLookup, SipHeader};
443///
444/// let mut headers = HashMap::new();
445/// headers.insert(
446///     "Call-Info".to_string(),
447///     "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId".to_string(),
448/// );
449///
450/// assert_eq!(
451///     headers.sip_header(SipHeader::CallInfo),
452///     Some("<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"),
453/// );
454///
455/// let ci = headers.call_info().unwrap().unwrap();
456/// assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
457/// ```
458pub trait SipHeaderLookup {
459    /// Look up a SIP header by its canonical name (e.g. `"Call-Info"`).
460    fn sip_header_str(&self, name: &str) -> Option<&str>;
461
462    /// Look up a SIP header by its [`SipHeader`] enum variant.
463    fn sip_header(&self, name: SipHeader) -> Option<&str> {
464        self.sip_header_str(name.as_str())
465    }
466
467    /// Return all occurrences of a header by canonical name.
468    ///
469    /// Unlike [`sip_header_str`](SipHeaderLookup::sip_header_str) which returns
470    /// at most one value, this method returns every occurrence. The default
471    /// implementation wraps `sip_header_str` in a single-element `Vec`; storage
472    /// backends that preserve per-occurrence values (e.g. `HashMap<String,
473    /// Vec<String>>`) should override this.
474    fn sip_header_all_str<'a>(&'a self, name: &str) -> Vec<&'a str> {
475        self.sip_header_str(name)
476            .into_iter()
477            .collect()
478    }
479
480    /// Return all occurrences of a header by [`SipHeader`] variant.
481    fn sip_header_all(&self, name: SipHeader) -> Vec<&str> {
482        self.sip_header_all_str(name.as_str())
483    }
484
485    /// Parse the `Call-Info` header into a [`UriInfo`].
486    ///
487    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
488    fn call_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
489        let rows = self.sip_header_all(SipHeader::CallInfo);
490        if rows.is_empty() {
491            return Ok(None);
492        }
493        UriInfo::from_entries(split_all(rows)).map(Some)
494    }
495
496    /// Parse the `History-Info` header into a [`HistoryInfo`].
497    ///
498    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
499    fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
500        let rows = self.sip_header_all(SipHeader::HistoryInfo);
501        if rows.is_empty() {
502            return Ok(None);
503        }
504        HistoryInfo::from_entries(split_all(rows)).map(Some)
505    }
506
507    /// Parse `P-Asserted-Identity` into a list of [`SipHeaderAddr`].
508    ///
509    /// PAI is multi-valued per RFC 3325 — a message may assert up to two
510    /// identities. Returns an empty `Vec` if the header is absent.
511    fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
512        parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
513    }
514
515    /// Parse `P-Preferred-Identity` into a list of [`SipHeaderAddr`] (RFC 3325).
516    fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
517        parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
518    }
519
520    /// Parse `Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.34).
521    fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
522        parse_addr_list(self.sip_header_all(SipHeader::Route))
523    }
524
525    /// Parse `Record-Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.30).
526    fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
527        parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
528    }
529
530    /// Parse `Path` into a list of [`SipHeaderAddr`] (RFC 3327).
531    fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
532        parse_addr_list(self.sip_header_all(SipHeader::Path))
533    }
534
535    /// Parse `Service-Route` into a list of [`SipHeaderAddr`] (RFC 3608).
536    fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
537        parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
538    }
539
540    /// Parse `Contact` into a list of [`ContactValue`] (RFC 3261 §20.10).
541    ///
542    /// The Contact header may contain `*` (wildcard, used in REGISTER) or
543    /// a comma-separated list of name-addr/addr-spec entries.
544    fn contact(&self) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
545        crate::contact::parse_contact_entries(split_all(self.sip_header_all(SipHeader::Contact)))
546    }
547
548    /// Parse `Alert-Info` into a [`UriInfo`] (RFC 3261 §20.4).
549    fn alert_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
550        let rows = self.sip_header_all(SipHeader::AlertInfo);
551        if rows.is_empty() {
552            return Ok(None);
553        }
554        UriInfo::from_entries(split_all(rows)).map(Some)
555    }
556
557    /// Parse `Error-Info` into a [`UriInfo`] (RFC 3261 §20.18).
558    fn error_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
559        let rows = self.sip_header_all(SipHeader::ErrorInfo);
560        if rows.is_empty() {
561            return Ok(None);
562        }
563        UriInfo::from_entries(split_all(rows)).map(Some)
564    }
565
566    /// `Allow` header values as individual method tokens (RFC 3261 §20.5).
567    fn allow(&self) -> Vec<&str> {
568        split_trim(self.sip_header_all(SipHeader::Allow))
569    }
570
571    /// `Supported` header values as individual option-tag tokens (RFC 3261 §20.37).
572    fn supported(&self) -> Vec<&str> {
573        split_trim(self.sip_header_all(SipHeader::Supported))
574    }
575
576    /// `Require` header values as individual option-tag tokens (RFC 3261 §20.32).
577    fn require_header(&self) -> Vec<&str> {
578        split_trim(self.sip_header_all(SipHeader::Require))
579    }
580
581    /// `Proxy-Require` values as individual option-tag tokens (RFC 3261 §20.29).
582    fn proxy_require(&self) -> Vec<&str> {
583        split_trim(self.sip_header_all(SipHeader::ProxyRequire))
584    }
585
586    /// `Unsupported` values as individual option-tag tokens (RFC 3261 §20.40).
587    fn unsupported(&self) -> Vec<&str> {
588        split_trim(self.sip_header_all(SipHeader::Unsupported))
589    }
590
591    /// `Allow-Events` values as individual event-type tokens (RFC 6665).
592    fn allow_events(&self) -> Vec<&str> {
593        split_trim(self.sip_header_all(SipHeader::AllowEvents))
594    }
595
596    /// `Content-Encoding` values as individual tokens (RFC 3261 §20.12).
597    fn content_encoding(&self) -> Vec<&str> {
598        split_trim(self.sip_header_all(SipHeader::ContentEncoding))
599    }
600
601    /// `Content-Language` values as individual language tags (RFC 3261 §20.13).
602    fn content_language(&self) -> Vec<&str> {
603        split_trim(self.sip_header_all(SipHeader::ContentLanguage))
604    }
605
606    /// `In-Reply-To` values as individual Call-ID tokens (RFC 3261 §20.21).
607    fn in_reply_to(&self) -> Vec<&str> {
608        split_trim(self.sip_header_all(SipHeader::InReplyTo))
609    }
610
611    /// Parse `Via` into a [`SipVia`] (RFC 3261 §20.42).
612    fn via(&self) -> Result<Option<SipVia>, SipViaError> {
613        let rows = self.sip_header_all(SipHeader::Via);
614        if rows.is_empty() {
615            return Ok(None);
616        }
617        SipVia::from_entries(split_all(rows)).map(Some)
618    }
619
620    /// Parse `Replaces` into a [`SipReplaces`] (RFC 3891 §6.1).
621    fn replaces(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
622        match self.sip_header(SipHeader::Replaces) {
623            Some(s) => SipReplaces::parse(s).map(Some),
624            None => Ok(None),
625        }
626    }
627
628    /// Parse `Join` into a [`SipReplaces`] (RFC 3911 §7.1).
629    ///
630    /// Join shares the Replaces grammar (`callid;to-tag=x;from-tag=y`), so
631    /// it reuses the same type.
632    fn join(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
633        match self.sip_header(SipHeader::Join) {
634            Some(s) => SipReplaces::parse(s).map(Some),
635            None => Ok(None),
636        }
637    }
638
639    /// Parse `Target-Dialog` into a [`SipTargetDialog`] (RFC 4538 §7).
640    fn target_dialog(&self) -> Result<Option<SipTargetDialog>, SipTargetDialogError> {
641        match self.sip_header(SipHeader::TargetDialog) {
642            Some(s) => SipTargetDialog::parse(s).map(Some),
643            None => Ok(None),
644        }
645    }
646
647    /// Parse `Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.7).
648    ///
649    /// Auth headers MUST NOT be comma-combined (RFC 3261 §7.3.1), so each
650    /// occurrence is parsed separately via [`sip_header_all`](SipHeaderLookup::sip_header_all).
651    fn authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
652        self.sip_header_all(SipHeader::Authorization)
653            .into_iter()
654            .map(|s| s.parse::<SipAuthValue>())
655            .collect()
656    }
657
658    /// Parse `Proxy-Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.28).
659    fn proxy_authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
660        self.sip_header_all(SipHeader::ProxyAuthorization)
661            .into_iter()
662            .map(|s| s.parse::<SipAuthValue>())
663            .collect()
664    }
665
666    /// Parse `WWW-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.44).
667    fn www_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
668        self.sip_header_all(SipHeader::WwwAuthenticate)
669            .into_iter()
670            .map(|s| s.parse::<SipAuthValue>())
671            .collect()
672    }
673
674    /// Parse `Proxy-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.27).
675    fn proxy_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
676        self.sip_header_all(SipHeader::ProxyAuthenticate)
677            .into_iter()
678            .map(|s| s.parse::<SipAuthValue>())
679            .collect()
680    }
681
682    /// Parse `Warning` into a [`SipWarning`] (RFC 3261 §20.43).
683    fn warning(&self) -> Result<Option<SipWarning>, SipWarningError> {
684        let rows = self.sip_header_all(SipHeader::Warning);
685        if rows.is_empty() {
686            return Ok(None);
687        }
688        SipWarning::from_entries(split_all(rows)).map(Some)
689    }
690
691    /// Parse `Security-Client` into a [`SipSecurity`] (RFC 3329).
692    fn security_client(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
693        let rows = self.sip_header_all(SipHeader::SecurityClient);
694        if rows.is_empty() {
695            return Ok(None);
696        }
697        SipSecurity::from_entries(split_all(rows)).map(Some)
698    }
699
700    /// Parse `Security-Server` into a [`SipSecurity`] (RFC 3329).
701    fn security_server(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
702        let rows = self.sip_header_all(SipHeader::SecurityServer);
703        if rows.is_empty() {
704            return Ok(None);
705        }
706        SipSecurity::from_entries(split_all(rows)).map(Some)
707    }
708
709    /// Parse `Security-Verify` into a [`SipSecurity`] (RFC 3329).
710    fn security_verify(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
711        let rows = self.sip_header_all(SipHeader::SecurityVerify);
712        if rows.is_empty() {
713            return Ok(None);
714        }
715        SipSecurity::from_entries(split_all(rows)).map(Some)
716    }
717
718    /// Parse `Accept` into a [`SipAccept`] (RFC 3261 §20.1).
719    fn accept(&self) -> Result<Option<SipAccept>, SipAcceptError> {
720        let rows = self.sip_header_all(SipHeader::Accept);
721        if rows.is_empty() {
722            return Ok(None);
723        }
724        SipAccept::from_entries(split_all(rows)).map(Some)
725    }
726
727    /// Parse `Accept-Encoding` into a [`SipAcceptEncoding`] (RFC 3261 §20.2).
728    fn accept_encoding(&self) -> Result<Option<SipAcceptEncoding>, SipAcceptEncodingError> {
729        let rows = self.sip_header_all(SipHeader::AcceptEncoding);
730        if rows.is_empty() {
731            return Ok(None);
732        }
733        SipAcceptEncoding::from_entries(split_all(rows)).map(Some)
734    }
735
736    /// Parse `Accept-Language` into a [`SipAcceptLanguage`] (RFC 3261 §20.3).
737    fn accept_language(&self) -> Result<Option<SipAcceptLanguage>, SipAcceptLanguageError> {
738        let rows = self.sip_header_all(SipHeader::AcceptLanguage);
739        if rows.is_empty() {
740            return Ok(None);
741        }
742        SipAcceptLanguage::from_entries(split_all(rows)).map(Some)
743    }
744
745    /// Parse `Diversion` into a list of [`SipHeaderAddr`] (draft-levy-sip-diversion-08).
746    #[cfg(feature = "draft")]
747    fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
748        parse_addr_list(self.sip_header_all(SipHeader::Diversion))
749    }
750
751    /// Parse `Remote-Party-ID` into a list of [`SipHeaderAddr`] (draft-ietf-sip-privacy-01).
752    #[cfg(feature = "draft")]
753    fn remote_party_id(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
754        parse_addr_list(self.sip_header_all(SipHeader::RemotePartyId))
755    }
756}
757
758fn split_all(rows: Vec<&str>) -> impl Iterator<Item = &str> {
759    rows.into_iter()
760        .flat_map(crate::split_comma_entries)
761}
762
763fn parse_addr_list(rows: Vec<&str>) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
764    split_all(rows)
765        .map(|s| {
766            s.trim()
767                .parse::<SipHeaderAddr>()
768        })
769        .collect()
770}
771
772fn split_trim(rows: Vec<&str>) -> Vec<&str> {
773    split_all(rows)
774        .map(str::trim)
775        .collect()
776}
777
778impl SipHeaderLookup for std::collections::HashMap<String, String> {
779    fn sip_header_str(&self, name: &str) -> Option<&str> {
780        self.get(name)
781            .map(|s| s.as_str())
782    }
783}
784
785impl SipHeaderLookup for std::collections::HashMap<String, Vec<String>> {
786    fn sip_header_str(&self, name: &str) -> Option<&str> {
787        self.get(name)
788            .and_then(|v| v.first())
789            .map(|s| s.as_str())
790    }
791
792    fn sip_header_all_str(&self, name: &str) -> Vec<&str> {
793        self.get(name)
794            .map(|v| {
795                v.iter()
796                    .map(|s| s.as_str())
797                    .collect()
798            })
799            .unwrap_or_default()
800    }
801}
802
803#[cfg(test)]
804mod tests {
805    use super::*;
806    use std::collections::HashMap;
807
808    #[test]
809    fn display_round_trip() {
810        assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
811        assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
812        assert_eq!(
813            SipHeader::PAssertedIdentity.to_string(),
814            "P-Asserted-Identity"
815        );
816    }
817
818    #[test]
819    fn as_ref_str() {
820        let h: &str = SipHeader::CallInfo.as_ref();
821        assert_eq!(h, "Call-Info");
822    }
823
824    #[test]
825    fn from_str_case_insensitive() {
826        assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
827        assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
828        assert_eq!(
829            "history-info".parse::<SipHeader>(),
830            Ok(SipHeader::HistoryInfo)
831        );
832        assert_eq!(
833            "p-asserted-identity".parse::<SipHeader>(),
834            Ok(SipHeader::PAssertedIdentity)
835        );
836        assert_eq!(
837            "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
838            Ok(SipHeader::PAssertedIdentity)
839        );
840    }
841
842    #[test]
843    fn from_str_unknown() {
844        assert!("X-Custom"
845            .parse::<SipHeader>()
846            .is_err());
847    }
848
849    fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
850        pairs
851            .iter()
852            .map(|(k, v)| (k.to_string(), v.to_string()))
853            .collect()
854    }
855
856    #[test]
857    fn sip_header_by_enum() {
858        let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
859        assert_eq!(
860            h.sip_header(SipHeader::CallInfo),
861            Some("<urn:x>;purpose=icon")
862        );
863    }
864
865    #[test]
866    fn call_info_raw_lookup() {
867        let h = headers_with(&[(
868            "Call-Info",
869            "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
870        )]);
871        assert_eq!(
872            h.sip_header(SipHeader::CallInfo),
873            Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
874        );
875    }
876
877    #[test]
878    fn call_info_typed() {
879        let h = headers_with(&[(
880            "Call-Info",
881            "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
882        )]);
883        let ci = h
884            .call_info()
885            .unwrap()
886            .unwrap();
887        assert_eq!(ci.len(), 1);
888        assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
889    }
890
891    #[test]
892    fn call_info_absent() {
893        let h = headers_with(&[]);
894        assert_eq!(
895            h.call_info()
896                .unwrap(),
897            None
898        );
899    }
900
901    #[test]
902    fn p_asserted_identity_typed() {
903        let h = headers_with(&[(
904            "P-Asserted-Identity",
905            r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
906        )]);
907        let pais = h
908            .p_asserted_identity()
909            .unwrap();
910        assert_eq!(pais.len(), 1);
911        assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
912    }
913
914    #[test]
915    fn p_asserted_identity_multi_value() {
916        let h = headers_with(&[(
917            "P-Asserted-Identity",
918            r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>, <tel:+15551234567>"#,
919        )]);
920        let pais = h
921            .p_asserted_identity()
922            .unwrap();
923        assert_eq!(pais.len(), 2);
924        assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
925        assert!(pais[1]
926            .uri()
927            .to_string()
928            .contains("+15551234567"));
929    }
930
931    #[test]
932    fn p_asserted_identity_absent() {
933        let h = headers_with(&[]);
934        assert!(h
935            .p_asserted_identity()
936            .unwrap()
937            .is_empty());
938    }
939
940    #[test]
941    fn history_info_raw_lookup() {
942        let h = headers_with(&[(
943            "History-Info",
944            "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
945        )]);
946        assert!(h
947            .sip_header(SipHeader::HistoryInfo)
948            .unwrap()
949            .contains("esrp.example.com"));
950    }
951
952    #[test]
953    fn history_info_typed() {
954        let h = headers_with(&[(
955            "History-Info",
956            "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
957        )]);
958        let hi = h
959            .history_info()
960            .unwrap()
961            .unwrap();
962        assert_eq!(hi.len(), 2);
963        assert_eq!(hi.entries()[0].index(), Some("1"));
964        assert_eq!(hi.entries()[1].index(), Some("1.1"));
965    }
966
967    #[test]
968    fn history_info_absent() {
969        let h = headers_with(&[]);
970        assert_eq!(
971            h.history_info()
972                .unwrap(),
973            None
974        );
975    }
976
977    #[test]
978    fn sip_header_all_str_default() {
979        let h = headers_with(&[("Via", "SIP/2.0/UDP host1")]);
980        let all = h.sip_header_all(SipHeader::Via);
981        assert_eq!(all.len(), 1);
982        assert_eq!(all[0], "SIP/2.0/UDP host1");
983    }
984
985    #[test]
986    fn sip_header_all_str_absent() {
987        let h = headers_with(&[]);
988        assert!(h
989            .sip_header_all(SipHeader::Via)
990            .is_empty());
991    }
992
993    #[test]
994    fn hashmap_vec_impl() {
995        let mut h: HashMap<String, Vec<String>> = HashMap::new();
996        h.insert(
997            "Via".into(),
998            vec!["SIP/2.0/UDP host1".into(), "SIP/2.0/UDP host2".into()],
999        );
1000        assert_eq!(h.sip_header_str("Via"), Some("SIP/2.0/UDP host1"));
1001        let all = h.sip_header_all_str("Via");
1002        assert_eq!(all.len(), 2);
1003        assert_eq!(all[0], "SIP/2.0/UDP host1");
1004        assert_eq!(all[1], "SIP/2.0/UDP host2");
1005    }
1006
1007    #[test]
1008    fn extract_from_sip_message() {
1009        let msg = concat!(
1010            "INVITE sip:bob@host SIP/2.0\r\n",
1011            "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
1012            "History-Info: <sip:esrp@example.com>;index=1\r\n",
1013            "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
1014            "\r\n",
1015        );
1016        let ci = SipHeader::CallInfo.extract_from(msg);
1017        assert_eq!(ci.len(), 1);
1018        assert_eq!(
1019            ci[0],
1020            "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"
1021        );
1022
1023        let hi = SipHeader::HistoryInfo.extract_from(msg);
1024        assert_eq!(hi.len(), 1);
1025        assert_eq!(hi[0], "<sip:esrp@example.com>;index=1");
1026
1027        let pai = SipHeader::PAssertedIdentity.extract_from(msg);
1028        assert_eq!(pai.len(), 1);
1029        assert_eq!(pai[0], "\"Corp\" <sip:+15551234567@198.51.100.1>");
1030    }
1031
1032    #[test]
1033    fn extract_from_missing() {
1034        let msg = concat!(
1035            "INVITE sip:bob@host SIP/2.0\r\n",
1036            "From: Alice <sip:alice@host>\r\n",
1037            "\r\n",
1038        );
1039        assert!(SipHeader::CallInfo
1040            .extract_from(msg)
1041            .is_empty());
1042        assert!(SipHeader::PAssertedIdentity
1043            .extract_from(msg)
1044            .is_empty());
1045    }
1046
1047    #[test]
1048    fn missing_headers_return_none() {
1049        let h = headers_with(&[]);
1050        assert_eq!(h.sip_header(SipHeader::CallInfo), None);
1051        assert_eq!(
1052            h.call_info()
1053                .unwrap(),
1054            None
1055        );
1056        assert_eq!(h.sip_header(SipHeader::HistoryInfo), None);
1057        assert_eq!(
1058            h.history_info()
1059                .unwrap(),
1060            None
1061        );
1062        assert_eq!(h.sip_header(SipHeader::PAssertedIdentity), None);
1063        assert!(h
1064            .p_asserted_identity()
1065            .unwrap()
1066            .is_empty());
1067    }
1068
1069    #[test]
1070    fn route_accessor() {
1071        let h = headers_with(&[(
1072            "Route",
1073            "<sip:proxy1.example.com;lr>, <sip:proxy2.example.com;lr>",
1074        )]);
1075        let routes = h
1076            .route()
1077            .unwrap();
1078        assert_eq!(routes.len(), 2);
1079        assert!(routes[0]
1080            .uri()
1081            .to_string()
1082            .contains("proxy1"));
1083        assert!(routes[1]
1084            .uri()
1085            .to_string()
1086            .contains("proxy2"));
1087    }
1088
1089    #[test]
1090    fn route_absent() {
1091        let h = headers_with(&[]);
1092        assert!(h
1093            .route()
1094            .unwrap()
1095            .is_empty());
1096    }
1097
1098    #[test]
1099    fn record_route_accessor() {
1100        let h = headers_with(&[("Record-Route", "<sip:ss1.example.com;lr>")]);
1101        let rr = h
1102            .record_route()
1103            .unwrap();
1104        assert_eq!(rr.len(), 1);
1105    }
1106
1107    #[test]
1108    fn allow_accessor() {
1109        let h = headers_with(&[("Allow", "INVITE, ACK, OPTIONS, BYE")]);
1110        let methods = h.allow();
1111        assert_eq!(methods, vec!["INVITE", "ACK", "OPTIONS", "BYE"]);
1112    }
1113
1114    #[test]
1115    fn allow_absent() {
1116        let h = headers_with(&[]);
1117        assert!(h
1118            .allow()
1119            .is_empty());
1120    }
1121
1122    #[test]
1123    fn supported_accessor() {
1124        let h = headers_with(&[("Supported", "100rel, timer")]);
1125        let opts = h.supported();
1126        assert_eq!(opts, vec!["100rel", "timer"]);
1127    }
1128
1129    #[test]
1130    fn require_header_accessor() {
1131        let h = headers_with(&[("Require", "100rel")]);
1132        assert_eq!(h.require_header(), vec!["100rel"]);
1133    }
1134
1135    #[test]
1136    fn alert_info_accessor() {
1137        let h = headers_with(&[("Alert-Info", "<http://www.example.com/sounds/moo.wav>")]);
1138        let ai = h
1139            .alert_info()
1140            .unwrap()
1141            .unwrap();
1142        assert_eq!(ai.len(), 1);
1143        assert!(ai.entries()[0]
1144            .data
1145            .contains("moo.wav"));
1146    }
1147
1148    #[test]
1149    fn error_info_accessor() {
1150        let h = headers_with(&[("Error-Info", "<sip:not-in-service@example.com>")]);
1151        let ei = h
1152            .error_info()
1153            .unwrap()
1154            .unwrap();
1155        assert_eq!(ei.len(), 1);
1156    }
1157
1158    #[test]
1159    fn p_preferred_identity_accessor() {
1160        let h = headers_with(&[(
1161            "P-Preferred-Identity",
1162            r#""User" <sip:+15551234567@198.51.100.1>"#,
1163        )]);
1164        let ppi = h
1165            .p_preferred_identity()
1166            .unwrap();
1167        assert_eq!(ppi.len(), 1);
1168        assert_eq!(ppi[0].display_name(), Some("User"));
1169    }
1170
1171    #[test]
1172    fn content_encoding_accessor() {
1173        let h = headers_with(&[("Content-Encoding", "gzip")]);
1174        assert_eq!(h.content_encoding(), vec!["gzip"]);
1175    }
1176
1177    #[test]
1178    fn contact_accessor() {
1179        let h = headers_with(&[("Contact", "<sip:alice@198.51.100.1>")]);
1180        let contacts = h
1181            .contact()
1182            .unwrap();
1183        assert_eq!(contacts.len(), 1);
1184        assert!(matches!(&contacts[0], ContactValue::Addr(_)));
1185    }
1186
1187    #[test]
1188    fn contact_wildcard() {
1189        let h = headers_with(&[("Contact", "*")]);
1190        let contacts = h
1191            .contact()
1192            .unwrap();
1193        assert_eq!(contacts.len(), 1);
1194        assert!(matches!(contacts[0], ContactValue::Wildcard));
1195    }
1196
1197    #[test]
1198    fn contact_absent() {
1199        let h = headers_with(&[]);
1200        assert!(h
1201            .contact()
1202            .unwrap()
1203            .is_empty());
1204    }
1205
1206    #[test]
1207    fn in_reply_to_accessor() {
1208        let h = headers_with(&[("In-Reply-To", "call1@example.com, call2@example.com")]);
1209        let calls = h.in_reply_to();
1210        assert_eq!(calls.len(), 2);
1211        assert_eq!(calls[0], "call1@example.com");
1212    }
1213
1214    #[test]
1215    fn via_accessor() {
1216        let h = headers_with(&[("Via", "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK776")]);
1217        let via = h
1218            .via()
1219            .unwrap()
1220            .unwrap();
1221        assert_eq!(via.len(), 1);
1222        assert_eq!(via.entries()[0].transport(), "UDP");
1223        assert_eq!(via.entries()[0].host(), "198.51.100.1");
1224    }
1225
1226    #[test]
1227    fn replaces_accessor() {
1228        let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1229        let r = h
1230            .replaces()
1231            .unwrap()
1232            .unwrap();
1233        assert_eq!(r.call_id(), "abc123@203.0.113.5");
1234        assert_eq!(r.to_tag(), "t1");
1235        assert_eq!(r.from_tag(), "f1");
1236    }
1237
1238    #[test]
1239    fn replaces_absent() {
1240        let h = headers_with(&[]);
1241        assert!(h
1242            .replaces()
1243            .unwrap()
1244            .is_none());
1245    }
1246
1247    #[test]
1248    fn replaces_malformed_err() {
1249        let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1")]);
1250        assert!(h
1251            .replaces()
1252            .is_err());
1253    }
1254
1255    #[test]
1256    fn join_accessor() {
1257        let h = headers_with(&[("Join", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1258        let j = h
1259            .join()
1260            .unwrap()
1261            .unwrap();
1262        assert_eq!(j.call_id(), "abc123@203.0.113.5");
1263    }
1264
1265    #[test]
1266    fn target_dialog_accessor() {
1267        let h = headers_with(&[(
1268            "Target-Dialog",
1269            "abc123@203.0.113.5;local-tag=l1;remote-tag=r1",
1270        )]);
1271        let t = h
1272            .target_dialog()
1273            .unwrap()
1274            .unwrap();
1275        assert_eq!(t.local_tag(), "l1");
1276        assert_eq!(t.remote_tag(), "r1");
1277    }
1278
1279    #[test]
1280    fn authorization_accessor() {
1281        let h = headers_with(&[(
1282            "Authorization",
1283            "Digest username=\"alice\", realm=\"example.com\", nonce=\"abc123\"",
1284        )]);
1285        let auth = h
1286            .authorization()
1287            .unwrap();
1288        assert_eq!(auth.len(), 1);
1289        assert_eq!(auth[0].scheme(), "Digest");
1290        assert_eq!(auth[0].username(), Some("alice"));
1291        assert_eq!(auth[0].realm(), Some("example.com"));
1292    }
1293
1294    #[test]
1295    fn www_authenticate_accessor() {
1296        let h = headers_with(&[(
1297            "WWW-Authenticate",
1298            "Digest realm=\"example.com\", nonce=\"xyz789\"",
1299        )]);
1300        let challenges = h
1301            .www_authenticate()
1302            .unwrap();
1303        assert_eq!(challenges.len(), 1);
1304        assert_eq!(challenges[0].realm(), Some("example.com"));
1305    }
1306
1307    #[test]
1308    fn warning_accessor() {
1309        let h = headers_with(&[(
1310            "Warning",
1311            "301 198.51.100.1 \"Incompatible network protocol\"",
1312        )]);
1313        let w = h
1314            .warning()
1315            .unwrap()
1316            .unwrap();
1317        assert_eq!(w.len(), 1);
1318        assert_eq!(w.entries()[0].code(), 301);
1319    }
1320
1321    #[test]
1322    fn security_client_accessor() {
1323        let h = headers_with(&[("Security-Client", "tls;q=0.2, digest;d-qop=auth;q=0.1")]);
1324        let sec = h
1325            .security_client()
1326            .unwrap()
1327            .unwrap();
1328        assert_eq!(sec.len(), 2);
1329        assert_eq!(sec.entries()[0].mechanism(), "tls");
1330    }
1331
1332    #[test]
1333    fn accept_accessor() {
1334        let h = headers_with(&[("Accept", "application/sdp, application/pidf+xml;q=0.5")]);
1335        let accept = h
1336            .accept()
1337            .unwrap()
1338            .unwrap();
1339        assert_eq!(accept.len(), 2);
1340        assert_eq!(accept.entries()[0].media_range(), "application/sdp");
1341    }
1342
1343    #[test]
1344    fn accept_encoding_accessor() {
1345        let h = headers_with(&[("Accept-Encoding", "gzip;q=1.0, identity;q=0.5")]);
1346        let ae = h
1347            .accept_encoding()
1348            .unwrap()
1349            .unwrap();
1350        assert_eq!(ae.len(), 2);
1351        assert_eq!(ae.entries()[0].encoding(), "gzip");
1352    }
1353
1354    #[test]
1355    fn accept_language_accessor() {
1356        let h = headers_with(&[("Accept-Language", "en;q=0.9, fr;q=0.8")]);
1357        let al = h
1358            .accept_language()
1359            .unwrap()
1360            .unwrap();
1361        assert_eq!(al.len(), 2);
1362        assert_eq!(al.entries()[0].language(), "en");
1363    }
1364}
1365
1366#[cfg(test)]
1367mod compact_form_tests {
1368    use super::*;
1369
1370    #[test]
1371    fn from_compact_known() {
1372        assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
1373        assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
1374        assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
1375        assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
1376        assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
1377        assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
1378        assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
1379    }
1380
1381    #[test]
1382    fn from_compact_unknown() {
1383        assert_eq!(SipHeader::from_compact(b'z'), None);
1384        assert_eq!(SipHeader::from_compact(b'g'), None);
1385    }
1386
1387    #[test]
1388    fn compact_form_roundtrip() {
1389        assert_eq!(SipHeader::From.compact_form(), Some('f'));
1390        assert_eq!(SipHeader::Via.compact_form(), Some('v'));
1391        assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
1392        assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
1393    }
1394
1395    #[test]
1396    fn compact_form_absent() {
1397        assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
1398        assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
1399    }
1400
1401    #[test]
1402    fn parse_name_compact() {
1403        assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
1404        assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
1405        assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
1406    }
1407
1408    #[test]
1409    fn parse_name_full() {
1410        assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
1411        assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
1412    }
1413
1414    #[test]
1415    fn parse_name_unknown() {
1416        assert!(SipHeader::parse_name("X-Custom").is_err());
1417    }
1418
1419    #[test]
1420    fn all_compact_forms_resolve() {
1421        let expected = [
1422            ('a', SipHeader::AcceptContact),
1423            ('b', SipHeader::ReferredBy),
1424            ('c', SipHeader::ContentType),
1425            ('d', SipHeader::RequestDisposition),
1426            ('e', SipHeader::ContentEncoding),
1427            ('f', SipHeader::From),
1428            ('i', SipHeader::CallId),
1429            ('j', SipHeader::RejectContact),
1430            ('k', SipHeader::Supported),
1431            ('l', SipHeader::ContentLength),
1432            ('m', SipHeader::Contact),
1433            ('n', SipHeader::IdentityInfo),
1434            ('o', SipHeader::Event),
1435            ('r', SipHeader::ReferTo),
1436            ('s', SipHeader::Subject),
1437            ('t', SipHeader::To),
1438            ('u', SipHeader::AllowEvents),
1439            ('v', SipHeader::Via),
1440            ('x', SipHeader::SessionExpires),
1441            ('y', SipHeader::Identity),
1442        ];
1443        for (ch, header) in expected {
1444            assert_eq!(
1445                SipHeader::from_compact(ch as u8),
1446                Some(header),
1447                "compact form '{ch}' failed"
1448            );
1449            assert_eq!(
1450                header.compact_form(),
1451                Some(ch),
1452                "compact_form() for {} failed",
1453                header
1454            );
1455        }
1456    }
1457}
1458
1459#[cfg(test)]
1460mod multi_valued_tests {
1461    use super::*;
1462
1463    #[test]
1464    fn rfc3261_multi_valued_headers() {
1465        assert!(SipHeader::Via.is_multi_valued());
1466        assert!(SipHeader::Route.is_multi_valued());
1467        assert!(SipHeader::RecordRoute.is_multi_valued());
1468        assert!(SipHeader::Contact.is_multi_valued());
1469        assert!(SipHeader::Allow.is_multi_valued());
1470        assert!(SipHeader::Supported.is_multi_valued());
1471        assert!(SipHeader::Require.is_multi_valued());
1472        assert!(SipHeader::ProxyRequire.is_multi_valued());
1473        assert!(SipHeader::Unsupported.is_multi_valued());
1474        assert!(SipHeader::Authorization.is_multi_valued());
1475        assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1476        assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1477        assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1478        assert!(SipHeader::Warning.is_multi_valued());
1479        assert!(SipHeader::ErrorInfo.is_multi_valued());
1480        assert!(SipHeader::CallInfo.is_multi_valued());
1481        assert!(SipHeader::AlertInfo.is_multi_valued());
1482        assert!(SipHeader::Accept.is_multi_valued());
1483        assert!(SipHeader::AcceptEncoding.is_multi_valued());
1484        assert!(SipHeader::AcceptLanguage.is_multi_valued());
1485        assert!(SipHeader::ContentEncoding.is_multi_valued());
1486        assert!(SipHeader::ContentLanguage.is_multi_valued());
1487        assert!(SipHeader::InReplyTo.is_multi_valued());
1488    }
1489
1490    #[test]
1491    fn extension_multi_valued_headers() {
1492        assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1493        assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1494        assert!(SipHeader::AllowEvents.is_multi_valued());
1495        assert!(SipHeader::SecurityClient.is_multi_valued());
1496        assert!(SipHeader::SecurityServer.is_multi_valued());
1497        assert!(SipHeader::SecurityVerify.is_multi_valued());
1498        assert!(SipHeader::Path.is_multi_valued());
1499        assert!(SipHeader::ServiceRoute.is_multi_valued());
1500        assert!(SipHeader::HistoryInfo.is_multi_valued());
1501    }
1502
1503    #[test]
1504    fn single_valued_headers() {
1505        assert!(!SipHeader::From.is_multi_valued());
1506        assert!(!SipHeader::To.is_multi_valued());
1507        assert!(!SipHeader::CallId.is_multi_valued());
1508        assert!(!SipHeader::Cseq.is_multi_valued());
1509        assert!(!SipHeader::MaxForwards.is_multi_valued());
1510        assert!(!SipHeader::ContentType.is_multi_valued());
1511        assert!(!SipHeader::ContentLength.is_multi_valued());
1512        assert!(!SipHeader::Expires.is_multi_valued());
1513        assert!(!SipHeader::Date.is_multi_valued());
1514        assert!(!SipHeader::Subject.is_multi_valued());
1515        assert!(!SipHeader::ReplyTo.is_multi_valued());
1516        assert!(!SipHeader::Server.is_multi_valued());
1517        assert!(!SipHeader::UserAgent.is_multi_valued());
1518    }
1519
1520    #[test]
1521    #[cfg(feature = "draft")]
1522    fn draft_multi_valued_headers() {
1523        assert!(SipHeader::Diversion.is_multi_valued());
1524        assert!(SipHeader::RemotePartyId.is_multi_valued());
1525    }
1526
1527    #[test]
1528    #[cfg(feature = "draft")]
1529    fn draft_parse_roundtrip() {
1530        let d: SipHeader = "Diversion"
1531            .parse()
1532            .unwrap();
1533        assert_eq!(d, SipHeader::Diversion);
1534        assert_eq!(d.to_string(), "Diversion");
1535
1536        let r: SipHeader = "remote-party-id"
1537            .parse()
1538            .unwrap();
1539        assert_eq!(r, SipHeader::RemotePartyId);
1540        assert_eq!(r.to_string(), "Remote-Party-ID");
1541    }
1542}
1543
1544#[cfg(test)]
1545mod special_case_tests {
1546    use super::*;
1547
1548    #[test]
1549    fn cseq_variants() {
1550        assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1551        assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1552        assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1553        assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1554    }
1555
1556    #[test]
1557    fn www_authenticate_variants() {
1558        assert_eq!(
1559            "WWW-Authenticate".parse::<SipHeader>(),
1560            Ok(SipHeader::WwwAuthenticate)
1561        );
1562        assert_eq!(
1563            "www-authenticate".parse::<SipHeader>(),
1564            Ok(SipHeader::WwwAuthenticate)
1565        );
1566        assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1567    }
1568
1569    #[test]
1570    fn rack_rseq_variants() {
1571        assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1572        assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1573        assert_eq!(SipHeader::Rack.to_string(), "RAck");
1574
1575        assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1576        assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1577        assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1578    }
1579
1580    #[test]
1581    fn user_to_user_variants() {
1582        assert_eq!(
1583            "User-to-User".parse::<SipHeader>(),
1584            Ok(SipHeader::UserToUser)
1585        );
1586        assert_eq!(
1587            "user-to-user".parse::<SipHeader>(),
1588            Ok(SipHeader::UserToUser)
1589        );
1590        assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1591    }
1592
1593    #[test]
1594    fn p_header_variants() {
1595        assert_eq!(
1596            "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1597            Ok(SipHeader::PDcsTracePartyId)
1598        );
1599        assert_eq!(
1600            "p-dcs-trace-party-id".parse::<SipHeader>(),
1601            Ok(SipHeader::PDcsTracePartyId)
1602        );
1603        assert_eq!(
1604            SipHeader::PDcsTracePartyId.to_string(),
1605            "P-DCS-Trace-Party-ID"
1606        );
1607    }
1608}
1609
1610#[cfg(test)]
1611mod multi_row_tests {
1612    use super::*;
1613    use std::collections::HashMap;
1614
1615    fn rows(pairs: &[(&str, &[&str])]) -> HashMap<String, Vec<String>> {
1616        pairs
1617            .iter()
1618            .map(|(k, vs)| {
1619                (
1620                    k.to_string(),
1621                    vs.iter()
1622                        .map(|v| v.to_string())
1623                        .collect(),
1624                )
1625            })
1626            .collect()
1627    }
1628
1629    #[test]
1630    fn via_reads_every_row() {
1631        let h = rows(&[(
1632            "Via",
1633            &[
1634                "SIP/2.0/UDP 198.51.100.1;branch=z9hG4bK1",
1635                "SIP/2.0/TCP 203.0.113.5",
1636            ],
1637        )]);
1638        let via = h
1639            .via()
1640            .unwrap()
1641            .unwrap();
1642        assert_eq!(via.len(), 2);
1643        assert_eq!(via.entries()[1].transport(), "TCP");
1644    }
1645
1646    #[test]
1647    fn via_row_with_list_plus_row() {
1648        let h = rows(&[(
1649            "Via",
1650            &[
1651                "SIP/2.0/UDP 198.51.100.1, SIP/2.0/UDP 198.51.100.2",
1652                "SIP/2.0/TCP 203.0.113.5",
1653            ],
1654        )]);
1655        assert_eq!(
1656            h.via()
1657                .unwrap()
1658                .unwrap()
1659                .len(),
1660            3
1661        );
1662    }
1663
1664    #[test]
1665    fn via_blank_rows() {
1666        let h = rows(&[("Via", &[""])]);
1667        assert!(matches!(h.via(), Err(SipViaError::Empty)));
1668        let h = rows(&[("Via", &["   "])]);
1669        assert!(matches!(h.via(), Err(SipViaError::InvalidFormat(_))));
1670    }
1671
1672    #[test]
1673    fn typed_lists_read_every_row() {
1674        let h = rows(&[
1675            (
1676                "Warning",
1677                &[r#"301 example.com "a""#, r#"399 example.org "b""#],
1678            ),
1679            ("Accept", &["application/sdp", "text/plain"]),
1680            ("Accept-Encoding", &["gzip", "identity"]),
1681            ("Accept-Language", &["en", "fr"]),
1682            ("Security-Client", &["tls", "digest"]),
1683            ("Security-Server", &["tls", "digest"]),
1684            ("Security-Verify", &["tls", "digest"]),
1685            (
1686                "Call-Info",
1687                &["<http://example.com/a>", "<http://example.com/b>"],
1688            ),
1689            (
1690                "Alert-Info",
1691                &["<http://example.com/a>", "<http://example.com/b>"],
1692            ),
1693            (
1694                "Error-Info",
1695                &["<http://example.com/a>", "<http://example.com/b>"],
1696            ),
1697            (
1698                "History-Info",
1699                &[
1700                    "<sip:a@example.com>;index=1",
1701                    "<sip:b@example.com>;index=1.1",
1702                ],
1703            ),
1704        ]);
1705        assert_eq!(
1706            h.warning()
1707                .unwrap()
1708                .unwrap()
1709                .len(),
1710            2
1711        );
1712        assert_eq!(
1713            h.accept()
1714                .unwrap()
1715                .unwrap()
1716                .len(),
1717            2
1718        );
1719        assert_eq!(
1720            h.accept_encoding()
1721                .unwrap()
1722                .unwrap()
1723                .len(),
1724            2
1725        );
1726        assert_eq!(
1727            h.accept_language()
1728                .unwrap()
1729                .unwrap()
1730                .len(),
1731            2
1732        );
1733        assert_eq!(
1734            h.security_client()
1735                .unwrap()
1736                .unwrap()
1737                .len(),
1738            2
1739        );
1740        assert_eq!(
1741            h.security_server()
1742                .unwrap()
1743                .unwrap()
1744                .len(),
1745            2
1746        );
1747        assert_eq!(
1748            h.security_verify()
1749                .unwrap()
1750                .unwrap()
1751                .len(),
1752            2
1753        );
1754        assert_eq!(
1755            h.call_info()
1756                .unwrap()
1757                .unwrap()
1758                .len(),
1759            2
1760        );
1761        assert_eq!(
1762            h.alert_info()
1763                .unwrap()
1764                .unwrap()
1765                .len(),
1766            2
1767        );
1768        assert_eq!(
1769            h.error_info()
1770                .unwrap()
1771                .unwrap()
1772                .len(),
1773            2
1774        );
1775        assert_eq!(
1776            h.history_info()
1777                .unwrap()
1778                .unwrap()
1779                .len(),
1780            2
1781        );
1782    }
1783
1784    #[test]
1785    fn contact_reads_every_row() {
1786        let h = rows(&[(
1787            "Contact",
1788            &[
1789                "<sip:a@example.com>",
1790                "<sip:b@example.com>, <sip:c@example.com>",
1791            ],
1792        )]);
1793        assert_eq!(
1794            h.contact()
1795                .unwrap()
1796                .len(),
1797            3
1798        );
1799        let h = rows(&[("Contact", &["*", "<sip:a@example.com>"])]);
1800        assert!(h
1801            .contact()
1802            .is_err());
1803        let h = rows(&[("Contact", &[""])]);
1804        assert!(h
1805            .contact()
1806            .unwrap()
1807            .is_empty());
1808    }
1809
1810    #[test]
1811    fn token_lists_read_every_row() {
1812        let h = rows(&[
1813            ("Allow", &["INVITE, ACK", "BYE"]),
1814            ("Supported", &["100rel", "timer"]),
1815            ("Require", &["100rel", "timer"]),
1816            ("Proxy-Require", &["100rel", "timer"]),
1817            ("Unsupported", &["100rel", "timer"]),
1818            ("Allow-Events", &["dialog", "presence"]),
1819            ("Content-Encoding", &["gzip", "identity"]),
1820            ("Content-Language", &["en", "fr"]),
1821            ("In-Reply-To", &["a@example.com", "b@example.com"]),
1822        ]);
1823        assert_eq!(h.allow(), vec!["INVITE", "ACK", "BYE"]);
1824        assert_eq!(
1825            h.supported()
1826                .len(),
1827            2
1828        );
1829        assert_eq!(
1830            h.require_header()
1831                .len(),
1832            2
1833        );
1834        assert_eq!(
1835            h.proxy_require()
1836                .len(),
1837            2
1838        );
1839        assert_eq!(
1840            h.unsupported()
1841                .len(),
1842            2
1843        );
1844        assert_eq!(
1845            h.allow_events()
1846                .len(),
1847            2
1848        );
1849        assert_eq!(
1850            h.content_encoding()
1851                .len(),
1852            2
1853        );
1854        assert_eq!(
1855            h.content_language()
1856                .len(),
1857            2
1858        );
1859        assert_eq!(
1860            h.in_reply_to()
1861                .len(),
1862            2
1863        );
1864    }
1865
1866    #[test]
1867    fn token_list_blank_rows() {
1868        let h = rows(&[("Allow", &[""])]);
1869        assert!(h
1870            .allow()
1871            .is_empty());
1872        let h = rows(&[("Allow", &["   "])]);
1873        assert_eq!(h.allow(), vec![""]);
1874    }
1875
1876    #[test]
1877    fn addr_list_blank_row_is_error() {
1878        let h = rows(&[("Route", &["   "])]);
1879        assert!(h
1880            .route()
1881            .is_err());
1882    }
1883}