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 { Ok($name(<$type>::from_str(s)?)) } } }; } pub trait Header: FromStr + 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 { 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 { // 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::()?; 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 { todo!() } } pub fn unquote(v: &str) -> Result { 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, 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 { todo!() } }