freeswitch_types/variables/
sip_invite.rs1#[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 pub enum SipInviteHeader {
40 From => "sip_i_from",
44 To => "sip_i_to",
46 CallId => "sip_i_call_id",
48 Cseq => "sip_i_cseq",
50 Identity => "sip_i_identity",
52 Route => "sip_i_route",
54 MaxForwards => "sip_i_max_forwards",
56 ProxyRequire => "sip_i_proxy_require",
58 Contact => "sip_i_contact",
60 UserAgent => "sip_i_user_agent",
62 Subject => "sip_i_subject",
64 Priority => "sip_i_priority",
66 Organization => "sip_i_organization",
68 InReplyTo => "sip_i_in_reply_to",
70 AcceptEncoding => "sip_i_accept_encoding",
72 AcceptLanguage => "sip_i_accept_language",
74 Allow => "sip_i_allow",
76 Require => "sip_i_require",
78 Supported => "sip_i_supported",
80 Date => "sip_i_date",
82 Timestamp => "sip_i_timestamp",
84 Expires => "sip_i_expires",
86 MinExpires => "sip_i_min_expires",
88 SessionExpires => "sip_i_session_expires",
90 MinSe => "sip_i_min_se",
92 Privacy => "sip_i_privacy",
94 MimeVersion => "sip_i_mime_version",
96 ContentType => "sip_i_content_type",
98 ContentEncoding => "sip_i_content_encoding",
100 ContentLanguage => "sip_i_content_language",
102 ContentDisposition => "sip_i_content_disposition",
104 ContentLength => "sip_i_content_length",
106
107 Via => "sip_i_via",
111 RecordRoute => "sip_i_record_route",
113 ProxyAuthorization => "sip_i_proxy_authorization",
115 CallInfo => "sip_i_call_info",
117 Accept => "sip_i_accept",
119 Authorization => "sip_i_authorization",
121 AlertInfo => "sip_i_alert_info",
123 PAssertedIdentity => "sip_i_p_asserted_identity",
125 PPreferredIdentity => "sip_i_p_preferred_identity",
127 RemotePartyId => "sip_i_remote_party_id",
129 ReplyTo => "sip_i_reply_to",
131 }
132}
133
134impl SipInviteHeader {
135 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 pub fn is_array_header(&self) -> bool {
152 Self::ARRAY_HEADERS.contains(self)
153 }
154
155 pub fn header_name(&self) -> &'static str {
157 match self {
158 Self::From => "From",
159 Self::To => "To",
160 Self::CallId => "Call-ID",
161 Self::Cseq => "CSeq",
162 Self::Identity => "Identity",
163 Self::Route => "Route",
164 Self::MaxForwards => "Max-Forwards",
165 Self::ProxyRequire => "Proxy-Require",
166 Self::Contact => "Contact",
167 Self::UserAgent => "User-Agent",
168 Self::Subject => "Subject",
169 Self::Priority => "Priority",
170 Self::Organization => "Organization",
171 Self::InReplyTo => "In-Reply-To",
172 Self::AcceptEncoding => "Accept-Encoding",
173 Self::AcceptLanguage => "Accept-Language",
174 Self::Allow => "Allow",
175 Self::Require => "Require",
176 Self::Supported => "Supported",
177 Self::Date => "Date",
178 Self::Timestamp => "Timestamp",
179 Self::Expires => "Expires",
180 Self::MinExpires => "Min-Expires",
181 Self::SessionExpires => "Session-Expires",
182 Self::MinSe => "Min-SE",
183 Self::Privacy => "Privacy",
184 Self::MimeVersion => "MIME-Version",
185 Self::ContentType => "Content-Type",
186 Self::ContentEncoding => "Content-Encoding",
187 Self::ContentLanguage => "Content-Language",
188 Self::ContentDisposition => "Content-Disposition",
189 Self::ContentLength => "Content-Length",
190 Self::Via => "Via",
191 Self::RecordRoute => "Record-Route",
192 Self::ProxyAuthorization => "Proxy-Authorization",
193 Self::CallInfo => "Call-Info",
194 Self::Accept => "Accept",
195 Self::Authorization => "Authorization",
196 Self::AlertInfo => "Alert-Info",
197 Self::PAssertedIdentity => "P-Asserted-Identity",
198 Self::PPreferredIdentity => "P-Preferred-Identity",
199 Self::RemotePartyId => "Remote-Party-ID",
200 Self::ReplyTo => "Reply-To",
201 }
202 }
203
204 pub fn extract_from(&self, message: &str) -> Option<String> {
209 crate::sip_message::extract_header(message, self.header_name())
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn display_round_trip() {
219 assert_eq!(
220 SipInviteHeader::PAssertedIdentity.to_string(),
221 "sip_i_p_asserted_identity"
222 );
223 assert_eq!(SipInviteHeader::From.to_string(), "sip_i_from");
224 assert_eq!(SipInviteHeader::Via.to_string(), "sip_i_via");
225 }
226
227 #[test]
228 fn as_ref_str() {
229 let v: &str = SipInviteHeader::CallId.as_ref();
230 assert_eq!(v, "sip_i_call_id");
231 }
232
233 #[test]
234 fn from_str_case_insensitive() {
235 assert_eq!(
236 "sip_i_p_asserted_identity".parse::<SipInviteHeader>(),
237 Ok(SipInviteHeader::PAssertedIdentity)
238 );
239 assert_eq!(
240 "SIP_I_P_ASSERTED_IDENTITY".parse::<SipInviteHeader>(),
241 Ok(SipInviteHeader::PAssertedIdentity)
242 );
243 }
244
245 #[test]
246 fn from_str_unknown() {
247 assert!("sip_i_nonexistent"
248 .parse::<SipInviteHeader>()
249 .is_err());
250 }
251
252 #[test]
253 fn from_str_round_trip_all() {
254 let variants = [
255 SipInviteHeader::From,
256 SipInviteHeader::To,
257 SipInviteHeader::CallId,
258 SipInviteHeader::Via,
259 SipInviteHeader::RecordRoute,
260 SipInviteHeader::PAssertedIdentity,
261 SipInviteHeader::PPreferredIdentity,
262 SipInviteHeader::RemotePartyId,
263 SipInviteHeader::AlertInfo,
264 SipInviteHeader::Privacy,
265 SipInviteHeader::ContentType,
266 ];
267 for v in variants {
268 let wire = v.to_string();
269 let parsed: SipInviteHeader = wire
270 .parse()
271 .unwrap();
272 assert_eq!(parsed, v, "round-trip failed for {wire}");
273 }
274 }
275
276 #[test]
277 fn header_name_mapping() {
278 assert_eq!(SipInviteHeader::From.header_name(), "From");
279 assert_eq!(SipInviteHeader::CallId.header_name(), "Call-ID");
280 assert_eq!(SipInviteHeader::Cseq.header_name(), "CSeq");
281 assert_eq!(
282 SipInviteHeader::PAssertedIdentity.header_name(),
283 "P-Asserted-Identity"
284 );
285 assert_eq!(SipInviteHeader::MinSe.header_name(), "Min-SE");
286 assert_eq!(SipInviteHeader::MimeVersion.header_name(), "MIME-Version");
287 assert_eq!(SipInviteHeader::UserAgent.header_name(), "User-Agent");
288 assert_eq!(SipInviteHeader::RecordRoute.header_name(), "Record-Route");
289 }
290
291 #[test]
292 fn extract_from_sip_message() {
293 let msg = "INVITE sip:bob@host SIP/2.0\r\n\
294 From: Alice <sip:alice@host>;tag=abc\r\n\
295 To: Bob <sip:bob@host>\r\n\
296 Call-ID: 12345@host\r\n\
297 \r\n";
298 assert_eq!(
299 SipInviteHeader::From.extract_from(msg),
300 Some("Alice <sip:alice@host>;tag=abc".into())
301 );
302 assert_eq!(
303 SipInviteHeader::CallId.extract_from(msg),
304 Some("12345@host".into())
305 );
306 }
307
308 #[test]
309 fn extract_from_array_header() {
310 let msg = "INVITE sip:bob@host SIP/2.0\r\n\
311 Via: SIP/2.0/UDP first.example.com\r\n\
312 Via: SIP/2.0/UDP second.example.com\r\n\
313 \r\n";
314 assert_eq!(
315 SipInviteHeader::Via.extract_from(msg),
316 Some("SIP/2.0/UDP first.example.com, SIP/2.0/UDP second.example.com".into())
317 );
318 }
319
320 #[test]
321 fn extract_from_missing() {
322 let msg = "INVITE sip:bob@host SIP/2.0\r\n\
323 From: Alice <sip:alice@host>\r\n\
324 \r\n";
325 assert_eq!(SipInviteHeader::Identity.extract_from(msg), None);
326 }
327
328 #[test]
329 fn array_headers_classification() {
330 assert!(SipInviteHeader::Via.is_array_header());
331 assert!(SipInviteHeader::PAssertedIdentity.is_array_header());
332 assert!(SipInviteHeader::RecordRoute.is_array_header());
333 assert!(!SipInviteHeader::From.is_array_header());
334 assert!(!SipInviteHeader::CallId.is_array_header());
335 assert!(!SipInviteHeader::ContentType.is_array_header());
336 }
337}