use crate::*;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult};
#[derive(Debug, PartialEq, Clone)]
pub struct RegistrationManager {
account_uri: Uri,
local_uri: Uri,
cseq_counter: u32,
nonce_c: u32,
c_nonce: Option<String>,
auth_header: Option<AuthHeader>,
branch: String,
call_id: String,
expires_header: Option<u32>,
user: Option<String>,
pass: Option<String>,
realm: Option<String>,
nonce: Option<String>,
}
impl RegistrationManager {
pub fn new(account_uri: Uri, local_uri: Uri) -> RegistrationManager {
RegistrationManager {
account_uri,
local_uri,
cseq_counter: 444,
auth_header: None,
nonce_c: 1,
c_nonce: None,
branch: format!("{:x}", md5::compute(rand::random::<[u8; 16]>())),
call_id: format!("{:x}", md5::compute(rand::random::<[u8; 16]>())),
expires_header: None,
user: None,
pass: None,
realm: None,
nonce: None,
}
}
pub fn username<S: Into<String>>(&mut self, s: S) {
self.user = Some(s.into());
}
pub fn password<S: Into<String>>(&mut self, p: S) {
self.pass = Some(p.into());
}
pub fn get_request(&mut self, cfg: &HeaderWriteConfig) -> IoResult<SipMessage> {
self.cseq_counter += 1;
self.nonce_c += 1;
let to_header = self.account_uri.clone();
let from_header = self.account_uri.clone();
let mut contact_header = self.local_uri.clone();
let mut headers = vec![];
if let Some(name) = &self.user {
contact_header = contact_header.auth(UriAuth::new(name));
if let Some(auth_header) = &self.auth_header {
if let Some(pass) = &self.pass {
let ctx = AuthContext {
user: &name,
pass,
nc: self.nonce_c,
uri: &self.account_uri,
};
headers.push(Header::Authorization(auth_header.authenticate(ctx)?));
}
}
}
headers.push(Header::ContentLength(0));
headers.push(Header::To(NamedHeader::new(to_header)));
headers.push(Header::From(NamedHeader::new(from_header)));
headers.push(Header::Contact(NamedHeader::new(contact_header)));
headers.push(Header::CSeq(self.cseq_counter, Method::Register));
headers.push(Header::CallId(format!(
"{}@{}",
self.call_id,
self.account_uri.host()
)));
headers.push(self.via_header());
cfg.write_headers_vec(&mut headers);
if let Some(exp) = self.expires_header {
headers.push(Header::Expires(exp));
}
Ok(RequestGenerator::new()
.method(Method::Register)
.uri(self.account_uri.clone().authless())
.headers(headers)
.build()?)
}
pub fn set_challenge(&mut self, msg: SipMessage) -> IoResult<()> {
if let SipMessage::Response { headers, .. } = msg {
for item in headers.into_iter() {
match item {
Header::WwwAuthenticate(auth) => {
self.auth_header = Some(auth);
},
Header::Expires(expire) => {
self.expires_header = Some(expire);
},
_ => {},
}
}
Ok(())
} else {
Err(IoError::new(
IoErrorKind::InvalidInput,
"Challenge Response was not a SIP response",
))
}
}
pub fn expires(&self) -> u32 {
self.expires_header.unwrap_or(60)
}
pub fn cseq(&self) -> u32 {
self.cseq_counter
}
pub fn via_header(&self) -> Header {
let via_uri = self
.local_uri
.clone()
.parameter(UriParam::Branch(self.branch.clone()))
.authless()
.schemaless();
Header::Via(ViaHeader {
uri: via_uri,
version: Default::default(),
transport: Transport::Udp,
})
}
}