diff options
Diffstat (limited to '')
| -rw-r--r-- | src/client.rs | 180 | ||||
| -rw-r--r-- | src/connection.rs | 91 | ||||
| -rw-r--r-- | src/error.rs | 17 | ||||
| -rw-r--r-- | src/jabber.rs | 377 | ||||
| -rw-r--r-- | src/lib.rs | 18 | 
5 files changed, 400 insertions, 283 deletions
diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..2908346 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,180 @@ +use std::sync::Arc; + +use futures::{Sink, Stream}; +use rsasl::config::SASLConfig; + +use crate::{ +    connection::{Tls, Unencrypted}, +    stanza::{ +        client::Stanza, +        sasl::Mechanisms, +        stream::{Feature, Features}, +    }, +    Connection, Error, JabberStream, Result, JID, +}; + +// feed it client stanzas, receive client stanzas +pub struct JabberClient { +    connection: JabberState, +    jid: JID, +    password: Arc<SASLConfig>, +    server: String, +} + +pub enum JabberState { +    Disconnected, +    InsecureConnectionEstablised(Unencrypted), +    InsecureStreamStarted(JabberStream<Unencrypted>), +    InsecureGotFeatures((Features, JabberStream<Unencrypted>)), +    StartTls(JabberStream<Unencrypted>), +    ConnectionEstablished(Tls), +    StreamStarted(JabberStream<Tls>), +    GotFeatures((Features, JabberStream<Tls>)), +    Sasl(Mechanisms, JabberStream<Tls>), +    Bind(JabberStream<Tls>), +    // when it's bound, can stream stanzas and sink stanzas +    Bound(JabberStream<Tls>), +} + +impl JabberState { +    pub async fn advance_state( +        self, +        jid: &mut JID, +        auth: Arc<SASLConfig>, +        server: &mut String, +    ) -> Result<JabberState> { +        match self { +            JabberState::Disconnected => match Connection::connect(server).await? { +                Connection::Encrypted(tls_stream) => { +                    Ok(JabberState::ConnectionEstablished(tls_stream)) +                } +                Connection::Unencrypted(tcp_stream) => { +                    Ok(JabberState::InsecureConnectionEstablised(tcp_stream)) +                } +            }, +            JabberState::InsecureConnectionEstablised(tcp_stream) => Ok({ +                JabberState::InsecureStreamStarted( +                    JabberStream::start_stream(tcp_stream, server).await?, +                ) +            }), +            JabberState::InsecureStreamStarted(jabber_stream) => Ok( +                JabberState::InsecureGotFeatures(jabber_stream.get_features().await?), +            ), +            JabberState::InsecureGotFeatures((features, jabber_stream)) => { +                match features.negotiate()? { +                    Feature::StartTls(_start_tls) => Ok(JabberState::StartTls(jabber_stream)), +                    // TODO: better error +                    _ => return Err(Error::TlsRequired), +                } +            } +            JabberState::StartTls(jabber_stream) => Ok(JabberState::ConnectionEstablished( +                jabber_stream.starttls(server).await?, +            )), +            JabberState::ConnectionEstablished(tls_stream) => Ok(JabberState::StreamStarted( +                JabberStream::start_stream(tls_stream, server).await?, +            )), +            JabberState::StreamStarted(jabber_stream) => Ok(JabberState::GotFeatures( +                jabber_stream.get_features().await?, +            )), +            JabberState::GotFeatures((features, jabber_stream)) => match features.negotiate()? { +                Feature::StartTls(_start_tls) => return Err(Error::AlreadyTls), +                Feature::Sasl(mechanisms) => { +                    return Ok(JabberState::Sasl(mechanisms, jabber_stream)) +                } +                Feature::Bind => return Ok(JabberState::Bind(jabber_stream)), +                Feature::Unknown => return Err(Error::Unsupported), +            }, +            JabberState::Sasl(mechanisms, jabber_stream) => { +                return Ok(JabberState::ConnectionEstablished( +                    jabber_stream.sasl(mechanisms, auth).await?, +                )) +            } +            JabberState::Bind(jabber_stream) => { +                Ok(JabberState::Bound(jabber_stream.bind(jid).await?)) +            } +            JabberState::Bound(jabber_stream) => Ok(JabberState::Bound(jabber_stream)), +        } +    } +} + +impl Features { +    pub fn negotiate(self) -> Result<Feature> { +        if let Some(Feature::StartTls(s)) = self +            .features +            .iter() +            .find(|feature| matches!(feature, Feature::StartTls(_s))) +        { +            // TODO: avoid clone +            return Ok(Feature::StartTls(s.clone())); +        } else if let Some(Feature::Sasl(mechanisms)) = self +            .features +            .iter() +            .find(|feature| matches!(feature, Feature::Sasl(_))) +        { +            // TODO: avoid clone +            return Ok(Feature::Sasl(mechanisms.clone())); +        } else if let Some(Feature::Bind) = self +            .features +            .into_iter() +            .find(|feature| matches!(feature, Feature::Bind)) +        { +            Ok(Feature::Bind) +        } else { +            // TODO: better error +            return Err(Error::Negotiation); +        } +    } +} + +pub enum InsecureJabberConnection { +    Disconnected, +    ConnectionEstablished(Connection), +    PreStarttls(JabberStream<Unencrypted>), +    PreAuthenticated(JabberStream<Tls>), +    Authenticated(Tls), +    PreBound(JabberStream<Tls>), +    Bound(JabberStream<Tls>), +} + +impl Stream for JabberClient { +    type Item = Stanza; + +    fn poll_next( +        self: std::pin::Pin<&mut Self>, +        cx: &mut std::task::Context<'_>, +    ) -> std::task::Poll<Option<Self::Item>> { +        todo!() +    } +} + +impl Sink<Stanza> for JabberClient { +    type Error = Error; + +    fn poll_ready( +        self: std::pin::Pin<&mut Self>, +        cx: &mut std::task::Context<'_>, +    ) -> std::task::Poll<std::result::Result<(), Self::Error>> { +        todo!() +    } + +    fn start_send( +        self: std::pin::Pin<&mut Self>, +        item: Stanza, +    ) -> std::result::Result<(), Self::Error> { +        todo!() +    } + +    fn poll_flush( +        self: std::pin::Pin<&mut Self>, +        cx: &mut std::task::Context<'_>, +    ) -> std::task::Poll<std::result::Result<(), Self::Error>> { +        todo!() +    } + +    fn poll_close( +        self: std::pin::Pin<&mut Self>, +        cx: &mut std::task::Context<'_>, +    ) -> std::task::Poll<std::result::Result<(), Self::Error>> { +        todo!() +    } +} diff --git a/src/connection.rs b/src/connection.rs index 9e485d3..bc5a282 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -10,7 +10,6 @@ use tokio_native_tls::native_tls::TlsConnector;  use tokio_native_tls::TlsStream;  use tracing::{debug, info, instrument, trace}; -use crate::Jabber;  use crate::Result;  use crate::{Error, JID}; @@ -19,69 +18,51 @@ pub type Unencrypted = TcpStream;  #[derive(Debug)]  pub enum Connection { -    Encrypted(Jabber<Tls>), -    Unencrypted(Jabber<Unencrypted>), +    Encrypted(Tls), +    Unencrypted(Unencrypted),  }  impl Connection { -    #[instrument] +    // #[instrument]      /// stream not started -    pub async fn ensure_tls(self) -> Result<Jabber<Tls>> { -        match self { -            Connection::Encrypted(j) => Ok(j), -            Connection::Unencrypted(mut j) => { -                j.start_stream().await?; -                info!("upgrading connection to tls"); -                j.get_features().await?; -                let j = j.starttls().await?; -                Ok(j) -            } -        } -    } - -    pub async fn connect_user(jid: impl AsRef<str>, password: String) -> Result<Self> { +    // pub async fn ensure_tls(self) -> Result<Jabber<Tls>> { +    //     match self { +    //         Connection::Encrypted(j) => Ok(j), +    //         Connection::Unencrypted(mut j) => { +    //             j.start_stream().await?; +    //             info!("upgrading connection to tls"); +    //             j.get_features().await?; +    //             let j = j.starttls().await?; +    //             Ok(j) +    //         } +    //     } +    // } + +    pub async fn connect_user(jid: impl AsRef<str>) -> Result<Self> {          let jid: JID = JID::from_str(jid.as_ref())?;          let server = jid.domainpart.clone(); -        let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?; -        println!("auth: {:?}", auth); -        Self::connect(&server, Some(jid), Some(auth)).await +        Self::connect(&server).await      }      #[instrument] -    pub async fn connect( -        server: &str, -        jid: Option<JID>, -        auth: Option<Arc<SASLConfig>>, -    ) -> Result<Self> { -        info!("connecting to {}", server); -        let sockets = Self::get_sockets(&server).await; +    pub async fn connect(server: impl AsRef<str> + std::fmt::Debug) -> Result<Self> { +        info!("connecting to {}", server.as_ref()); +        let sockets = Self::get_sockets(server.as_ref()).await;          debug!("discovered sockets: {:?}", sockets);          for (socket_addr, tls) in sockets {              match tls {                  true => { -                    if let Ok(connection) = Self::connect_tls(socket_addr, &server).await { +                    if let Ok(connection) = Self::connect_tls(socket_addr, server.as_ref()).await {                          info!("connected via encrypted stream to {}", socket_addr); -                        let (readhalf, writehalf) = tokio::io::split(connection); -                        return Ok(Self::Encrypted(Jabber::new( -                            readhalf, -                            writehalf, -                            jid, -                            auth, -                            server.to_owned(), -                        ))); +                        // let (readhalf, writehalf) = tokio::io::split(connection); +                        return Ok(Self::Encrypted(connection));                      }                  }                  false => {                      if let Ok(connection) = Self::connect_unencrypted(socket_addr).await {                          info!("connected via unencrypted stream to {}", socket_addr); -                        let (readhalf, writehalf) = tokio::io::split(connection); -                        return Ok(Self::Unencrypted(Jabber::new( -                            readhalf, -                            writehalf, -                            jid, -                            auth, -                            server.to_owned(), -                        ))); +                        // let (readhalf, writehalf) = tokio::io::split(connection); +                        return Ok(Self::Unencrypted(connection));                      }                  }              } @@ -188,16 +169,16 @@ mod tests {      #[test(tokio::test)]      async fn connect() { -        Connection::connect("blos.sm", None, None).await.unwrap(); +        Connection::connect("blos.sm").await.unwrap();      } -    #[test(tokio::test)] -    async fn test_tls() { -        Connection::connect("blos.sm", None, None) -            .await -            .unwrap() -            .ensure_tls() -            .await -            .unwrap(); -    } +    // #[test(tokio::test)] +    // async fn test_tls() { +    //     Connection::connect("blos.sm", None, None) +    //         .await +    //         .unwrap() +    //         .ensure_tls() +    //         .await +    //         .unwrap(); +    // }  } diff --git a/src/error.rs b/src/error.rs index b5cf446..8cb6496 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,23 +8,12 @@ use crate::{jid::ParseError, stanza::sasl::Failure};  #[derive(Debug)]  pub enum Error {      Connection, -    BadStream, -    StartTlsUnavailable, -    TlsNegotiation,      Utf8Decode, -    NoFeatures, -    UnknownNamespace, -    UnknownAttribute, -    NoID, -    NoType, -    IDMismatch, -    BindError, -    ParseError,      Negotiation,      TlsRequired, -    UnexpectedEnd, +    AlreadyTls, +    Unsupported,      UnexpectedElement(peanuts::Element), -    UnexpectedText,      XML(peanuts::Error),      SASL(SASLError),      JID(ParseError), @@ -37,8 +26,6 @@ pub enum Error {  pub enum SASLError {      SASL(rsasl::prelude::SASLError),      MechanismName(MechanismNameError), -    NoChallenge, -    NoSuccess,  }  impl From<rsasl::prelude::SASLError> for Error { diff --git a/src/jabber.rs b/src/jabber.rs index d5cfe13..cf90f73 100644 --- a/src/jabber.rs +++ b/src/jabber.rs @@ -1,4 +1,4 @@ -use std::str; +use std::str::{self, FromStr};  use std::sync::Arc;  use async_recursion::async_recursion; @@ -20,47 +20,18 @@ use crate::stanza::XML_VERSION;  use crate::JID;  use crate::{Connection, Result}; -pub struct Jabber<S> { +// open stream (streams started) +pub struct JabberStream<S> {      reader: Reader<ReadHalf<S>>,      writer: Writer<WriteHalf<S>>, -    jid: Option<JID>, -    auth: Option<Arc<SASLConfig>>, -    server: String,  } -impl<S> Jabber<S> +impl<S> JabberStream<S>  where -    S: AsyncRead + AsyncWrite + Unpin, +    S: AsyncRead + AsyncWrite + Unpin + Send + std::fmt::Debug, +    JabberStream<S>: std::fmt::Debug,  { -    pub fn new( -        reader: ReadHalf<S>, -        writer: WriteHalf<S>, -        jid: Option<JID>, -        auth: Option<Arc<SASLConfig>>, -        server: String, -    ) -> Self { -        let reader = Reader::new(reader); -        let writer = Writer::new(writer); -        Self { -            reader, -            writer, -            jid, -            auth, -            server, -        } -    } -} - -impl<S> Jabber<S> -where -    S: AsyncRead + AsyncWrite + Unpin + Send, -    Jabber<S>: std::fmt::Debug, -{ -    pub async fn sasl( -        &mut self, -        mechanisms: Mechanisms, -        sasl_config: Arc<SASLConfig>, -    ) -> Result<()> { +    pub async fn sasl(mut self, mechanisms: Mechanisms, sasl_config: Arc<SASLConfig>) -> Result<S> {          let sasl = SASLClient::new(sasl_config);          let mut offered_mechs: Vec<&Mechname> = Vec::new();          for mechanism in &mechanisms.mechanisms { @@ -143,12 +114,15 @@ where                  }              }          } -        Ok(()) +        let writer = self.writer.into_inner(); +        let reader = self.reader.into_inner(); +        let stream = reader.unsplit(writer); +        Ok(stream)      } -    pub async fn bind(&mut self) -> Result<()> { +    pub async fn bind(mut self, jid: &mut JID) -> Result<Self> {          let iq_id = nanoid::nanoid!(); -        if let Some(resource) = self.jid.clone().unwrap().resourcepart { +        if let Some(resource) = &jid.resourcepart {              let iq = Iq {                  from: None,                  id: iq_id.clone(), @@ -156,7 +130,7 @@ where                  r#type: IqType::Set,                  lang: None,                  query: Some(Query::Bind(Bind { -                    r#type: Some(BindType::Resource(ResourceType(resource))), +                    r#type: Some(BindType::Resource(ResourceType(resource.to_string()))),                  })),                  errors: Vec::new(),              }; @@ -171,12 +145,12 @@ where                      lang: _,                      query:                          Some(Query::Bind(Bind { -                            r#type: Some(BindType::Jid(FullJidType(jid))), +                            r#type: Some(BindType::Jid(FullJidType(new_jid))),                          })),                      errors: _,                  } if id == iq_id => { -                    self.jid = Some(jid); -                    return Ok(()); +                    *jid = new_jid; +                    return Ok(self);                  }                  Iq {                      from: _, @@ -214,12 +188,12 @@ where                      lang: _,                      query:                          Some(Query::Bind(Bind { -                            r#type: Some(BindType::Jid(FullJidType(jid))), +                            r#type: Some(BindType::Jid(FullJidType(new_jid))),                          })),                      errors: _,                  } if id == iq_id => { -                    self.jid = Some(jid); -                    return Ok(()); +                    *jid = new_jid; +                    return Ok(self);                  }                  Iq {                      from: _, @@ -240,39 +214,44 @@ where      }      #[instrument] -    pub async fn start_stream(&mut self) -> Result<()> { +    pub async fn start_stream(connection: S, server: &mut String) -> Result<Self> {          // client to server +        let (reader, writer) = tokio::io::split(connection); +        let mut reader = Reader::new(reader); +        let mut writer = Writer::new(writer);          // declaration -        self.writer.write_declaration(XML_VERSION).await?; +        writer.write_declaration(XML_VERSION).await?;          // opening stream element -        let server = self.server.clone().try_into()?; -        let stream = Stream::new_client(None, server, None, "en".to_string()); -        self.writer.write_start(&stream).await?; +        let stream = Stream::new_client( +            None, +            JID::from_str(server.as_ref())?, +            None, +            "en".to_string(), +        ); +        writer.write_start(&stream).await?;          // server to client          // may or may not send a declaration -        let _decl = self.reader.read_prolog().await?; +        let _decl = reader.read_prolog().await?;          // receive stream element and validate -        let text = str::from_utf8(self.reader.buffer.data()).unwrap(); -        debug!("data: {}", text); -        let stream: Stream = self.reader.read_start().await?; +        let stream: Stream = reader.read_start().await?;          debug!("got stream: {:?}", stream);          if let Some(from) = stream.from { -            self.server = from.to_string() +            *server = from.to_string();          } -        Ok(()) +        Ok(Self { reader, writer })      } -    pub async fn get_features(&mut self) -> Result<Features> { +    pub async fn get_features(mut self) -> Result<(Features, Self)> {          debug!("getting features");          let features: Features = self.reader.read().await?;          debug!("got features: {:?}", features); -        Ok(features) +        Ok((features, self))      }      pub fn into_inner(self) -> S { @@ -280,89 +259,89 @@ where      }  } -impl Jabber<Unencrypted> { -    pub async fn negotiate<S: AsyncRead + AsyncWrite + Unpin>(mut self) -> Result<Jabber<Tls>> { -        self.start_stream().await?; -        // TODO: timeout -        let features = self.get_features().await?.features; -        if let Some(Feature::StartTls(_)) = features -            .iter() -            .find(|feature| matches!(feature, Feature::StartTls(_s))) -        { -            let jabber = self.starttls().await?; -            let jabber = jabber.negotiate().await?; -            return Ok(jabber); -        } else { -            // TODO: better error -            return Err(Error::TlsRequired); -        } -    } - -    #[async_recursion] -    pub async fn negotiate_tls_optional(mut self) -> Result<Connection> { -        self.start_stream().await?; -        // TODO: timeout -        let features = self.get_features().await?.features; -        if let Some(Feature::StartTls(_)) = features -            .iter() -            .find(|feature| matches!(feature, Feature::StartTls(_s))) -        { -            let jabber = self.starttls().await?; -            let jabber = jabber.negotiate().await?; -            return Ok(Connection::Encrypted(jabber)); -        } else if let (Some(sasl_config), Some(Feature::Sasl(mechanisms))) = ( -            self.auth.clone(), -            features -                .iter() -                .find(|feature| matches!(feature, Feature::Sasl(_))), -        ) { -            self.sasl(mechanisms.clone(), sasl_config).await?; -            let jabber = self.negotiate_tls_optional().await?; -            Ok(jabber) -        } else if let Some(Feature::Bind) = features -            .iter() -            .find(|feature| matches!(feature, Feature::Bind)) -        { -            self.bind().await?; -            Ok(Connection::Unencrypted(self)) -        } else { -            // TODO: better error -            return Err(Error::Negotiation); -        } -    } +impl JabberStream<Unencrypted> { +    // pub async fn negotiate<S: AsyncRead + AsyncWrite + Unpin>( +    //     mut self, +    //     features: Features, +    // ) -> Result<Feature> { +    //     // TODO: timeout +    //     if let Some(Feature::StartTls(_)) = features +    //         .features +    //         .iter() +    //         .find(|feature| matches!(feature, Feature::StartTls(_s))) +    //     { +    //         return Ok(self); +    //     } else { +    //         // TODO: better error +    //         return Err(Error::TlsRequired); +    //     } +    // } + +    // #[async_recursion] +    // pub async fn negotiate_tls_optional(mut self) -> Result<Connection> { +    //     self.start_stream().await?; +    //     // TODO: timeout +    //     let features = self.get_features().await?.features; +    //     if let Some(Feature::StartTls(_)) = features +    //         .iter() +    //         .find(|feature| matches!(feature, Feature::StartTls(_s))) +    //     { +    //         let jabber = self.starttls().await?; +    //         let jabber = jabber.negotiate().await?; +    //         return Ok(Connection::Encrypted(jabber)); +    //     } else if let (Some(sasl_config), Some(Feature::Sasl(mechanisms))) = ( +    //         self.auth.clone(), +    //         features +    //             .iter() +    //             .find(|feature| matches!(feature, Feature::Sasl(_))), +    //     ) { +    //         self.sasl(mechanisms.clone(), sasl_config).await?; +    //         let jabber = self.negotiate_tls_optional().await?; +    //         Ok(jabber) +    //     } else if let Some(Feature::Bind) = features +    //         .iter() +    //         .find(|feature| matches!(feature, Feature::Bind)) +    //     { +    //         self.bind().await?; +    //         Ok(Connection::Unencrypted(self)) +    //     } else { +    //         // TODO: better error +    //         return Err(Error::Negotiation); +    //     } +    // }  } -impl Jabber<Tls> { -    #[async_recursion] -    pub async fn negotiate(mut self) -> Result<Jabber<Tls>> { -        self.start_stream().await?; -        let features = self.get_features().await?.features; - -        if let (Some(sasl_config), Some(Feature::Sasl(mechanisms))) = ( -            self.auth.clone(), -            features -                .iter() -                .find(|feature| matches!(feature, Feature::Sasl(_))), -        ) { -            // TODO: avoid clone -            self.sasl(mechanisms.clone(), sasl_config).await?; -            let jabber = self.negotiate().await?; -            Ok(jabber) -        } else if let Some(Feature::Bind) = features -            .iter() -            .find(|feature| matches!(feature, Feature::Bind)) -        { -            self.bind().await?; -            Ok(self) -        } else { -            // TODO: better error -            return Err(Error::Negotiation); -        } -    } +impl JabberStream<Tls> { +    // #[async_recursion] +    // pub async fn negotiate(mut self) -> Result<JabberStream<Tls>> { +    //     self.start_stream().await?; +    //     let features = self.get_features().await?.features; + +    //     if let (Some(sasl_config), Some(Feature::Sasl(mechanisms))) = ( +    //         self.auth.clone(), +    //         features +    //             .iter() +    //             .find(|feature| matches!(feature, Feature::Sasl(_))), +    //     ) { +    //         // TODO: avoid clone +    //         self.sasl(mechanisms.clone(), sasl_config).await?; +    //         let jabber = self.negotiate().await?; +    //         Ok(jabber) +    //     } else if let Some(Feature::Bind) = features +    //         .iter() +    //         .find(|feature| matches!(feature, Feature::Bind)) +    //     { +    //         self.bind().await?; +    //         Ok(self) +    //     } else { +    //         // TODO: better error +    //         return Err(Error::Negotiation); +    //     } +    // }  } -impl Jabber<Unencrypted> { -    pub async fn starttls(mut self) -> Result<Jabber<Tls>> { +impl JabberStream<Unencrypted> { +    pub async fn starttls(mut self, domain: impl AsRef<str>) -> Result<Tls> {          self.writer              .write_full(&StartTls { required: false })              .await?; @@ -370,43 +349,31 @@ impl Jabber<Unencrypted> {          debug!("got proceed: {:?}", proceed);          let connector = TlsConnector::new().unwrap();          let stream = self.reader.into_inner().unsplit(self.writer.into_inner()); -        if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector) -            .connect(&self.server, stream) +        if let Ok(tls_stream) = tokio_native_tls::TlsConnector::from(connector) +            .connect(domain.as_ref(), stream)              .await          { -            let (read, write) = tokio::io::split(tlsstream); -            let client = Jabber::new( -                read, -                write, -                self.jid.to_owned(), -                self.auth.to_owned(), -                self.server.to_owned(), -            ); -            return Ok(client); +            // let (read, write) = tokio::io::split(tlsstream); +            // let client = JabberStream::new(read, write); +            return Ok(tls_stream);          } else {              return Err(Error::Connection);          }      }  } -impl std::fmt::Debug for Jabber<Tls> { +impl std::fmt::Debug for JabberStream<Tls> {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          f.debug_struct("Jabber")              .field("connection", &"tls") -            .field("jid", &self.jid) -            .field("auth", &self.auth) -            .field("server", &self.server)              .finish()      }  } -impl std::fmt::Debug for Jabber<Unencrypted> { +impl std::fmt::Debug for JabberStream<Unencrypted> {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          f.debug_struct("Jabber")              .field("connection", &"unencrypted") -            .field("jid", &self.jid) -            .field("auth", &self.auth) -            .field("server", &self.server)              .finish()      }  } @@ -422,61 +389,61 @@ mod tests {      #[test(tokio::test)]      async fn start_stream() { -        let connection = Connection::connect("blos.sm", None, None).await.unwrap(); -        match connection { -            Connection::Encrypted(mut c) => c.start_stream().await.unwrap(), -            Connection::Unencrypted(mut c) => c.start_stream().await.unwrap(), -        } +        // let connection = Connection::connect("blos.sm", None, None).await.unwrap(); +        // match connection { +        //     Connection::Encrypted(mut c) => c.start_stream().await.unwrap(), +        //     Connection::Unencrypted(mut c) => c.start_stream().await.unwrap(), +        // }      }      #[test(tokio::test)]      async fn sasl() { -        let mut jabber = Connection::connect_user("test@blos.sm", "slayed".to_string()) -            .await -            .unwrap() -            .ensure_tls() -            .await -            .unwrap(); -        let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); -        println!("data: {}", text); -        jabber.start_stream().await.unwrap(); - -        let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); -        println!("data: {}", text); -        jabber.reader.read_buf().await.unwrap(); -        let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); -        println!("data: {}", text); - -        let features = jabber.get_features().await.unwrap(); -        let (sasl_config, feature) = ( -            jabber.auth.clone().unwrap(), -            features -                .features -                .iter() -                .find(|feature| matches!(feature, Feature::Sasl(_))) -                .unwrap(), -        ); -        match feature { -            Feature::StartTls(_start_tls) => todo!(), -            Feature::Sasl(mechanisms) => { -                jabber.sasl(mechanisms.clone(), sasl_config).await.unwrap(); -            } -            Feature::Bind => todo!(), -            Feature::Unknown => todo!(), -        } +        // let mut jabber = Connection::connect_user("test@blos.sm", "slayed".to_string()) +        //     .await +        //     .unwrap() +        //     .ensure_tls() +        //     .await +        //     .unwrap(); +        // let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); +        // println!("data: {}", text); +        // jabber.start_stream().await.unwrap(); + +        // let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); +        // println!("data: {}", text); +        // jabber.reader.read_buf().await.unwrap(); +        // let text = str::from_utf8(jabber.reader.buffer.data()).unwrap(); +        // println!("data: {}", text); + +        // let features = jabber.get_features().await.unwrap(); +        // let (sasl_config, feature) = ( +        //     jabber.auth.clone().unwrap(), +        //     features +        //         .features +        //         .iter() +        //         .find(|feature| matches!(feature, Feature::Sasl(_))) +        //         .unwrap(), +        // ); +        // match feature { +        //     Feature::StartTls(_start_tls) => todo!(), +        //     Feature::Sasl(mechanisms) => { +        //         jabber.sasl(mechanisms.clone(), sasl_config).await.unwrap(); +        //     } +        //     Feature::Bind => todo!(), +        //     Feature::Unknown => todo!(), +        // }      }      #[tokio::test]      async fn negotiate() { -        let _jabber = Connection::connect_user("test@blos.sm", "slayed".to_string()) -            .await -            .unwrap() -            .ensure_tls() -            .await -            .unwrap() -            .negotiate() -            .await -            .unwrap(); -        sleep(Duration::from_secs(5)).await +        // let _jabber = Connection::connect_user("test@blos.sm", "slayed".to_string()) +        //     .await +        //     .unwrap() +        //     .ensure_tls() +        //     .await +        //     .unwrap() +        //     .negotiate() +        //     .await +        //     .unwrap(); +        // sleep(Duration::from_secs(5)).await      }  } @@ -2,6 +2,7 @@  // #![feature(let_chains)]  // TODO: logging (dropped errors) +pub mod client;  pub mod connection;  pub mod error;  pub mod jabber; @@ -11,18 +12,19 @@ pub mod stanza;  pub use connection::Connection;  use connection::Tls;  pub use error::Error; -pub use jabber::Jabber; +pub use jabber::JabberStream;  pub use jid::JID;  pub type Result<T> = std::result::Result<T, Error>; -pub async fn login<J: AsRef<str>, P: AsRef<str>>(jid: J, password: P) -> Result<Jabber<Tls>> { -    Ok(Connection::connect_user(jid, password.as_ref().to_string()) -        .await? -        .ensure_tls() -        .await? -        .negotiate() -        .await?) +pub async fn login<J: AsRef<str>, P: AsRef<str>>(jid: J, password: P) -> Result<JabberStream<Tls>> { +    todo!() +    // Ok(Connection::connect_user(jid, password.as_ref().to_string()) +    //     .await? +    //     .ensure_tls() +    //     .await? +    //     .negotiate() +    //     .await?)  }  #[cfg(test)]  | 
