diff options
| author | 2023-10-20 04:51:56 +0100 | |
|---|---|---|
| committer | 2023-10-20 04:51:56 +0100 | |
| commit | ba94ee66fafbabd63d6d1ed5edf435d4c46c6796 (patch) | |
| tree | fe1bebc35914941b5c4fbd6f0286f4c9f8916154 /src | |
| parent | 2536fa4937f0283b4187142cc6cede8e1dbfafa8 (diff) | |
| download | luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.tar.gz luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.tar.bz2 luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.zip | |
WIP: refactor to parse incoming stream as state machine
Diffstat (limited to '')
| -rw-r--r-- | src/client/encrypted.rs | 16 | ||||
| -rw-r--r-- | src/client/mod.rs | 10 | ||||
| -rw-r--r-- | src/client/unencrypted.rs | 199 | ||||
| -rw-r--r-- | src/jabber.rs | 18 | ||||
| -rw-r--r-- | src/jid.rs (renamed from src/jid/mod.rs) | 19 | ||||
| -rw-r--r-- | src/lib.rs | 25 | ||||
| -rw-r--r-- | src/stanza/bind.rs | 47 | ||||
| -rw-r--r-- | src/stanza/iq.rs | 169 | ||||
| -rw-r--r-- | src/stanza/message.rs | 1 | ||||
| -rw-r--r-- | src/stanza/mod.rs | 656 | ||||
| -rw-r--r-- | src/stanza/presence.rs | 1 | ||||
| -rw-r--r-- | src/stanza/sasl.rs | 144 | ||||
| -rw-r--r-- | src/stanza/starttls.rs | 1 | ||||
| -rw-r--r-- | src/stanza/stream.rs | 226 | 
14 files changed, 237 insertions, 1295 deletions
| diff --git a/src/client/encrypted.rs b/src/client/encrypted.rs index 47b2b2c..263d5ff 100644 --- a/src/client/encrypted.rs +++ b/src/client/encrypted.rs @@ -2,36 +2,26 @@ use std::{collections::BTreeMap, str};  use quick_xml::{      events::{BytesDecl, Event}, -    Reader, Writer, +    NsReader, Writer,  };  use rsasl::prelude::{Mechname, SASLClient};  use tokio::io::{BufReader, ReadHalf, WriteHalf};  use tokio::net::TcpStream;  use tokio_native_tls::TlsStream; -use crate::stanza::{ -    bind::Bind, -    iq::IQ, -    sasl::{Challenge, Success}, -    Element, -}; -use crate::stanza::{ -    sasl::{Auth, Response}, -    stream::{Stream, StreamFeature}, -};  use crate::Jabber;  use crate::JabberError;  use crate::Result;  pub struct JabberClient<'j> { -    pub reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>, +    pub reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,      pub writer: Writer<WriteHalf<TlsStream<TcpStream>>>,      jabber: &'j mut Jabber<'j>,  }  impl<'j> JabberClient<'j> {      pub fn new( -        reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>, +        reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,          writer: Writer<WriteHalf<TlsStream<TcpStream>>>,          jabber: &'j mut Jabber<'j>,      ) -> Self { diff --git a/src/client/mod.rs b/src/client/mod.rs index 280e0a1..01df4a4 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,22 +1,24 @@ -pub mod encrypted; +// pub mod encrypted;  pub mod unencrypted;  // use async_trait::async_trait; -use crate::stanza::stream::StreamFeature; +// use crate::stanza::stream::StreamFeature;  use crate::JabberError;  use crate::Result;  pub enum JabberClientType<'j> { -    Encrypted(encrypted::JabberClient<'j>), +    // Encrypted(encrypted::JabberClient<'j>),      Unencrypted(unencrypted::JabberClient<'j>),  }  impl<'j> JabberClientType<'j> { +    /// ensures an encrypted jabber client      pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {          match self {              Self::Encrypted(c) => Ok(c),              Self::Unencrypted(mut c) => { +                c.start_stream().await?;                  let features = c.get_features().await?;                  if features.contains(&StreamFeature::StartTls) {                      Ok(c.starttls().await?) @@ -28,7 +30,7 @@ impl<'j> JabberClientType<'j> {      }  } -// TODO: jabber client trait over both client types +// TODO: jabber client trait over both client types using macro  // #[async_trait]  // pub trait JabberTrait {  //     async fn start_stream(&mut self) -> Result<()>; diff --git a/src/client/unencrypted.rs b/src/client/unencrypted.rs index 27b0a5f..4aa9c63 100644 --- a/src/client/unencrypted.rs +++ b/src/client/unencrypted.rs @@ -1,27 +1,30 @@ +use std::str; +  use quick_xml::{ -    events::{BytesDecl, BytesStart, Event}, +    events::{BytesStart, Event},      name::QName, -    Reader, Writer, +    se, NsReader, Writer,  };  use tokio::io::{BufReader, ReadHalf, WriteHalf};  use tokio::net::TcpStream;  use tokio_native_tls::native_tls::TlsConnector; +use try_map::FallibleMapExt; -use crate::stanza::stream::StreamFeature; -use crate::stanza::Element; +use crate::error::JabberError; +use crate::stanza::stream::Stream; +use crate::stanza::DECLARATION;  use crate::Jabber;  use crate::Result; -use crate::{error::JabberError, stanza::stream::Stream};  pub struct JabberClient<'j> { -    reader: Reader<BufReader<ReadHalf<TcpStream>>>, +    reader: NsReader<BufReader<ReadHalf<TcpStream>>>,      writer: Writer<WriteHalf<TcpStream>>,      jabber: &'j mut Jabber<'j>,  }  impl<'j> JabberClient<'j> {      pub fn new( -        reader: Reader<BufReader<ReadHalf<TcpStream>>>, +        reader: NsReader<BufReader<ReadHalf<TcpStream>>>,          writer: Writer<WriteHalf<TcpStream>>,          jabber: &'j mut Jabber<'j>,      ) -> Self { @@ -34,60 +37,144 @@ impl<'j> JabberClient<'j> {      pub async fn start_stream(&mut self) -> Result<()> {          // client to server -        let declaration = BytesDecl::new("1.0", None, None); + +        // declaration +        self.writer.write_event_async(DECLARATION).await?; + +        // opening stream element          let server = &self.jabber.server.to_owned().try_into()?; -        let stream_element = -            Stream::new_client(&self.jabber.jid, server, None, Some("en".to_string())); -        self.writer -            .write_event_async(Event::Decl(declaration)) -            .await?; -        let stream_element: Element<'_> = stream_element.into(); -        stream_element.write_start(&mut self.writer).await?; -        // server to client -        let mut buf = Vec::new(); -        self.reader.read_event_into_async(&mut buf).await?; -        let _stream_response = Element::read_start(&mut self.reader).await?; -        Ok(()) -    } +        let stream_element = Stream::new_client(None, server, None, "en"); +        se::to_writer_with_root(&mut self.writer, "stream:stream", &stream_element); -    pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> { -        Element::read(&mut self.reader).await?.try_into() -    } +        // server to client -    pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> { -        let mut starttls_element = BytesStart::new("starttls"); -        starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls")); -        self.writer -            .write_event_async(Event::Empty(starttls_element)) -            .await -            .unwrap(); -        let mut buf = Vec::new(); -        match self.reader.read_event_into_async(&mut buf).await.unwrap() { -            Event::Empty(e) => match e.name() { -                QName(b"proceed") => { -                    let connector = TlsConnector::new().unwrap(); -                    let stream = self -                        .reader -                        .into_inner() -                        .into_inner() -                        .unsplit(self.writer.into_inner()); -                    if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector) -                        .connect(&self.jabber.server, stream) -                        .await -                    { -                        let (read, write) = tokio::io::split(tlsstream); -                        let reader = Reader::from_reader(BufReader::new(read)); -                        let writer = Writer::new(write); -                        let mut client = -                            super::encrypted::JabberClient::new(reader, writer, self.jabber); -                        client.start_stream().await?; -                        return Ok(client); +        // may or may not send a declaration +        let buf = Vec::new(); +        let mut first_event = self.reader.read_resolved_event_into_async(&mut buf).await?; +        match first_event { +            (quick_xml::name::ResolveResult::Unbound, Event::Decl(e)) => { +                if let Ok(version) = e.version() { +                    if version.as_ref() == b"1.0" { +                        first_event = self.reader.read_resolved_event_into_async(&mut buf).await? +                    } else { +                        // todo: error +                        todo!()                      } +                } else { +                    first_event = self.reader.read_resolved_event_into_async(&mut buf).await? +                } +            } +            _ => (), +        } + +        // receive stream element and validate +        let stream_response: Stream; +        match first_event { +            (quick_xml::name::ResolveResult::Bound(ns), Event::Start(e)) => { +                if ns.0 == crate::stanza::stream::XMLNS.as_bytes() { +                    // stream_response = Stream::new( +                    //     e.try_get_attribute("from")?.try_map(|attribute| { +                    //         str::from_utf8(attribute.value.as_ref())? +                    //             .try_into()? +                    //             .as_ref() +                    //     })?, +                    //     e.try_get_attribute("to")?.try_map(|attribute| { +                    //         str::from_utf8(attribute.value.as_ref())? +                    //             .try_into()? +                    //             .as_ref() +                    //     })?, +                    //     e.try_get_attribute("id")?.try_map(|attribute| { +                    //         str::from_utf8(attribute.value.as_ref())? +                    //             .try_into()? +                    //             .as_ref() +                    //     })?, +                    //     e.try_get_attribute("version")?.try_map(|attribute| { +                    //         str::from_utf8(attribute.value.as_ref())? +                    //             .try_into()? +                    //             .as_ref() +                    //     })?, +                    //     e.try_get_attribute("lang")?.try_map(|attribute| { +                    //         str::from_utf8(attribute.value.as_ref())? +                    //             .try_into()? +                    //             .as_ref() +                    //     })?, +                    // ); +                    return Ok(()); +                } else { +                    return Err(JabberError::BadStream);                  } -                QName(_) => return Err(JabberError::TlsNegotiation), -            }, -            _ => return Err(JabberError::TlsNegotiation), +            } +            // TODO: errors for incorrect namespace +            (quick_xml::name::ResolveResult::Unbound, Event::Decl(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Start(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::End(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Empty(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Text(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::CData(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Comment(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Decl(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::PI(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::DocType(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unknown(_), Event::Eof) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::Start(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::End(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::Empty(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::Text(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::CData(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::Comment(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::PI(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::DocType(_)) => todo!(), +            (quick_xml::name::ResolveResult::Unbound, Event::Eof) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::End(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::Empty(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::Text(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::CData(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::Comment(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::Decl(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::PI(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::DocType(_)) => todo!(), +            (quick_xml::name::ResolveResult::Bound(_), Event::Eof) => todo!(),          } -        Err(JabberError::TlsNegotiation)      } + +    // pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> { +    //     Element::read(&mut self.reader).await?.try_into() +    // } + +    // pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> { +    //     let mut starttls_element = BytesStart::new("starttls"); +    //     starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls")); +    //     self.writer +    //         .write_event_async(Event::Empty(starttls_element)) +    //         .await +    //         .unwrap(); +    //     let mut buf = Vec::new(); +    //     match self.reader.read_event_into_async(&mut buf).await.unwrap() { +    //         Event::Empty(e) => match e.name() { +    //             QName(b"proceed") => { +    //                 let connector = TlsConnector::new().unwrap(); +    //                 let stream = self +    //                     .reader +    //                     .into_inner() +    //                     .into_inner() +    //                     .unsplit(self.writer.into_inner()); +    //                 if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector) +    //                     .connect(&self.jabber.server, stream) +    //                     .await +    //                 { +    //                     let (read, write) = tokio::io::split(tlsstream); +    //                     let reader = Reader::from_reader(BufReader::new(read)); +    //                     let writer = Writer::new(write); +    //                     let mut client = +    //                         super::encrypted::JabberClient::new(reader, writer, self.jabber); +    //                     client.start_stream().await?; +    //                     return Ok(client); +    //                 } +    //             } +    //             QName(_) => return Err(JabberError::TlsNegotiation), +    //         }, +    //         _ => return Err(JabberError::TlsNegotiation), +    //     } +    //     Err(JabberError::TlsNegotiation) +    // }  } diff --git a/src/jabber.rs b/src/jabber.rs index 1a7eddb..d48eb9c 100644 --- a/src/jabber.rs +++ b/src/jabber.rs @@ -3,7 +3,7 @@ use std::net::{IpAddr, SocketAddr};  use std::str::FromStr;  use std::sync::Arc; -use quick_xml::{Reader, Writer}; +use quick_xml::{NsReader, Writer};  use rsasl::prelude::SASLConfig;  use tokio::io::BufReader;  use tokio::net::TcpStream; @@ -22,7 +22,7 @@ pub struct Jabber<'j> {  }  impl<'j> Jabber<'j> { -    pub fn new(jid: JID, password: String) -> Result<Self> { +    pub fn user(jid: JID, password: String) -> Result<Self> {          let server = jid.domainpart.clone();          let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?;          println!("auth: {:?}", auth); @@ -36,7 +36,7 @@ impl<'j> Jabber<'j> {      pub async fn login(&'j mut self) -> Result<JabberClient<'j>> {          let mut client = self.connect().await?.ensure_tls().await?; -        println!("negotiation"); +        client.start_stream().await?;          client.negotiate().await?;          Ok(client)      } @@ -106,6 +106,7 @@ impl<'j> Jabber<'j> {          socket_addrs      } +    /// establishes a connection to the server      pub async fn connect(&'j mut self) -> Result<JabberClientType> {          for (socket_addr, is_tls) in self.get_sockets().await {              println!("trying {}", socket_addr); @@ -118,21 +119,18 @@ impl<'j> Jabber<'j> {                          .await                      {                          let (read, write) = tokio::io::split(stream); -                        let reader = Reader::from_reader(BufReader::new(read)); +                        let reader = NsReader::from_reader(BufReader::new(read));                          let writer = Writer::new(write); -                        let mut client = client::encrypted::JabberClient::new(reader, writer, self); -                        client.start_stream().await?; +                        let client = client::encrypted::JabberClient::new(reader, writer, self);                          return Ok(JabberClientType::Encrypted(client));                      }                  }                  false => {                      if let Ok(stream) = TcpStream::connect(socket_addr).await {                          let (read, write) = tokio::io::split(stream); -                        let reader = Reader::from_reader(BufReader::new(read)); +                        let reader = NsReader::from_reader(BufReader::new(read));                          let writer = Writer::new(write); -                        let mut client = -                            client::unencrypted::JabberClient::new(reader, writer, self); -                        client.start_stream().await?; +                        let client = client::unencrypted::JabberClient::new(reader, writer, self);                          return Ok(JabberClientType::Unencrypted(client));                      }                  } diff --git a/src/jid/mod.rs b/src/jid.rs index e13fed7..65738dc 100644 --- a/src/jid/mod.rs +++ b/src/jid.rs @@ -1,5 +1,7 @@  use std::str::FromStr; +use serde::Serialize; +  #[derive(PartialEq, Debug, Clone)]  pub struct JID {      // TODO: validate localpart (length, char] @@ -8,6 +10,15 @@ pub struct JID {      pub resourcepart: Option<String>,  } +impl Serialize for JID { +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> +    where +        S: serde::Serializer, +    { +        serializer.serialize_str(&self.to_string()) +    } +} +  pub enum JIDError {      NoResourcePart,      ParseError(ParseError), @@ -97,6 +108,14 @@ impl TryFrom<String> for JID {      }  } +impl TryFrom<&str> for JID { +    type Error = ParseError; + +    fn try_from(value: &str) -> Result<Self, Self::Error> { +        value.parse() +    } +} +  impl std::fmt::Display for JID {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          write!( @@ -8,7 +8,7 @@ pub mod jabber;  pub mod jid;  pub mod stanza; -pub use client::encrypted::JabberClient; +// pub use client::encrypted::JabberClient;  pub use error::JabberError;  pub use jabber::Jabber;  pub use jid::JID; @@ -22,30 +22,9 @@ mod tests {      use crate::Jabber;      use crate::JID; -    // #[tokio::test] -    // async fn get_sockets() { -    //     let jabber = Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned()); -    //     println!("{:?}", jabber.get_sockets().await) -    // } - -    // #[tokio::test] -    // async fn connect() { -    //     Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned()) -    //         .unwrap() -    //         .connect() -    //         .await -    //         .unwrap() -    //         .ensure_tls() -    //         .await -    //         .unwrap() -    //         .start_stream() -    //         .await -    //         .unwrap(); -    // } -      #[tokio::test]      async fn login() { -        Jabber::new( +        Jabber::user(              JID::from_str("test@blos.sm/clown").unwrap(),              "slayed".to_owned(),          ) diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs index 939a716..8b13789 100644 --- a/src/stanza/bind.rs +++ b/src/stanza/bind.rs @@ -1,48 +1 @@ -use super::{Element, ElementParseError}; -use crate::{JabberError, JID}; -const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind"; - -pub struct Bind { -    pub resource: Option<String>, -    pub jid: Option<JID>, -} - -impl From<Bind> for Element { -    fn from(bind: Bind) -> Self { -        let bind_element = Element::new("bind", None, XMLNS); -        bind_element.push_namespace_declaration((None, XMLNS)); -        if let Some(resource) = bind.resource { -            let resource_element = Element::new("resource", None, XMLNS); -            resource_element.push_child(resource); -            bind_element.push_child(resource_element) -        } -        if let Some(jid) = bind.jid { -            let jid_element = Element::new("jid", None, XMLNS); -            jid_element.push_child(jid); -            bind_element.push_child(jid_element) -        } -        bind_element -    } -} - -impl TryFrom<Element> for Bind { -    type Error = JabberError; - -    fn try_from(element: Element) -> Result<Self, Self::Error> { -        if element.namespace() == XMLNS && element.localname() == "bind" { -            let (resource, jid); -            let child: &Element = element.child()?; -            if child.namespace() == XMLNS { -                match child.localname() { -                    "resource" => Bind::new(Some( -                        child -                            .text_content()? -                            .first() -                            .ok_or(ElementParseError::NoContent)?, -                    )), -                } -            } -        } -    } -} diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs index 6c7dee3..8b13789 100644 --- a/src/stanza/iq.rs +++ b/src/stanza/iq.rs @@ -1,170 +1 @@ -use nanoid::nanoid; -use quick_xml::{ -    events::{BytesStart, Event}, -    name::QName, -    Reader, Writer, -}; -use crate::{JabberClient, JabberError, JID}; - -use crate::Result; - -#[derive(Debug)] -pub struct IQ { -    to: Option<JID>, -    from: Option<JID>, -    id: String, -    r#type: IQType, -    lang: Option<String>, -    child: Element<'static>, -} - -#[derive(Debug)] -enum IQType { -    Get, -    Set, -    Result, -    Error, -} - -impl IQ { -    pub async fn set<'j, R: IntoElement<'static>>( -        client: &mut JabberClient<'j>, -        to: Option<JID>, -        from: Option<JID>, -        element: R, -    ) -> Result<Element<'static>> { -        let id = nanoid!(); -        let iq = IQ { -            to, -            from, -            id: id.clone(), -            r#type: IQType::Set, -            lang: None, -            child: Element::from(element), -        }; -        println!("{:?}", iq); -        let iq = Element::from(iq); -        println!("{:?}", iq); -        iq.write(&mut client.writer).await?; -        let result = Element::read(&mut client.reader).await?; -        let iq = IQ::try_from(result)?; -        if iq.id == id { -            return Ok(iq.child); -        } -        Err(JabberError::IDMismatch) -    } -} - -impl<'e> IntoElement<'e> for IQ { -    fn event(&self) -> quick_xml::events::Event<'e> { -        let mut start = BytesStart::new("iq"); -        if let Some(to) = &self.to { -            start.push_attribute(("to", to.to_string().as_str())); -        } -        if let Some(from) = &self.from { -            start.push_attribute(("from", from.to_string().as_str())); -        } -        start.push_attribute(("id", self.id.as_str())); -        match self.r#type { -            IQType::Get => start.push_attribute(("type", "get")), -            IQType::Set => start.push_attribute(("type", "set")), -            IQType::Result => start.push_attribute(("type", "result")), -            IQType::Error => start.push_attribute(("type", "error")), -        } -        if let Some(lang) = &self.lang { -            start.push_attribute(("from", lang.to_string().as_str())); -        } - -        quick_xml::events::Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        Some(vec![self.child.clone()]) -    } -} - -impl TryFrom<Element<'static>> for IQ { -    type Error = JabberError; - -    fn try_from(element: Element<'static>) -> std::result::Result<Self, Self::Error> { -        if let Event::Start(start) = &element.event { -            if start.name() == QName(b"iq") { -                let mut to: Option<JID> = None; -                let mut from: Option<JID> = None; -                let mut id = None; -                let mut r#type = None; -                let mut lang = None; -                start -                    .attributes() -                    .into_iter() -                    .try_for_each(|attribute| -> Result<()> { -                        if let Ok(attribute) = attribute { -                            let buf: Vec<u8> = Vec::new(); -                            let reader = Reader::from_reader(buf); -                            match attribute.key { -                                QName(b"to") => { -                                    to = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned() -                                            .try_into()?, -                                    ) -                                } -                                QName(b"from") => { -                                    from = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned() -                                            .try_into()?, -                                    ) -                                } -                                QName(b"id") => { -                                    id = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned(), -                                    ) -                                } -                                QName(b"type") => { -                                    let value = attribute -                                        .decode_and_unescape_value(&reader) -                                        .or(Err(JabberError::Utf8Decode))?; -                                    match value.as_ref() { -                                        "get" => r#type = Some(IQType::Get), -                                        "set" => r#type = Some(IQType::Set), -                                        "result" => r#type = Some(IQType::Result), -                                        "error" => r#type = Some(IQType::Error), -                                        _ => return Err(JabberError::ParseError), -                                    } -                                } -                                QName(b"lang") => { -                                    lang = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned(), -                                    ) -                                } -                                _ => return Err(JabberError::UnknownAttribute), -                            } -                        } -                        Ok(()) -                    })?; -                let iq = IQ { -                    to, -                    from, -                    id: id.ok_or(JabberError::NoID)?, -                    r#type: r#type.ok_or(JabberError::NoType)?, -                    lang, -                    child: element.child()?.to_owned(), -                }; -                return Ok(iq); -            } -        } -        Err(JabberError::ParseError) -    } -} diff --git a/src/stanza/message.rs b/src/stanza/message.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/message.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 13fc31e..c5a6da3 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -1,657 +1,11 @@ -// use quick_xml::events::BytesDecl; -  pub mod bind;  pub mod iq; +pub mod message; +pub mod presence;  pub mod sasl; +pub mod starttls;  pub mod stream; -use std::collections::BTreeMap; -use std::str; - -// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None); -use async_recursion::async_recursion; -use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; -use quick_xml::name::PrefixDeclaration; -use quick_xml::{Reader, Writer}; -use tokio::io::{AsyncBufRead, AsyncWrite}; - -use crate::{JabberError, Result}; - -#[derive(Clone, Debug)] -/// represents an xml element as a tree of nodes -pub struct Element { -    /// element prefix -    /// e.g. `foo` in `<foo:bar />`. -    prefix: Option<String>, -    /// element name -    /// e.g. `bar` in `<foo:bar />`. -    localname: String, -    /// qualifying namespace -    /// an element must be qualified by a namespace -    /// e.g. for `<stream:features>` in -    /// ``` -    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'> -    ///     <stream:features> -    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> -    ///         <compression xmlns='http://jabber.org/features/compress'> -    ///             <method>zlib</method> -    ///             <method>lzw</method> -    ///         </compression> -    ///     </stream:features> -    /// </stream:stream> -    /// ``` -    /// would be `"http://etherx.jabber.org/streams"` but for -    /// ``` -    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'> -    ///     <features> -    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> -    ///         <compression xmlns='http://jabber.org/features/compress'> -    ///             <method>zlib</method> -    ///             <method>lzw</method> -    ///         </compression> -    ///     </features> -    /// </stream:stream> -    /// ``` -    /// would be `"jabber:client"` -    namespace: String, -    /// all namespaces applied to element -    /// e.g. for `<bind>` in -    /// ``` -    /// <stream:features xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'> -    ///     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> -    ///     <compression xmlns='http://jabber.org/features/compress'> -    ///         <method>zlib</method> -    ///         <method>lzw</method> -    ///     </compression> -    /// </stream:features> -    /// ``` -    /// would be `[(None, "urn:ietf:params:xml:ns:xmpp-bind")]` despite -    /// `(Some("stream"), "http://etherx.jabber.org/streams")` also being available -    // TODO: maybe not even needed, as can calculate when writing which namespaces need to be declared -    // but then can't have unused namespace on element, confusing. -    namespace_declarations: Box<BTreeMap<Option<String>, String>>, -    /// element attributes -    attributes: Box<BTreeMap<String, String>>, -    // children elements namespaces contain their parents' namespaces -    children: Box<Vec<Node>>, -} - -#[derive(Clone, Debug)] -pub enum Node { -    Element(Element), -    Text(String), -    Unknown, -} - -impl From<Element> for Node { -    fn from(element: Element) -> Self { -        Self::Element(element) -    } -} - -impl<S: ToString> From<S> for Node { -    fn from(text: S) -> Self { -        Self::Text(text.to_string()) -    } -} - -impl<'s> From<&Node> for Vec<Event<'s>> { -    fn from(node: &Node) -> Self { -        match node { -            Node::Element(e) => e.into(), -            Node::Text(t) => vec![Event::Text(BytesText::new(t))], -            Unknown => vec![], -        } -    } -} - -impl Element { -    /// returns the fully qualified name -    /// e.g. `foo:bar` in -    /// `<foo:bar>`. -    pub fn name(&self) -> &str { -        if let Some(prefix) = self.prefix { -            format!("{}:{}", prefix, self.localname).as_str() -        } else { -            &self.localname -        } -    } - -    /// returns the localname. -    /// e.g. `bar` in `<foo:bar>` -    pub fn localname(&self) -> &str { -        &self.localname -    } - -    /// returns the prefix. -    /// e.g. `foo` in `<foo:bar>`. returns None if there is -    /// no prefix. -    pub fn prefix(&self) -> Option<&str> { -        self.prefix -    } - -    /// returns the namespace which applies to the current element, e.g. for -    /// `<element xmlns='foo' xmlns:bar='bar'>` -    /// it will be `foo` but for -    /// `<bar:element xmlns='foo' xmlns:bar='bar'>` -    /// it will be `bar`. -    pub fn namespace(&self) -> &str { -        &self.namespace -    } -} - -impl<'s> From<&Element> for Vec<Event<'s>> { -    fn from(element: &Element) -> Self { -        let name = element.name(); - -        let event = BytesStart::new(name); - -        // namespace declarations -        let namespace_declarations = element.namespace_declarations.iter().map(|declaration| { -            let (prefix, namespace) = declaration; -            match prefix { -                Some(prefix) => return (format!("xmlns:{}", prefix).as_str(), *namespace), -                None => return ("xmlns", *namespace), -            } -        }); -        let event = event.with_attributes(namespace_declarations); - -        // attributes -        let event = event.with_attributes(element.attributes.into_iter()); - -        match element.children.is_empty() { -            true => return vec![Event::Empty(event)], -            false => { -                return { -                    let start: Vec<Event<'s>> = vec![Event::Start(event)]; -                    let start_and_content: Vec<Event<'s>> = start -                        .into_iter() -                        .chain({ -                            let u = element.children.iter().fold( -                                Vec::new(), -                                |acc: Vec<Event<'s>>, child: &Node<'s>| { -                                    acc.into_iter() -                                        .chain(Into::<Vec<Event<'s>>>::into(child).into_iter()) -                                        .collect() -                                }, -                            ); -                            u -                        }) -                        .collect(); -                    let full: Vec<Event<'s>> = start_and_content -                        .into_iter() -                        .chain(vec![Event::End(BytesEnd::new(name))]) -                        .collect(); -                    full -                } -            } -        } -    } -} - -impl Element { -    /// if there is only one child in the vec of children, will return that element -    pub fn child(&self) -> Result<&Node> { -        if self.children.len() == 1 { -            Ok(&self.children[0]) -        } else if self.children.len() > 1 { -            Err(ElementError::MultipleChildren.into()) -        } else { -            Err(ElementError::NoChildren.into()) -        } -    } - -    /// returns reference to children -    pub fn children(&self) -> Result<&Vec<Node>> { -        if !self.children.is_empty() { -            Ok(&self.children) -        } else { -            Err(ElementError::NoChildren.into()) -        } -    } - -    /// returns text content, error if there is none -    pub fn text_content(&self) -> Result<Vec<&str>> { -        let mut text = Vec::new(); -        for node in *self.children { -            match node { -                Node::Text(t) => text.push(t), -                _ => {} -            } -        } -        if text.is_empty() { -            return Err(ElementError::NotText.into()); -        } -        Ok(text) -    } - -    /// returns whether or not the element is qualified by a namespace, either declared -    /// by a parent, or itself. -    fn namespace_qualified<S: AsRef<str>>( -        &self, -        namespace_context: &BTreeMap<Option<S>, S>, -    ) -> bool { -        // create a new local_namespaces combining that in the context and those declared within the element -        let mut local_namespaces = *namespace_context.clone(); -        self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration)); - -        if let Some(namespace) = local_namespaces.get(self.prefix) { -            if namespace != self.namespace { -                return false; -            } -        } else { -            return false; -        }; - -        for child in *self.children { -            if child.namespace_qualified(&local_namespaces) == false { -                return false; -            } -        } - -        true -    } - - -    /// writes an element to a writer. the element's namespace must be qualified by the -    /// context given in `local_namespaces` or the element's internal namespace declarations -    pub async fn write<S: AsRef<str>, W: AsyncWrite + Unpin + Send>( -        &self, -        writer: &mut Writer<W>, -        local_namespaces: &BTreeMap<Option<S>, S>, -    ) -> Result<()> { -        // TODO: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s -        if self.namespace_qualified(local_namespaces) { -            let events: Vec<Event> = self.into(); -            for event in events { -                writer.write_event_async(event).await? -            } -            Ok(()) -        } else { -            Err(ElementError::NamespaceNotQualified.into()) -        } -    } - -    pub async fn write_start<S: AsRef<str>, W: AsyncWrite + Unpin + Send>( -        &self, -        writer: &mut Writer<W>, -        local_namespaces: &BTreeMap<Option<S>, S>, -    ) -> Result<()> { -        if self.namespace_qualified(local_namespaces) { -            let mut event = BytesStart::new(self.name()); - -            // namespace declarations -            self.namespace_declarations.iter().for_each(|declaration| { -                let (prefix, namespace) = declaration; -                match prefix { -                    Some(prefix) => { -                        event.push_attribute((format!("xmlns:{}", prefix).as_str(), *namespace)) -                    } -                    None => event.push_attribute(("xmlns", *namespace)), -                } -            }); - -            // attributes -            let event = -                event.with_attributes(self.attributes.iter().map(|(attr, value)| (*attr, *value))); - -            writer.write_event_async(Event::Start(event)).await?; - -            Ok(()) -        } else { -            Err(ElementError::NamespaceNotQualified.into()) -        } -    } - -    pub async fn write_end<W: AsyncWrite + Unpin + Send>( -        &self, -        writer: &mut Writer<W>, -    ) -> Result<()> { -        let event = BytesEnd::new(self.name()); -        writer.write_event_async(Event::End(event)).await?; -        Ok(()) -    } - -    #[async_recursion] -    pub async fn read<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>( -        reader: &mut Reader<R>, -        local_namespaces: &BTreeMap<Option<S>, S>, -    ) -> Result<Self> { -        let node = Node::read_recursive(reader, local_namespaces) -            .await? -            .ok_or(JabberError::UnexpectedEnd)?; -        match node { -            Node::Element(e) => Ok(e), -            Node::Text(_) => Err(JabberError::UnexpectedText), -            Node::Unknown => Err(JabberError::UnexpectedElement), -        } -    } - -    pub async fn read_start<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>( -        reader: &mut Reader<R>, -        local_namespaces: &BTreeMap<Option<S>, S>, -    ) -> Result<Element> { -        let buf = Vec::new(); -        let event = reader.read_event_into_async(&mut buf).await?; -        match event { -            Event::Start(e) => { -                let prefix = e.name().prefix().map(|prefix| prefix.into_inner()); -                let converted_prefix; -                if let Some(raw_prefix) = prefix { -                    converted_prefix = Some(str::from_utf8(raw_prefix)?) -                } -                let prefix = converted_prefix; - -                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned(); - -                let mut local_namespaces = local_namespaces.clone(); -                let mut namespace_declarations = BTreeMap::new(); -                let attributes = BTreeMap::new(); - -                for attribute in e.attributes() { -                    let attribute = attribute?; -                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() { -                        match prefix_declaration { -                            PrefixDeclaration::Default => { -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(None, value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(None, value); -                            } -                            PrefixDeclaration::Named(prefix) => { -                                let key = str::from_utf8(prefix)?; -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(Some(key), value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(Some(key), value); -                            } -                        } -                    } else { -                        if let Some(_) = attributes.insert( -                            str::from_utf8(attribute.key.into_inner())?, -                            str::from_utf8(attribute.value.as_ref())?, -                        ) { -                            return Err(ElementParseError::DuplicateAttribute.into()); -                        }; -                    } -                } - -                let namespace = *local_namespaces -                    .get(&prefix) -                    .ok_or(ElementParseError::NoNamespace)?; - -                let mut children = Vec::new(); - -                Ok(Self { -                    prefix, -                    localname, -                    namespace, -                    namespace_declarations: Box::new(namespace_declarations), -                    attributes: Box::new(attributes), -                    children: Box::new(children), -                }) -            } -            e => Err(ElementError::NotAStart(e).into()), -        } -    } -} - -impl Node { -    #[async_recursion] -    async fn read_recursive<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>( -        reader: &mut Reader<R>, -        local_namespaces: &BTreeMap<Option<S>, S>, -    ) -> Result<Option<Node>> { -        let mut buf = Vec::new(); -        let event = reader.read_event_into_async(&mut buf).await?; -        match event { -            Event::Empty(e) => { -                let prefix = e.name().prefix().map(|prefix| prefix.into_inner()); -                let converted_prefix; -                if let Some(raw_prefix) = prefix { -                    converted_prefix = Some(str::from_utf8(raw_prefix)?) -                } -                let prefix = converted_prefix; - -                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned(); - -                let mut local_namespaces = local_namespaces.clone(); -                let mut namespace_declarations = BTreeMap::new(); -                let attributes = BTreeMap::new(); - -                for attribute in e.attributes() { -                    let attribute = attribute?; -                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() { -                        match prefix_declaration { -                            PrefixDeclaration::Default => { -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(None, value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(None, value); -                            } -                            PrefixDeclaration::Named(prefix) => { -                                let key = str::from_utf8(prefix)?; -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(Some(key), value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(Some(key), value); -                            } -                        } -                    } else { -                        if let Some(_) = attributes.insert( -                            str::from_utf8(attribute.key.into_inner())?, -                            str::from_utf8(attribute.value.as_ref())?, -                        ) { -                            return Err(ElementParseError::DuplicateAttribute.into()); -                        }; -                    } -                } - -                let namespace = *local_namespaces -                    .get(&prefix) -                    .ok_or(ElementParseError::NoNamespace)?; - -                let mut children = Vec::new(); - -                Ok(Some(Self::Element(Element { -                    prefix, -                    localname, -                    namespace, -                    namespace_declarations: Box::new(namespace_declarations), -                    attributes: Box::new(attributes), -                    children: Box::new(children), -                }))) -            } -            Event::Start(e) => { -                let prefix = e.name().prefix().map(|prefix| prefix.into_inner()); -                let converted_prefix; -                if let Some(raw_prefix) = prefix { -                    converted_prefix = Some(str::from_utf8(raw_prefix)?) -                } -                let prefix = converted_prefix; - -                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned(); - -                let mut local_namespaces = local_namespaces.clone(); -                let mut namespace_declarations = BTreeMap::new(); -                let attributes = BTreeMap::new(); - -                for attribute in e.attributes() { -                    let attribute = attribute?; -                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() { -                        match prefix_declaration { -                            PrefixDeclaration::Default => { -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(None, value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(None, value); -                            } -                            PrefixDeclaration::Named(prefix) => { -                                let key = str::from_utf8(prefix)?; -                                let value = str::from_utf8(attribute.value.as_ref())?; -                                if let Some(_) = namespace_declarations.insert(Some(key), value) { -                                    return Err(ElementParseError::DuplicateAttribute.into()); -                                }; -                                local_namespaces.insert(Some(key), value); -                            } -                        } -                    } else { -                        if let Some(_) = attributes.insert( -                            str::from_utf8(attribute.key.into_inner())?, -                            str::from_utf8(attribute.value.as_ref())?, -                        ) { -                            return Err(ElementParseError::DuplicateAttribute.into()); -                        }; -                    } -                } - -                let namespace = *local_namespaces -                    .get(&prefix) -                    .ok_or(ElementParseError::NoNamespace)?; - -                let mut children = Vec::new(); -                while let Some(child_node) = Node::read_recursive(reader, &local_namespaces).await? -                { -                    children.push(child_node) -                } - -                let mut children = Vec::new(); - -                Ok(Some(Self::Element(Element { -                    prefix, -                    localname, -                    namespace, -                    namespace_declarations: Box::new(namespace_declarations), -                    attributes: Box::new(attributes), -                    children: Box::new(children), -                }))) -            } -            Event::End(_) => Ok(None), -            Event::Text(e) => Ok(Some(Self::Text(e.unescape()?.as_ref().to_string()))), -            e => Ok(Some(Self::Unknown)), -        } -    } - -    fn namespace_qualified<S: AsRef<str>>( -        &self, -        namespace_context: &BTreeMap<Option<S>, S>, -    ) -> bool { -        match self { -            Self::Element(e) => e.namespace_qualified(namespace_context), -            _ => true, -        } -    } -} - -pub enum NodeBuilder { -    Text(String), -    Element(ElementBuilder), -} - -pub struct ElementBuilder { -    localname: String, -    prefix: Option<String>, -    namespace: String, -    namespace_declarations: BTreeMap<Option<String>, String>, -    attributes: BTreeMap<String, String>, -    children: Vec<NodeBuilder>, -} - -impl ElementBuilder { -    pub fn new<S: ToString>(localname: S, prefix: Option<S>, namespace: S) -> Self { -        Self { -            prefix, -            localname, -            namespace, -            namespace_declarations: Box::new(BTreeMap::new()), -            attributes: Box::new(BTreeMap::new()), -            children: Box::new(Vec::new()), -        } -    } - -    pub fn push_namespace_declaration<S: ToString>( -        &mut self, -        (prefix, namespace): (Option<S>, S), -    ) -> Option<S> { -        self.namespace_declarations.insert(prefix, namespace) -    } - -    pub fn push_attribute<S: ToString>(&mut self, (key, value): (S, S)) -> Option<S> { -        self.attributes.insert(key, value) -    } - -    pub fn push_child(&mut self, child: Node) { -        self.children.push(child) -    } - -    /// checks if there is a namespace conflict within the element being built -    pub fn namespace_conflict<S: AsRef<str>>( -        &self -    ) -> bool { -        self.namespace_conflict_recursive(&BTreeMap::new()) -    } - -    fn namespace_conflict_recursive<S: AsRef<str>>( -        &self, -        parent_namespaces: &BTreeMap<Option<S>, S>, -    ) -> bool { -        // create a new local_namespaces combining that in the context and those declared within the element -        let mut local_namespaces = *parent_namespaces.clone(); -        self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration)); - -        if let Some(namespace) = local_namespaces.get(self.prefix) { -            if namespace != self.namespace { -                return false; -            } -        } else { -            return false; -        }; - -        for child in *self.children { -            if child.namespace_conflict(&local_namespaces) == false { -                return false; -            } -        } - -        true -    } - -    // check for possible conflicts in namespace -    pub fn build(self) -> Result<Element> { -        for child in self.children { -            match child { -                Node::Element(e) => { -                    if !e.namespace_conflict() -                } -            } -        } -        Element { -            prefix: self.prefix, -            localname: self.localname, -            namespace: self.namespace, -            namespace_declarations: self.namespace_declarations, -            attributes: self.attributes, -            children: self.children, -        } -    } -} - -#[derive(Debug)] -pub enum ElementError<'e> { -    NotAStart(Event<'e>), -    NotText, -    NoChildren, -    NamespaceNotQualified, -    MultipleChildren, -} +use quick_xml::events::{BytesDecl, Event}; -#[derive(Debug)] -pub enum ElementParseError { -    DuplicateAttribute, -    NoNamespace, -} +pub static DECLARATION: Event = Event::Decl(BytesDecl::new("1.0", None, None)); diff --git a/src/stanza/presence.rs b/src/stanza/presence.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/presence.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs index 20cd063..8b13789 100644 --- a/src/stanza/sasl.rs +++ b/src/stanza/sasl.rs @@ -1,145 +1 @@ -use quick_xml::{ -    events::{BytesStart, BytesText, Event}, -    name::QName, -}; -use crate::error::SASLError; -use crate::JabberError; - -use super::Element; - -const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; - -#[derive(Debug)] -pub struct Auth<'e> { -    pub mechanism: &'e str, -    pub sasl_data: &'e str, -} - -impl<'e> IntoElement<'e> for Auth<'e> { -    fn event(&self) -> Event<'e> { -        let mut start = BytesStart::new("auth"); -        start.push_attribute(("xmlns", XMLNS)); -        start.push_attribute(("mechanism", self.mechanism)); -        Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        let sasl = BytesText::from_escaped(self.sasl_data); -        let sasl = Element { -            event: Event::Text(sasl), -            children: None, -        }; -        Some(vec![sasl]) -    } -} - -#[derive(Debug)] -pub struct Challenge { -    pub sasl_data: Vec<u8>, -} - -impl<'e> TryFrom<&Element<'e>> for Challenge { -    type Error = JabberError; - -    fn try_from(element: &Element<'e>) -> Result<Challenge, Self::Error> { -        if let Event::Start(start) = &element.event { -            if start.name() == QName(b"challenge") { -                let sasl_data: &Element<'_> = element.child()?; -                if let Event::Text(sasl_data) = &sasl_data.event { -                    let s = sasl_data.clone(); -                    let s = s.into_inner(); -                    let s = s.to_vec(); -                    return Ok(Challenge { sasl_data: s }); -                } -            } -        } -        Err(SASLError::NoChallenge.into()) -    } -} - -// impl<'e> TryFrom<Element<'e>> for Challenge { -//     type Error = JabberError; - -//     fn try_from(element: Element<'e>) -> Result<Challenge, Self::Error> { -//         if let Event::Start(start) = &element.event { -//             if start.name() == QName(b"challenge") { -//                 println!("one"); -//                 if let Some(children) = element.children.as_deref() { -//                     if children.len() == 1 { -//                         let sasl_data = children.first().unwrap(); -//                         if let Event::Text(sasl_data) = &sasl_data.event { -//                             return Ok(Challenge { -//                                 sasl_data: sasl_data.clone().into_inner().to_vec(), -//                             }); -//                         } else { -//                             return Err(SASLError::NoChallenge.into()); -//                         } -//                     } else { -//                         return Err(SASLError::NoChallenge.into()); -//                     } -//                 } else { -//                     return Err(SASLError::NoChallenge.into()); -//                 } -//             } -//         } -//         Err(SASLError::NoChallenge.into()) -//     } -// } - -#[derive(Debug)] -pub struct Response<'e> { -    pub sasl_data: &'e str, -} - -impl<'e> IntoElement<'e> for Response<'e> { -    fn event(&self) -> Event<'e> { -        let mut start = BytesStart::new("response"); -        start.push_attribute(("xmlns", XMLNS)); -        Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        let sasl = BytesText::from_escaped(self.sasl_data); -        let sasl = Element { -            event: Event::Text(sasl), -            children: None, -        }; -        Some(vec![sasl]) -    } -} - -#[derive(Debug)] -pub struct Success { -    pub sasl_data: Option<Vec<u8>>, -} - -impl<'e> TryFrom<&Element<'e>> for Success { -    type Error = JabberError; - -    fn try_from(element: &Element<'e>) -> Result<Success, Self::Error> { -        match &element.event { -            Event::Start(start) => { -                if start.name() == QName(b"success") { -                    match element.child() { -                        Ok(sasl_data) => { -                            if let Event::Text(sasl_data) = &sasl_data.event { -                                return Ok(Success { -                                    sasl_data: Some(sasl_data.clone().into_inner().to_vec()), -                                }); -                            } -                        } -                        Err(_) => return Ok(Success { sasl_data: None }), -                    }; -                } -            } -            Event::Empty(empty) => { -                if empty.name() == QName(b"success") { -                    return Ok(Success { sasl_data: None }); -                } -            } -            _ => {} -        } -        Err(SASLError::NoSuccess.into()) -    } -} diff --git a/src/stanza/starttls.rs b/src/stanza/starttls.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/starttls.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/stream.rs b/src/stanza/stream.rs index f85166f..07f7e6e 100644 --- a/src/stanza/stream.rs +++ b/src/stanza/stream.rs @@ -1,190 +1,60 @@ -use std::str; - -use quick_xml::{ -    events::{BytesStart, Event}, -    name::QName, -}; - -use super::Element; -use crate::{JabberError, Result, JID}; - -const XMLNS_STREAM: &str = "http://etherx.jabber.org/streams"; -const VERSION: &str = "1.0"; - -enum XMLNS { -    Client, -    Server, -} - -impl From<XMLNS> for &str { -    fn from(xmlns: XMLNS) -> Self { -        match xmlns { -            XMLNS::Client => return "jabber:client", -            XMLNS::Server => return "jabber:server", -        } -    } -} - -impl TryInto<XMLNS> for &str { -    type Error = JabberError; - -    fn try_into(self) -> Result<XMLNS> { -        match self { -            "jabber:client" => Ok(XMLNS::Client), -            "jabber:server" => Ok(XMLNS::Server), -            _ => Err(JabberError::UnknownNamespace), -        } -    } -} - -pub struct Stream { -    from: Option<JID>, -    id: Option<String>, -    to: Option<JID>, -    version: Option<String>, -    lang: Option<String>, -    ns: XMLNS, +use serde::Serialize; + +use crate::JID; + +pub static XMLNS: &str = "http://etherx.jabber.org/streams"; +pub static XMLNS_CLIENT: &str = "jabber:client"; + +// MUST be qualified by stream namespace +#[derive(Serialize)] +pub struct Stream<'s> { +    #[serde(rename = "@from")] +    from: Option<&'s JID>, +    #[serde(rename = "@to")] +    to: Option<&'s JID>, +    #[serde(rename = "@id")] +    id: Option<&'s str>, +    #[serde(rename = "@version")] +    version: Option<&'s str>, +    // TODO: lang enum +    #[serde(rename = "@lang")] +    lang: Option<&'s str>, +    #[serde(rename = "@xmlns")] +    xmlns: &'s str, +    #[serde(rename = "@xmlns:stream")] +    xmlns_stream: &'s str,  }  impl Stream { -    pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self { +    pub fn new( +        from: Option<&JID>, +        to: Option<&JID>, +        id: Option<&str>, +        version: Option<&str>, +        lang: Option<&str>, +    ) -> Self {          Self { -            from: Some(from.clone()), +            from, +            to,              id, -            to: Some(to.clone()), -            version: Some(VERSION.to_owned()), +            version,              lang, -            ns: XMLNS::Client, -        } -    } - -    fn event(&self) -> Event<'static> { -        let mut start = BytesStart::new("stream:stream"); -        if let Some(from) = &self.from { -            start.push_attribute(("from", from.to_string().as_str())); -        } -        if let Some(id) = &self.id { -            start.push_attribute(("id", id.as_str())); -        } -        if let Some(to) = &self.to { -            start.push_attribute(("to", to.to_string().as_str())); -        } -        if let Some(version) = &self.version { -            start.push_attribute(("version", version.to_string().as_str())); -        } -        if let Some(lang) = &self.lang { -            start.push_attribute(("xml:lang", lang.as_str())); -        } -        match &self.ns { -            XMLNS::Client => start.push_attribute(("xmlns", XMLNS::Client.into())), -            XMLNS::Server => start.push_attribute(("xmlns", XMLNS::Server.into())), -        } -        start.push_attribute(("xmlns:stream", XMLNS_STREAM)); -        Event::Start(start) -    } -} - -impl<'e> Into<Element<'e>> for Stream { -    fn into(self) -> Element<'e> { -        Element { -            event: self.event(), -            children: None, +            xmlns: XMLNS_CLIENT, +            xmlns_stream: XMLNS,          }      } -} - -impl<'e> TryFrom<Element<'e>> for Stream { -    type Error = JabberError; -    fn try_from(value: Element<'e>) -> Result<Stream> { -        let (mut from, mut id, mut to, mut version, mut lang, mut ns) = -            (None, None, None, None, None, XMLNS::Client); -        if let Event::Start(e) = value.event.as_ref() { -            for attribute in e.attributes() { -                let attribute = attribute?; -                match attribute.key { -                    QName(b"from") => { -                        from = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?); -                    } -                    QName(b"id") => { -                        id = Some(str::from_utf8(&attribute.value)?.to_owned()); -                    } -                    QName(b"to") => { -                        to = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?); -                    } -                    QName(b"version") => { -                        version = Some(str::from_utf8(&attribute.value)?.to_owned()); -                    } -                    QName(b"lang") => { -                        lang = Some(str::from_utf8(&attribute.value)?.to_owned()); -                    } -                    QName(b"xmlns") => { -                        ns = str::from_utf8(&attribute.value)?.try_into()?; -                    } -                    _ => { -                        println!("unknown attribute: {:?}", attribute) -                    } -                } -            } -            Ok(Stream { -                from, -                id, -                to, -                version, -                lang, -                ns, -            }) -        } else { -            Err(JabberError::ParseError) -        } -    } -} - -#[derive(PartialEq, Debug)] -pub enum StreamFeature { -    StartTls, -    Sasl(Vec<String>), -    Bind, -    Unknown, -} - -impl<'e> TryFrom<Element<'e>> for Vec<StreamFeature> { -    type Error = JabberError; - -    fn try_from(features_element: Element) -> Result<Self> { -        let mut features = Vec::new(); -        if let Some(children) = features_element.children { -            for feature_element in children { -                match feature_element.event { -                    Event::Start(e) => match e.name() { -                        QName(b"starttls") => features.push(StreamFeature::StartTls), -                        QName(b"mechanisms") => { -                            let mut mechanisms = Vec::new(); -                            if let Some(children) = feature_element.children { -                                for mechanism_element in children { -                                    if let Some(children) = mechanism_element.children { -                                        for mechanism_text in children { -                                            match mechanism_text.event { -                                                Event::Text(e) => mechanisms -                                                    .push(str::from_utf8(e.as_ref())?.to_owned()), -                                                _ => {} -                                            } -                                        } -                                    } -                                } -                            } -                            features.push(StreamFeature::Sasl(mechanisms)) -                        } -                        _ => features.push(StreamFeature::Unknown), -                    }, -                    Event::Empty(e) => match e.name() { -                        QName(b"bind") => features.push(StreamFeature::Bind), -                        _ => features.push(StreamFeature::Unknown), -                    }, -                    _ => features.push(StreamFeature::Unknown), -                } -            } +    /// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute. +    /// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection. +    pub fn new_client(from: Option<&JID>, to: &JID, id: Option<&str>, lang: &str) -> Self { +        Self { +            from, +            to: Some(to), +            id, +            version: Some("1.0"), +            lang: Some(lang), +            xmlns: XMLNS_CLIENT, +            xmlns_stream: XMLNS,          } -        Ok(features)      }  } | 
