1use sip_header::SipHeader;
4
5sip_header::define_header_enum! {
6 tests_mod: sofia_variable_generated_tests,
7 error_type: ParseSofiaVariableError => "unknown sofia variable",
8 pub enum SofiaVariable {
13 SipFromUser => "sip_from_user",
15 SipFromHost => "sip_from_host",
16 SipFromPort => "sip_from_port",
17 SipFromUri => "sip_from_uri",
18 SipFromDisplay => "sip_from_display",
19 SipFromTag => "sip_from_tag",
20 SipFromComment => "sip_from_comment",
21 SipFromUserStripped => "sip_from_user_stripped",
22 SipFullFrom => "sip_full_from",
23
24 SipToUser => "sip_to_user",
26 SipToHost => "sip_to_host",
27 SipToPort => "sip_to_port",
28 SipToUri => "sip_to_uri",
29 SipToDisplay => "sip_to_display",
30 SipToTag => "sip_to_tag",
31 SipToComment => "sip_to_comment",
32 SipFullTo => "sip_full_to",
33
34 SipContactUser => "sip_contact_user",
36 SipContactHost => "sip_contact_host",
37 SipContactPort => "sip_contact_port",
38 SipContactUri => "sip_contact_uri",
39 SipContactParams => "sip_contact_params",
40
41 SipReqUser => "sip_req_user",
43 SipReqHost => "sip_req_host",
44 SipReqPort => "sip_req_port",
45 SipReqUri => "sip_req_uri",
46
47 SipViaHost => "sip_via_host",
49 SipViaPort => "sip_via_port",
50 SipViaRport => "sip_via_rport",
51 SipViaProtocol => "sip_via_protocol",
52 SipFullVia => "sip_full_via",
53 SipFullRoute => "sip_full_route",
54
55 SipCallId => "sip_call_id",
57 SipCseq => "sip_cseq",
58 SipUserAgent => "sip_user_agent",
59 SipSubject => "sip_subject",
60 SipAllow => "sip_allow",
61 SipAcceptLanguage => "sip_accept_language",
62 SipCallInfo => "sip_call_info",
63 SipDateEpochTime => "sip_date_epoch_time",
64
65 SipReceivedIp => "sip_received_ip",
67 SipReceivedPort => "sip_received_port",
68 SipNetworkIp => "sip_network_ip",
69 SipNetworkPort => "sip_network_port",
70 SipNatDetected => "sip_nat_detected",
71 SipTransport => "sip_transport",
72 SipReplyHost => "sip_reply_host",
73
74 SipAuthUsername => "sip_auth_username",
76 SipAuthPassword => "sip_auth_password",
77 SipAuthorized => "sip_authorized",
78 SipAclAuthedBy => "sip_acl_authed_by",
79 SipAclToken => "sip_acl_token",
80 SipChallengeRealm => "sip_challenge_realm",
81
82 SipInviteFailureStatus => "sip_invite_failure_status",
84 SipInviteFailurePhrase => "sip_invite_failure_phrase",
85 SipHangupDisposition => "sip_hangup_disposition",
86 SipTermStatus => "sip_term_status",
87 SipTermCause => "sip_term_cause",
88 SipReason => "sip_reason",
89
90 SipPAssertedIdentity => "sip_P-Asserted-Identity",
92 SipPPreferredIdentity => "sip_P-Preferred-Identity",
93 SipPrivacy => "sip_Privacy",
94 SipRemotePartyId => "sip_Remote-Party-ID",
95 SipStirShakenAttest => "sip_stir_shaken_attest",
96 SipVerstat => "sip_verstat",
97 SipVerstatDetailed => "sip_verstat_detailed",
98
99 SipInviteCallId => "sip_invite_call_id",
101 SipInviteCseq => "sip_invite_cseq",
102 SipInviteFullFrom => "sip_invite_full_from",
103 SipInviteFullTo => "sip_invite_full_to",
104 SipInviteFullVia => "sip_invite_full_via",
105 SipInviteFromUri => "sip_invite_from_uri",
106 SipInviteToUri => "sip_invite_to_uri",
107 SipInviteReqUri => "sip_invite_req_uri",
108 SipInviteRecordRoute => "sip_invite_record_route",
109 SipInviteRouteUri => "sip_invite_route_uri",
110 SipInviteDomain => "sip_invite_domain",
111 SipInviteParams => "sip_invite_params",
112
113 SipAutoAnswer => "sip_auto_answer",
115 SipAutoSimplify => "sip_auto_simplify",
116 SipEnableSoa => "sip_enable_soa",
117 SipCopyCustomHeaders => "sip_copy_custom_headers",
118 SipCopyMultipart => "sip_copy_multipart",
119 SipLoopedCall => "sip_looped_call",
120
121 SipRedirectedTo => "sip_redirected_to",
123 SipRedirectedBy => "sip_redirected_by",
124 SipRedirectDialstring => "sip_redirect_dialstring",
125 SipReferReply => "sip_refer_reply",
126 SipReferStatusCode => "sip_refer_status_code",
127 SipReferredByFull => "sip_referred_by_full",
128 SipReferredByCid => "sip_referred_by_cid",
129 SipReinviteSdp => "sip_reinvite_sdp",
130
131 SipGateway => "sip_gateway",
133 SipGatewayName => "sip_gateway_name",
134 SipUseGateway => "sip_use_gateway",
135 SipDestinationUrl => "sip_destination_url",
136
137 SipProfileName => "sip_profile_name",
139 SofiaProfileName => "sofia_profile_name",
140 SofiaProfileUrl => "sofia_profile_url",
141 SofiaProfileDomainName => "sofia_profile_domain_name",
142
143 RtpSecureMediaConfirmed => "rtp_secure_media_confirmed",
145 Rtp2833SendPayload => "rtp_2833_send_payload",
146 Rtp2833RecvPayload => "rtp_2833_recv_payload",
147 RtpDisableHold => "rtp_disable_hold",
148 RtpJitterBufferPlc => "rtp_jitter_buffer_plc",
149 RtpVideoMaxBandwidthIn => "rtp_video_max_bandwidth_in",
150 RtpVideoMaxBandwidthOut => "rtp_video_max_bandwidth_out",
151
152 SipCalleeIdName => "sip_callee_id_name",
154 SipCalleeIdNumber => "sip_callee_id_number",
155 SipCidType => "sip_cid_type",
156
157 SipRtpRxstat => "sip_rtp_rxstat",
159 SipRtpTxstat => "sip_rtp_txstat",
160 SipPRtpStat => "sip_p_rtp_stat",
161
162 SipHistoryInfo => "sip_history_info",
164 SipGeolocation => "sip_geolocation",
165
166 SipMultipart => "sip_multipart",
168
169 SipReferredByUser => "sip_referred_by_user",
171 SipReferredByUserStripped => "sip_referred_by_user_stripped",
172 SipReferredByHost => "sip_referred_by_host",
173 SipReferredByPort => "sip_referred_by_port",
174 SipReferredByUri => "sip_referred_by_uri",
175 SipReferredByParams => "sip_referred_by_params",
176
177 SipFromParams => "sip_from_params",
179 SipToParams => "sip_to_params",
180 SipReqParams => "sip_req_params",
181 SipNameParams => "sip_name_params",
182 SipAcceptLanguageCount => "sip_accept_language_count",
183
184 SipAuthRealm => "sip_auth_realm",
186 SipNumberAlias => "sip_number_alias",
187
188 SipRecoverVia => "sip_recover_via",
190 SipRecoverContact => "sip_recover_contact",
191 SipOutgoingContactUri => "sip_outgoing_contact_uri",
192
193 SipInviteStamp => "sip_invite_stamp",
195 SipReplyPort => "sip_reply_port",
196 SipLocalNetworkAddr => "sip_local_network_addr",
197 }
198}
199
200#[non_exhaustive]
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
203pub enum CarriedHeader {
204 Verbatim(SipHeader),
206 Derived(SipHeader),
209 DerivedOneOf(&'static [SipHeader]),
212}
213
214impl SofiaVariable {
215 pub fn carried_header(&self) -> Option<CarriedHeader> {
225 use CarriedHeader::{Derived, DerivedOneOf, Verbatim};
226 use SipHeader as H;
227
228 match self {
229 Self::SipFromUser
230 | Self::SipFromHost
231 | Self::SipFromPort
232 | Self::SipFromUri
233 | Self::SipFromDisplay
234 | Self::SipFromTag
235 | Self::SipFromComment
236 | Self::SipFromUserStripped
237 | Self::SipFromParams => Some(Derived(H::From)),
238 Self::SipFullFrom => Some(Verbatim(H::From)),
239
240 Self::SipToUser
241 | Self::SipToHost
242 | Self::SipToPort
243 | Self::SipToUri
244 | Self::SipToDisplay
245 | Self::SipToTag
246 | Self::SipToComment
247 | Self::SipToParams => Some(Derived(H::To)),
248 Self::SipFullTo => Some(Verbatim(H::To)),
249
250 Self::SipContactUser
251 | Self::SipContactHost
252 | Self::SipContactPort
253 | Self::SipContactUri
254 | Self::SipContactParams => Some(Derived(H::Contact)),
255
256 Self::SipReqUser
258 | Self::SipReqHost
259 | Self::SipReqPort
260 | Self::SipReqUri
261 | Self::SipReqParams
262 | Self::SipInviteReqUri => None,
263
264 Self::SipViaHost | Self::SipViaPort | Self::SipViaRport | Self::SipViaProtocol => {
265 Some(Derived(H::Via))
266 }
267 Self::SipFullVia | Self::SipRecoverVia => Some(Verbatim(H::Via)),
268 Self::SipFullRoute => Some(Verbatim(H::Route)),
269
270 Self::SipCallId => Some(Verbatim(H::CallId)),
271 Self::SipCseq => Some(Verbatim(H::Cseq)),
272 Self::SipUserAgent => Some(Verbatim(H::UserAgent)),
274 Self::SipSubject => Some(Verbatim(H::Subject)),
275 Self::SipAllow => Some(Verbatim(H::Allow)),
276 Self::SipAcceptLanguage | Self::SipAcceptLanguageCount => {
278 Some(Derived(H::AcceptLanguage))
279 }
280 Self::SipCallInfo => Some(Verbatim(H::CallInfo)),
281 Self::SipDateEpochTime => Some(Derived(H::Date)),
282
283 Self::SipReceivedIp
284 | Self::SipReceivedPort
285 | Self::SipNetworkIp
286 | Self::SipNetworkPort
287 | Self::SipNatDetected
288 | Self::SipTransport
289 | Self::SipReplyHost
290 | Self::SipReplyPort
291 | Self::SipLocalNetworkAddr => None,
292
293 Self::SipAuthUsername
294 | Self::SipAuthPassword
295 | Self::SipAuthorized
296 | Self::SipAclAuthedBy
297 | Self::SipAclToken
298 | Self::SipChallengeRealm
299 | Self::SipNumberAlias => None,
300 Self::SipAuthRealm => Some(DerivedOneOf(&[H::Authorization, H::ProxyAuthorization])),
301
302 Self::SipInviteFailureStatus
303 | Self::SipInviteFailurePhrase
304 | Self::SipHangupDisposition
305 | Self::SipTermStatus
306 | Self::SipTermCause => None,
307 Self::SipReason => Some(Verbatim(H::Reason)),
308
309 Self::SipPAssertedIdentity => Some(Verbatim(H::PAssertedIdentity)),
310 Self::SipPPreferredIdentity => Some(Verbatim(H::PPreferredIdentity)),
311 Self::SipPrivacy => Some(Verbatim(H::Privacy)),
312 Self::SipRemotePartyId => Some(Verbatim(H::RemotePartyId)),
313 Self::SipStirShakenAttest | Self::SipVerstat | Self::SipVerstatDetailed => None,
316
317 Self::SipInviteCallId => Some(Verbatim(H::CallId)),
318 Self::SipInviteCseq => Some(Verbatim(H::Cseq)),
319 Self::SipInviteFullFrom => Some(Verbatim(H::From)),
320 Self::SipInviteFullTo => Some(Verbatim(H::To)),
321 Self::SipInviteFullVia => Some(Verbatim(H::Via)),
322 Self::SipInviteFromUri => Some(Derived(H::From)),
323 Self::SipInviteToUri => Some(Derived(H::To)),
324 Self::SipInviteRecordRoute => Some(Verbatim(H::RecordRoute)),
325 Self::SipInviteRouteUri => Some(Derived(H::Route)),
326 Self::SipInviteDomain | Self::SipInviteParams | Self::SipInviteStamp => None,
327
328 Self::SipAutoAnswer
329 | Self::SipAutoSimplify
330 | Self::SipEnableSoa
331 | Self::SipCopyCustomHeaders
332 | Self::SipCopyMultipart
333 | Self::SipLoopedCall => None,
334
335 Self::SipRedirectedTo | Self::SipRecoverContact | Self::SipOutgoingContactUri => {
336 Some(Verbatim(H::Contact))
337 }
338 Self::SipRedirectedBy => Some(Verbatim(H::Diversion)),
339 Self::SipReferredByFull => Some(Verbatim(H::ReferredBy)),
340 Self::SipReferredByCid
341 | Self::SipReferredByUser
342 | Self::SipReferredByUserStripped
343 | Self::SipReferredByHost
344 | Self::SipReferredByPort
345 | Self::SipReferredByUri
346 | Self::SipReferredByParams => Some(Derived(H::ReferredBy)),
347 Self::SipNameParams => Some(DerivedOneOf(&[
350 H::From,
351 H::PAssertedIdentity,
352 H::PPreferredIdentity,
353 H::RemotePartyId,
354 ])),
355 Self::SipRedirectDialstring
356 | Self::SipReferReply
357 | Self::SipReferStatusCode
358 | Self::SipReinviteSdp => None,
359
360 Self::SipGateway
361 | Self::SipGatewayName
362 | Self::SipUseGateway
363 | Self::SipDestinationUrl
364 | Self::SipProfileName
365 | Self::SofiaProfileName
366 | Self::SofiaProfileUrl
367 | Self::SofiaProfileDomainName => None,
368
369 Self::RtpSecureMediaConfirmed
370 | Self::Rtp2833SendPayload
371 | Self::Rtp2833RecvPayload
372 | Self::RtpDisableHold
373 | Self::RtpJitterBufferPlc
374 | Self::RtpVideoMaxBandwidthIn
375 | Self::RtpVideoMaxBandwidthOut => None,
376
377 Self::SipCalleeIdName | Self::SipCalleeIdNumber | Self::SipCidType => None,
380
381 Self::SipRtpRxstat | Self::SipRtpTxstat | Self::SipPRtpStat => None,
383
384 Self::SipHistoryInfo => Some(Verbatim(H::HistoryInfo)),
385 Self::SipGeolocation => Some(Verbatim(H::Geolocation)),
386
387 Self::SipMultipart => None,
388 }
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395
396 #[test]
397 fn display_round_trip() {
398 assert_eq!(SofiaVariable::SipCallId.to_string(), "sip_call_id");
399 assert_eq!(
400 SofiaVariable::SipFromDisplay.to_string(),
401 "sip_from_display"
402 );
403 assert_eq!(
404 SofiaVariable::SofiaProfileName.to_string(),
405 "sofia_profile_name"
406 );
407 }
408
409 #[test]
410 fn as_ref_str() {
411 let v: &str = SofiaVariable::SipNetworkIp.as_ref();
412 assert_eq!(v, "sip_network_ip");
413 }
414
415 #[test]
416 fn from_str_case_insensitive() {
417 assert_eq!(
418 "sip_call_id".parse::<SofiaVariable>(),
419 Ok(SofiaVariable::SipCallId)
420 );
421 assert_eq!(
422 "SIP_CALL_ID".parse::<SofiaVariable>(),
423 Ok(SofiaVariable::SipCallId)
424 );
425 }
426
427 #[test]
428 fn from_str_unknown() {
429 let err = "nonexistent_sip_var".parse::<SofiaVariable>();
430 assert!(err.is_err());
431 }
432
433 #[test]
434 fn carried_header_verbatim() {
435 assert_eq!(
436 SofiaVariable::SipPrivacy.carried_header(),
437 Some(CarriedHeader::Verbatim(SipHeader::Privacy))
438 );
439 assert_eq!(
440 SofiaVariable::SipPAssertedIdentity.carried_header(),
441 Some(CarriedHeader::Verbatim(SipHeader::PAssertedIdentity))
442 );
443 assert_eq!(
444 SofiaVariable::SipRemotePartyId.carried_header(),
445 Some(CarriedHeader::Verbatim(SipHeader::RemotePartyId))
446 );
447 assert_eq!(
448 SofiaVariable::SipFullFrom.carried_header(),
449 Some(CarriedHeader::Verbatim(SipHeader::From))
450 );
451 }
452
453 #[test]
454 fn carried_header_derived() {
455 assert_eq!(
456 SofiaVariable::SipFromUser.carried_header(),
457 Some(CarriedHeader::Derived(SipHeader::From))
458 );
459 }
460
461 #[test]
462 fn recovery_and_outbound_contact_are_verbatim() {
463 assert_eq!(
464 SofiaVariable::SipRecoverVia.carried_header(),
465 Some(CarriedHeader::Verbatim(SipHeader::Via))
466 );
467 assert_eq!(
468 SofiaVariable::SipRecoverContact.carried_header(),
469 Some(CarriedHeader::Verbatim(SipHeader::Contact))
470 );
471 assert_eq!(
472 SofiaVariable::SipOutgoingContactUri.carried_header(),
473 Some(CarriedHeader::Verbatim(SipHeader::Contact))
474 );
475 }
476
477 #[test]
478 fn parsed_sub_fields_are_derived() {
479 assert_eq!(
480 SofiaVariable::SipFromParams.carried_header(),
481 Some(CarriedHeader::Derived(SipHeader::From))
482 );
483 assert_eq!(
484 SofiaVariable::SipToParams.carried_header(),
485 Some(CarriedHeader::Derived(SipHeader::To))
486 );
487 assert_eq!(
488 SofiaVariable::SipReferredByParams.carried_header(),
489 Some(CarriedHeader::Derived(SipHeader::ReferredBy))
490 );
491 assert_eq!(
492 SofiaVariable::SipAcceptLanguageCount.carried_header(),
493 Some(CarriedHeader::Derived(SipHeader::AcceptLanguage))
494 );
495 }
496
497 #[test]
500 fn runtime_chosen_source_names_the_candidates() {
501 assert_eq!(
502 SofiaVariable::SipNameParams.carried_header(),
503 Some(CarriedHeader::DerivedOneOf(&[
504 SipHeader::From,
505 SipHeader::PAssertedIdentity,
506 SipHeader::PPreferredIdentity,
507 SipHeader::RemotePartyId,
508 ]))
509 );
510 assert_eq!(
511 SofiaVariable::SipAuthRealm.carried_header(),
512 Some(CarriedHeader::DerivedOneOf(&[
513 SipHeader::Authorization,
514 SipHeader::ProxyAuthorization,
515 ]))
516 );
517 }
518
519 #[test]
520 fn switch_side_variables_carry_no_header() {
521 for var in [
522 SofiaVariable::SipInviteStamp,
523 SofiaVariable::SipReplyPort,
524 SofiaVariable::SipLocalNetworkAddr,
525 SofiaVariable::SipNumberAlias,
526 SofiaVariable::SipReqParams,
527 ] {
528 assert_eq!(var.carried_header(), None, "{}", var.as_str());
529 }
530 }
531
532 #[test]
533 fn carried_header_none() {
534 assert_eq!(SofiaVariable::SipReqUri.carried_header(), None);
536 assert_eq!(SofiaVariable::SipGateway.carried_header(), None);
537 }
538
539 #[test]
542 fn hyphenated_wire_names_match_their_header() {
543 for var in SofiaVariable::ALL {
544 let Some(suffix) = var
545 .as_str()
546 .strip_prefix("sip_")
547 else {
548 continue;
549 };
550 if !suffix.contains('-') {
551 continue;
552 }
553 let Some(carried) = var.carried_header() else {
554 panic!("{} spells a header but carries none", var.as_str());
555 };
556 let CarriedHeader::Verbatim(header) = carried else {
557 panic!("{} spells a header but is not verbatim", var.as_str());
558 };
559 assert_eq!(header.as_str(), suffix, "for {}", var.as_str());
560 }
561 }
562}