use crate::api::dialog_package::{DialogInfo, DialogInfoDocument};
use crate::state_table::types::SessionId;
pub use rvoip_infra_common::events::cross_crate::{SipTraceConfig, SipTraceDirection};
use rvoip_sip_core::types::sdp::CryptoSuite;
pub type CallId = SessionId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SipTrace {
pub direction: SipTraceDirection,
pub transport: String,
pub local_addr: String,
pub remote_addr: String,
pub timestamp_unix_millis: u64,
pub start_line: String,
pub sip_call_id: Option<String>,
pub session_id: Option<CallId>,
pub raw_message: String,
pub original_len: usize,
pub truncated: bool,
pub redacted: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransferKind {
Blind,
Attended,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransferTargetEvidence {
ReferProgressThenFinal {
progress_status_code: u16,
progress_reason: String,
final_status_code: u16,
final_reason: String,
},
LocalTargetLeg {
call_id: CallId,
},
DialogPackage {
dialog: DialogInfo,
},
}
impl TransferKind {
pub fn from_header_value(value: &str) -> Self {
match value.to_ascii_lowercase().as_str() {
"blind" => Self::Blind,
"attended" => Self::Attended,
_ => Self::Unknown,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Blind => "blind",
Self::Attended => "attended",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptionState {
pub state: String,
pub expires: Option<u32>,
pub reason: Option<String>,
pub raw: String,
}
impl SubscriptionState {
pub fn parse(raw: impl Into<String>) -> Self {
let raw = raw.into();
let mut parts = raw.split(';').map(str::trim);
let state = parts.next().unwrap_or_default().to_string();
let mut expires = None;
let mut reason = None;
for part in parts {
if let Some(value) = part.strip_prefix("expires=") {
expires = value.parse::<u32>().ok();
} else if let Some(value) = part.strip_prefix("reason=") {
reason = Some(value.to_string());
}
}
Self {
state,
expires,
reason,
raw,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaSecurityKeying {
Sdes,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaSecurityProfile {
RtpSavp,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MediaSecurityState {
pub keying: MediaSecurityKeying,
pub suite: CryptoSuite,
pub profile: MediaSecurityProfile,
pub contexts_installed: bool,
}
#[derive(Debug, Clone)]
pub enum Event {
IncomingCall {
call_id: CallId,
from: String,
to: String,
sdp: Option<String>,
},
CallAnswered {
call_id: CallId,
sdp: Option<String>,
},
CallProgress {
call_id: CallId,
status_code: u16,
reason: String,
sdp: Option<String>,
},
CallEnded {
call_id: CallId,
reason: String,
},
CallFailed {
call_id: CallId,
status_code: u16,
reason: String,
},
CallProgressDetailed(crate::api::incoming::IncomingResponse),
CallEstablishedDetailed(crate::api::incoming::IncomingResponse),
CallFailedDetailed(crate::api::incoming::IncomingResponse),
CallCancelled {
call_id: CallId,
},
SessionRefreshed {
call_id: CallId,
expires_secs: u32,
},
SessionRefreshFailed {
call_id: CallId,
reason: String,
},
CallAuthRetrying {
call_id: CallId,
status_code: u16,
realm: String,
},
ReferReceived {
call_id: CallId,
refer_to: String,
referred_by: Option<String>,
replaces: Option<String>,
transaction_id: String, transfer_type: String, request: Option<crate::api::incoming::IncomingRequest>,
},
TransferAccepted {
call_id: CallId,
refer_to: String,
},
ReferCompleted {
call_id: CallId,
target: String,
status_code: u16,
reason: String,
},
TransferFailed {
call_id: CallId,
reason: String,
status_code: u16,
},
ReferProgress {
call_id: CallId,
status_code: u16,
reason: String,
},
ReferNotify {
call_id: CallId,
status_code: u16,
reason: String,
subscription_state: Option<SubscriptionState>,
body: Option<String>,
},
TransferTargetAnswered {
transfer_call_id: CallId,
target_uri: String,
evidence: TransferTargetEvidence,
},
TransferReplacementDialogObserved {
transfer_call_id: CallId,
dialog: DialogInfo,
},
TransferReplacementDialogTerminated {
transfer_call_id: CallId,
dialog: DialogInfo,
reason: Option<String>,
},
NotifyReceived {
call_id: CallId,
event_package: String,
subscription_state: Option<String>,
content_type: Option<String>,
body: Option<String>,
request: Option<crate::api::incoming::IncomingRequest>,
},
InfoReceived {
call_id: CallId,
request: crate::api::incoming::IncomingRequest,
},
MessageReceived {
call_id: CallId,
request: crate::api::incoming::IncomingRequest,
},
OptionsReceived {
call_id: Option<CallId>,
request: crate::api::incoming::IncomingRequest,
},
UpdateReceived {
call_id: CallId,
request: crate::api::incoming::IncomingRequest,
},
IncomingRegister {
register: crate::api::incoming::IncomingRegister,
},
DialogPackageNotify {
subscription_id: CallId,
entity: Option<String>,
version: Option<u32>,
dialogs: Vec<DialogInfo>,
document: DialogInfoDocument,
},
DialogStateChanged {
subscription_id: CallId,
dialog: DialogInfo,
},
CallOnHold {
call_id: CallId,
},
CallResumed {
call_id: CallId,
},
RemoteCallOnHold {
call_id: CallId,
},
RemoteCallResumed {
call_id: CallId,
},
CallMuted {
call_id: CallId,
},
CallUnmuted {
call_id: CallId,
},
DtmfReceived {
call_id: CallId,
digit: char,
},
MediaQualityChanged {
call_id: CallId,
packet_loss_percent: u32,
jitter_ms: u32,
},
MediaSecurityNegotiated {
call_id: CallId,
keying: MediaSecurityKeying,
suite: CryptoSuite,
profile: MediaSecurityProfile,
contexts_installed: bool,
},
RegistrationSuccess {
registrar: String,
expires: u32,
contact: String,
},
RegistrationFailed {
registrar: String,
status_code: u16,
reason: String,
},
UnregistrationSuccess {
registrar: String,
},
UnregistrationFailed {
registrar: String,
reason: String,
},
SipTrace(SipTrace),
NetworkError {
call_id: Option<CallId>,
error: String,
},
AuthenticationRequired {
call_id: CallId,
realm: String,
},
}
impl Event {
pub fn call_id(&self) -> Option<&CallId> {
match self {
Event::IncomingCall { call_id, .. }
| Event::CallAnswered { call_id, .. }
| Event::CallProgress { call_id, .. }
| Event::CallEnded { call_id, .. }
| Event::CallFailed { call_id, .. }
| Event::CallCancelled { call_id, .. }
| Event::SessionRefreshed { call_id, .. }
| Event::SessionRefreshFailed { call_id, .. }
| Event::CallAuthRetrying { call_id, .. }
| Event::ReferReceived { call_id, .. }
| Event::TransferAccepted { call_id, .. }
| Event::TransferFailed { call_id, .. }
| Event::ReferProgress { call_id, .. }
| Event::ReferNotify { call_id, .. }
| Event::ReferCompleted { call_id, .. }
| Event::CallOnHold { call_id, .. }
| Event::CallResumed { call_id, .. }
| Event::RemoteCallOnHold { call_id, .. }
| Event::RemoteCallResumed { call_id, .. }
| Event::CallMuted { call_id, .. }
| Event::CallUnmuted { call_id, .. }
| Event::DtmfReceived { call_id, .. }
| Event::MediaQualityChanged { call_id, .. }
| Event::MediaSecurityNegotiated { call_id, .. }
| Event::NotifyReceived { call_id, .. }
| Event::AuthenticationRequired { call_id, .. } => Some(call_id),
Event::TransferTargetAnswered {
transfer_call_id, ..
}
| Event::TransferReplacementDialogObserved {
transfer_call_id, ..
}
| Event::TransferReplacementDialogTerminated {
transfer_call_id, ..
} => Some(transfer_call_id),
Event::DialogPackageNotify {
subscription_id, ..
}
| Event::DialogStateChanged {
subscription_id, ..
} => Some(subscription_id),
Event::SipTrace(trace) => trace.session_id.as_ref(),
Event::NetworkError { call_id, .. } => call_id.as_ref(),
Event::CallProgressDetailed(r)
| Event::CallEstablishedDetailed(r)
| Event::CallFailedDetailed(r) => Some(&r.call_id),
Event::InfoReceived { call_id, .. }
| Event::MessageReceived { call_id, .. }
| Event::UpdateReceived { call_id, .. } => Some(call_id),
Event::OptionsReceived { call_id, .. } => call_id.as_ref(),
Event::RegistrationSuccess { .. }
| Event::RegistrationFailed { .. }
| Event::UnregistrationSuccess { .. }
| Event::UnregistrationFailed { .. }
| Event::IncomingRegister { .. } => None,
}
}
pub fn is_call_event(&self) -> bool {
matches!(
self,
Event::IncomingCall { .. }
| Event::CallAnswered { .. }
| Event::CallProgress { .. }
| Event::CallEnded { .. }
| Event::CallFailed { .. }
| Event::CallCancelled { .. }
| Event::CallProgressDetailed(_)
| Event::CallEstablishedDetailed(_)
| Event::CallFailedDetailed(_)
| Event::InfoReceived { .. }
| Event::MessageReceived { .. }
| Event::OptionsReceived { .. }
| Event::UpdateReceived { .. }
)
}
pub fn is_call_state_event(&self) -> bool {
matches!(
self,
Event::CallOnHold { .. }
| Event::CallResumed { .. }
| Event::RemoteCallOnHold { .. }
| Event::RemoteCallResumed { .. }
| Event::CallMuted { .. }
| Event::CallUnmuted { .. }
)
}
pub fn is_transfer_event(&self) -> bool {
matches!(
self,
Event::ReferReceived { .. }
| Event::TransferAccepted { .. }
| Event::ReferCompleted { .. }
| Event::TransferFailed { .. }
| Event::ReferProgress { .. }
| Event::ReferNotify { .. }
| Event::TransferTargetAnswered { .. }
| Event::TransferReplacementDialogObserved { .. }
| Event::TransferReplacementDialogTerminated { .. }
)
}
pub fn is_media_event(&self) -> bool {
matches!(
self,
Event::DtmfReceived { .. }
| Event::MediaQualityChanged { .. }
| Event::MediaSecurityNegotiated { .. }
)
}
pub fn transfer_kind(&self) -> Option<TransferKind> {
match self {
Event::ReferReceived { transfer_type, .. } => {
Some(TransferKind::from_header_value(transfer_type))
}
_ => None,
}
}
pub fn subscription_state(&self) -> Option<SubscriptionState> {
match self {
Event::NotifyReceived {
subscription_state: Some(raw),
..
} => Some(SubscriptionState::parse(raw.clone())),
Event::ReferNotify {
subscription_state: Some(parsed),
..
} => Some(parsed.clone()),
_ => None,
}
}
}