use super::{
authenticate::Credential,
client_dialog::ClientInviteDialog,
dialog::{DialogInner, DialogStateSender},
dialog_layer::DialogLayer,
};
use crate::rsip;
use crate::{
dialog::{dialog::Dialog, dialog_layer::DialogLayerInnerRef, DialogId},
transaction::{
key::{TransactionKey, TransactionRole},
make_tag,
transaction::Transaction,
},
transport::SipAddr,
Result,
};
use rsip::{Request, Response};
use std::sync::Arc;
use tracing::{debug, info, warn};
#[derive(Default, Clone)]
pub struct InviteOption {
pub caller: rsip::Uri,
pub callee: rsip::Uri,
pub destination: Option<SipAddr>,
pub content_type: Option<String>,
pub offer: Option<Vec<u8>>,
pub contact: rsip::Uri,
pub credential: Option<Credential>,
pub headers: Option<Vec<rsip::Header>>,
}
pub struct DialogGuard {
pub dialog_layer_inner: DialogLayerInnerRef,
pub id: DialogId,
}
impl DialogGuard {
pub fn new(dialog_layer: &Arc<DialogLayer>, id: DialogId) -> Self {
Self {
dialog_layer_inner: dialog_layer.inner.clone(),
id,
}
}
}
impl Drop for DialogGuard {
fn drop(&mut self) {
let dlg = match self.dialog_layer_inner.dialogs.write() {
Ok(mut dialogs) => match dialogs.remove(&self.id) {
Some(dlg) => dlg,
None => return,
},
_ => return,
};
let _ = tokio::spawn(async move {
if let Err(e) = dlg.hangup().await {
info!(id=%dlg.id(), "failed to hangup dialog: {}", e);
}
});
}
}
pub(super) struct DialogGuardForUnconfirmed<'a> {
pub dialog_layer_inner: &'a DialogLayerInnerRef,
pub id: &'a DialogId,
}
impl<'a> Drop for DialogGuardForUnconfirmed<'a> {
fn drop(&mut self) {
match self.dialog_layer_inner.dialogs.write() {
Ok(mut dialogs) => {
if let Some(dlg) = dialogs.get(self.id) {
if !dlg.can_cancel() {
return;
}
match dialogs.remove(self.id) {
Some(dlg) => {
info!(%self.id, "unconfirmed dialog dropped, cancelling it");
let _ = tokio::spawn(async move {
if let Err(e) = dlg.hangup().await {
info!(id=%dlg.id(), "failed to hangup unconfirmed dialog: {}", e);
}
});
}
None => {}
}
}
}
Err(e) => {
warn!(%self.id, "failed to acquire write lock on dialogs: {}", e);
}
}
}
}
impl DialogLayer {
pub fn make_invite_request(&self, opt: &InviteOption) -> Result<Request> {
let last_seq = self.increment_last_seq();
let to = rsip::typed::To {
display_name: None,
uri: opt.callee.clone(),
params: vec![],
};
let recipient = to.uri.clone();
let form = rsip::typed::From {
display_name: None,
uri: opt.caller.clone(),
params: vec![],
}
.with_tag(make_tag());
let via = self.endpoint.get_via(None, None)?;
let mut request =
self.endpoint
.make_request(rsip::Method::Invite, recipient, via, form, to, last_seq);
let contact = rsip::typed::Contact {
display_name: None,
uri: opt.contact.clone(),
params: vec![],
};
request
.headers
.unique_push(rsip::Header::Contact(contact.into()));
request.headers.unique_push(rsip::Header::ContentType(
opt.content_type
.clone()
.unwrap_or("application/sdp".to_string())
.into(),
));
if let Some(headers) = opt.headers.as_ref() {
for header in headers {
request.headers.unique_push(header.clone());
}
}
Ok(request)
}
pub async fn do_invite(
&self,
opt: InviteOption,
state_sender: DialogStateSender,
) -> Result<(ClientInviteDialog, Option<Response>)> {
let (dialog, tx) = self.create_client_invite_dialog(opt, state_sender)?;
let id = dialog.id();
self.inner
.dialogs
.write()
.unwrap()
.insert(id.clone(), Dialog::ClientInvite(dialog.clone()));
info!(%id, "client invite dialog created");
let _guard = DialogGuardForUnconfirmed {
dialog_layer_inner: &self.inner,
id: &id,
};
match dialog.process_invite(tx).await {
Ok((new_dialog_id, resp)) => {
debug!(
"client invite dialog confirmed: {} => {}",
id, new_dialog_id
);
self.inner.dialogs.write().unwrap().remove(&id);
self.inner
.dialogs
.write()
.unwrap()
.insert(new_dialog_id, Dialog::ClientInvite(dialog.clone()));
return Ok((dialog, resp));
}
Err(e) => {
self.inner.dialogs.write().unwrap().remove(&id);
return Err(e);
}
}
}
pub fn create_client_invite_dialog(
&self,
opt: InviteOption,
state_sender: DialogStateSender,
) -> Result<(ClientInviteDialog, Transaction)> {
let mut request = self.make_invite_request(&opt)?;
request.body = opt.offer.unwrap_or_default();
request.headers.unique_push(rsip::Header::ContentLength(
(request.body.len() as u32).into(),
));
let key = TransactionKey::from_request(&request, TransactionRole::Client)?;
let mut tx = Transaction::new_client(key, request.clone(), self.endpoint.clone(), None);
tx.destination = opt.destination;
let id = DialogId::try_from(&request)?;
let mut dlg_inner = DialogInner::new(
TransactionRole::Client,
id.clone(),
request.clone(),
self.endpoint.clone(),
state_sender,
opt.credential,
Some(opt.contact),
tx.tu_sender.clone(),
)?;
dlg_inner.initial_destination = tx.destination.clone();
let dialog = ClientInviteDialog {
inner: Arc::new(dlg_inner),
};
Ok((dialog, tx))
}
pub fn confirm_client_dialog(&self, dialog: ClientInviteDialog) {
self.inner
.dialogs
.write()
.unwrap()
.insert(dialog.id(), Dialog::ClientInvite(dialog));
}
}