diff options
Diffstat (limited to 'sip/src/encoding/headers.rs')
-rw-r--r-- | sip/src/encoding/headers.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/sip/src/encoding/headers.rs b/sip/src/encoding/headers.rs new file mode 100644 index 0000000..e880739 --- /dev/null +++ b/sip/src/encoding/headers.rs @@ -0,0 +1,182 @@ +use super::{headermap::HeaderMap, method::Method, uri::Uri}; +use anyhow::{anyhow, bail, Result}; +use std::{fmt::Display, str::FromStr}; + +macro_rules! header { + ($hname:literal, struct $name:ident($type:ty)) => { + #[derive(Debug)] + pub struct $name(pub $type); + impl Header for $name { + const NAME: &'static str = $hname; + } + impl Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + impl FromStr for $name { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok($name(<$type>::from_str(s)?)) + } + } + }; +} + +pub trait Header: FromStr<Err = anyhow::Error> + Display { + const NAME: &'static str; +} + +header!("Content-Length", struct ContentLength(usize)); +header!("Content-Type", struct ContentType(String)); +header!("Call-ID", struct CallID(String)); +header!("Via", struct Via(String)); +header!("Max-Forwards", struct MaxForwards(usize)); +header!("From", struct From(String)); +header!("To", struct To(String)); +header!("User-Agent", struct UserAgent(String)); +header!("Allow", struct Allow(String)); + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct CSeq(pub u32, pub Method); + +impl Header for CSeq { + const NAME: &'static str = "CSeq"; +} +impl Display for CSeq { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.0, self.1) + } +} +impl FromStr for CSeq { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (seq, method) = s.split_once(" ").ok_or(anyhow!("method missing"))?; + Ok(CSeq(seq.parse()?, method.parse()?)) + } +} + +#[derive(Debug)] +pub struct WWWAuthenticate { + pub realm: String, + pub nonce: String, +} +impl Header for WWWAuthenticate { + const NAME: &'static str = "WWW-Authenticate"; +} +impl Display for WWWAuthenticate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Digest realm={:?}, nonce={:?}", self.realm, self.nonce) + } +} +impl FromStr for WWWAuthenticate { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + // TODO this is totally wrong + let kvs = s + .strip_prefix("Digest ") + .ok_or(anyhow!("type not digest"))? + .split(",") + .map(|e| { + let Some((k, v)) = e.split_once("=") else { + bail!("not a KV-pair") + }; + Ok((k.trim().to_string(), v.trim().to_string())) + }) + .try_collect::<HeaderMap>()?; + Ok(WWWAuthenticate { + realm: unquote( + &kvs.get_raw("realm") + .ok_or(anyhow!("realm missing"))? + .to_string(), + )?, + nonce: unquote( + &kvs.get_raw("nonce") + .ok_or(anyhow!("nonce missing"))? + .to_string(), + )?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct Authorization { + pub username: String, + pub realm: String, + pub uri: String, + pub nonce: String, + pub response: String, +} +impl Header for Authorization { + const NAME: &'static str = "Authorization"; +} +impl Display for Authorization { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + username, + realm, + nonce, + uri, + response, + } = self; + write!( + f, + "Digest username={username:?},\r\n realm={realm:?},\r\n nonce={nonce:?},\r\n uri={uri:?},\r\n response={response:?},\r\n algorithm=MD5" + ) + } +} +impl FromStr for Authorization { + type Err = anyhow::Error; + fn from_str(_s: &str) -> Result<Self, Self::Err> { + todo!() + } +} + +pub fn unquote(v: &str) -> Result<String> { + Ok(v.strip_prefix("\"") + .ok_or(anyhow!("start quote missing"))? + .strip_suffix("\"") + .ok_or(anyhow!("end quote missing"))? + .to_string()) +} + +#[derive(Debug)] +pub struct Contact { + pub display_name: Option<String>, + pub uri: Uri, + pub params: String, +} + +impl Header for Contact { + const NAME: &'static str = "Contact"; +} +impl Display for Contact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + display_name, + uri, + params, + } = self; + if let Some(display_name) = display_name { + write!(f, "{display_name} <{uri}>{params}") + } else { + write!(f, "<{uri}>{params}") + } + } +} +impl FromStr for Contact { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (display_name, rest) = s.split_once("<").ok_or(anyhow!("malformed contact"))?; + let (uri, params) = rest.split_once(">;").ok_or(anyhow!("malformed contact"))?; + Ok(Self { + display_name: if display_name.is_empty() { + None + } else { + Some(display_name.to_string()) + }, + params: params.to_string(), + uri: Uri::from_str(uri)?, + }) + } +} |