use std::borrow::Borrow; use anyhow::{anyhow, Result}; use quick_xml::events::{BytesEnd, BytesStart, Event}; use quick_xml::{NsReader, Writer}; use tokio::io::{AsyncBufRead, AsyncWrite}; use super::skip_text; pub enum Mechanism { Plain, } impl Mechanism { pub fn to_str(&self) -> &'static str { match self { Mechanism::Plain => "PLAIN", } } pub fn from_str(input: &[u8]) -> Result { match input { b"PLAIN" => Ok(Mechanism::Plain), _ => Err(anyhow!("unknown auth mechanism: {input:?}")), } } } pub struct Auth { pub mechanism: Mechanism, pub body: Vec, } impl Auth { pub async fn parse(reader: &mut NsReader, buf: &mut Vec) -> Result { let event = skip_text!(reader, buf); let mechanism = if let Event::Start(bytes) = event { let mut mechanism = None; for attr in bytes.attributes() { let attr = attr?; if attr.key.0 == b"mechanism" { mechanism = Some(attr.value) } } if let Some(mechanism) = mechanism { Mechanism::from_str(mechanism.borrow())? } else { return Err(anyhow!("expected mechanism attribute in ")); } } else { return Err(anyhow!("expected start of ")); }; let body = if let Event::Text(text) = reader.read_event_into_async(buf).await? { text.into_inner().into_owned() } else { return Err(anyhow!("expected text body in ")); }; if let Event::End(_) = reader.read_event_into_async(buf).await? { //TODO } else { return Err(anyhow!("expected end of ")); }; Ok(Auth { mechanism, body }) } } pub struct Success; impl Success { pub async fn write_xml(&self, writer: &mut Writer) -> Result<()> { let event = BytesStart::new(r#"success xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#); writer.write_event_async(Event::Empty(event)).await?; Ok(()) } } pub struct Failure; impl Failure { pub async fn write_xml(&self, writer: &mut Writer) -> Result<()> { let event = BytesStart::new(r#"failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#); writer.write_event_async(Event::Start(event)).await?; let event = BytesStart::new(r#"not-authorized"#); writer.write_event_async(Event::Empty(event)).await?; let event = BytesEnd::new(r#"failure"#); writer.write_event_async(Event::End(event)).await?; Ok(()) } }