Skip to main content

freeswitch_types/variables/
sip_invite.rs

1//! Raw SIP INVITE header variables (`sip_i_*`).
2//!
3//! These are set by `sofia_parse_all_invite_headers()` when
4//! `parse-all-invite-headers` is enabled on the sofia profile.
5//! Each variable contains the verbatim serialized SIP header value.
6//!
7//! Some headers may repeat in a SIP message. Those are stored using
8//! FreeSWITCH's ARRAY format (`ARRAY::value1|:value2`). Parse them
9//! with [`EslArray`](super::EslArray).
10
11/// Error returned when parsing an unrecognized SIP invite header variable name.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct ParseSipInviteHeaderError(pub String);
14
15impl std::fmt::Display for ParseSipInviteHeaderError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "unknown SIP invite header variable: {}", self.0)
18    }
19}
20
21impl std::error::Error for ParseSipInviteHeaderError {}
22
23define_header_enum! {
24    error_type: ParseSipInviteHeaderError,
25    /// Raw SIP INVITE headers preserved verbatim as channel variables.
26    ///
27    /// Set by `sofia_parse_all_invite_headers()` when the sofia profile has
28    /// `parse-all-invite-headers` enabled. Access via
29    /// [`HeaderLookup::variable()`](crate::HeaderLookup::variable).
30    ///
31    /// Variants marked "ARRAY" may contain multiple values in
32    /// `ARRAY::val1|:val2` format when the SIP message has repeated headers.
33    /// Parse with [`EslArray`](super::EslArray). Variants marked "single"
34    /// contain one serialized header value.
35    ///
36    /// For headers not covered by this enum (dynamic unknown headers stored
37    /// as `sip_i_<lowercased_name>`), use
38    /// [`variable_str()`](crate::HeaderLookup::variable_str).
39    pub enum SipInviteHeader {
40        // --- Single-value headers ---
41
42        /// SIP From header.
43        From => "sip_i_from",
44        /// SIP To header.
45        To => "sip_i_to",
46        /// SIP Call-ID header.
47        CallId => "sip_i_call_id",
48        /// SIP CSeq header.
49        Cseq => "sip_i_cseq",
50        /// SIP Identity header (RFC 8224).
51        Identity => "sip_i_identity",
52        /// SIP Route header.
53        Route => "sip_i_route",
54        /// SIP Max-Forwards header.
55        MaxForwards => "sip_i_max_forwards",
56        /// SIP Proxy-Require header.
57        ProxyRequire => "sip_i_proxy_require",
58        /// SIP Contact header.
59        Contact => "sip_i_contact",
60        /// SIP User-Agent header.
61        UserAgent => "sip_i_user_agent",
62        /// SIP Subject header.
63        Subject => "sip_i_subject",
64        /// SIP Priority header.
65        Priority => "sip_i_priority",
66        /// SIP Organization header.
67        Organization => "sip_i_organization",
68        /// SIP In-Reply-To header.
69        InReplyTo => "sip_i_in_reply_to",
70        /// SIP Accept-Encoding header.
71        AcceptEncoding => "sip_i_accept_encoding",
72        /// SIP Accept-Language header.
73        AcceptLanguage => "sip_i_accept_language",
74        /// SIP Allow header.
75        Allow => "sip_i_allow",
76        /// SIP Require header.
77        Require => "sip_i_require",
78        /// SIP Supported header.
79        Supported => "sip_i_supported",
80        /// SIP Date header.
81        Date => "sip_i_date",
82        /// SIP Timestamp header.
83        Timestamp => "sip_i_timestamp",
84        /// SIP Expires header.
85        Expires => "sip_i_expires",
86        /// SIP Min-Expires header.
87        MinExpires => "sip_i_min_expires",
88        /// SIP Session-Expires header.
89        SessionExpires => "sip_i_session_expires",
90        /// SIP Min-SE header.
91        MinSe => "sip_i_min_se",
92        /// SIP Privacy header.
93        Privacy => "sip_i_privacy",
94        /// SIP MIME-Version header.
95        MimeVersion => "sip_i_mime_version",
96        /// SIP Content-Type header.
97        ContentType => "sip_i_content_type",
98        /// SIP Content-Encoding header.
99        ContentEncoding => "sip_i_content_encoding",
100        /// SIP Content-Language header.
101        ContentLanguage => "sip_i_content_language",
102        /// SIP Content-Disposition header.
103        ContentDisposition => "sip_i_content_disposition",
104        /// SIP Content-Length header.
105        ContentLength => "sip_i_content_length",
106
107        // --- ARRAY headers (may contain multiple values) ---
108
109        /// SIP Via headers. ARRAY when multiple hops present.
110        Via => "sip_i_via",
111        /// SIP Record-Route headers. ARRAY when multiple proxies present.
112        RecordRoute => "sip_i_record_route",
113        /// SIP Proxy-Authorization headers. ARRAY when multiple credentials present.
114        ProxyAuthorization => "sip_i_proxy_authorization",
115        /// SIP Call-Info headers. ARRAY when multiple info URIs present.
116        CallInfo => "sip_i_call_info",
117        /// SIP Accept headers. ARRAY when multiple media types present.
118        Accept => "sip_i_accept",
119        /// SIP Authorization headers. ARRAY when multiple credentials present.
120        Authorization => "sip_i_authorization",
121        /// SIP Alert-Info headers. ARRAY when multiple alert URIs present.
122        AlertInfo => "sip_i_alert_info",
123        /// SIP P-Asserted-Identity headers. ARRAY when multiple identities present (RFC 3325).
124        PAssertedIdentity => "sip_i_p_asserted_identity",
125        /// SIP P-Preferred-Identity headers. ARRAY when multiple identities present.
126        PPreferredIdentity => "sip_i_p_preferred_identity",
127        /// SIP Remote-Party-ID headers. ARRAY when multiple identities present.
128        RemotePartyId => "sip_i_remote_party_id",
129        /// SIP Reply-To headers. ARRAY when multiple reply addresses present.
130        ReplyTo => "sip_i_reply_to",
131    }
132}
133
134impl SipInviteHeader {
135    /// Headers that may contain multiple values in ARRAY format.
136    pub const ARRAY_HEADERS: &[SipInviteHeader] = &[
137        SipInviteHeader::Via,
138        SipInviteHeader::RecordRoute,
139        SipInviteHeader::ProxyAuthorization,
140        SipInviteHeader::CallInfo,
141        SipInviteHeader::Accept,
142        SipInviteHeader::Authorization,
143        SipInviteHeader::AlertInfo,
144        SipInviteHeader::PAssertedIdentity,
145        SipInviteHeader::PPreferredIdentity,
146        SipInviteHeader::RemotePartyId,
147        SipInviteHeader::ReplyTo,
148    ];
149
150    /// Whether this header may contain multiple values in ARRAY format.
151    pub fn is_array_header(&self) -> bool {
152        Self::ARRAY_HEADERS.contains(self)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn display_round_trip() {
162        assert_eq!(
163            SipInviteHeader::PAssertedIdentity.to_string(),
164            "sip_i_p_asserted_identity"
165        );
166        assert_eq!(SipInviteHeader::From.to_string(), "sip_i_from");
167        assert_eq!(SipInviteHeader::Via.to_string(), "sip_i_via");
168    }
169
170    #[test]
171    fn as_ref_str() {
172        let v: &str = SipInviteHeader::CallId.as_ref();
173        assert_eq!(v, "sip_i_call_id");
174    }
175
176    #[test]
177    fn from_str_case_insensitive() {
178        assert_eq!(
179            "sip_i_p_asserted_identity".parse::<SipInviteHeader>(),
180            Ok(SipInviteHeader::PAssertedIdentity)
181        );
182        assert_eq!(
183            "SIP_I_P_ASSERTED_IDENTITY".parse::<SipInviteHeader>(),
184            Ok(SipInviteHeader::PAssertedIdentity)
185        );
186    }
187
188    #[test]
189    fn from_str_unknown() {
190        assert!("sip_i_nonexistent"
191            .parse::<SipInviteHeader>()
192            .is_err());
193    }
194
195    #[test]
196    fn from_str_round_trip_all() {
197        let variants = [
198            SipInviteHeader::From,
199            SipInviteHeader::To,
200            SipInviteHeader::CallId,
201            SipInviteHeader::Via,
202            SipInviteHeader::RecordRoute,
203            SipInviteHeader::PAssertedIdentity,
204            SipInviteHeader::PPreferredIdentity,
205            SipInviteHeader::RemotePartyId,
206            SipInviteHeader::AlertInfo,
207            SipInviteHeader::Privacy,
208            SipInviteHeader::ContentType,
209        ];
210        for v in variants {
211            let wire = v.to_string();
212            let parsed: SipInviteHeader = wire
213                .parse()
214                .unwrap();
215            assert_eq!(parsed, v, "round-trip failed for {wire}");
216        }
217    }
218
219    #[test]
220    fn array_headers_classification() {
221        assert!(SipInviteHeader::Via.is_array_header());
222        assert!(SipInviteHeader::PAssertedIdentity.is_array_header());
223        assert!(SipInviteHeader::RecordRoute.is_array_header());
224        assert!(!SipInviteHeader::From.is_array_header());
225        assert!(!SipInviteHeader::CallId.is_array_header());
226        assert!(!SipInviteHeader::ContentType.is_array_header());
227    }
228}