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