aboutsummaryrefslogtreecommitdiff
path: root/sip/src/encoding/headers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sip/src/encoding/headers.rs')
-rw-r--r--sip/src/encoding/headers.rs182
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)?,
+ })
+ }
+}