From e0373c0520e7fae792bc907e9c500ab846d34e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?cel=20=F0=9F=8C=B8?= Date: Tue, 3 Dec 2024 23:57:04 +0000 Subject: [PATCH] WIP: connecting fsm --- Cargo.toml | 1 + src/client.rs | 180 ++++++++++++++++++++++ src/connection.rs | 89 +++++------ src/error.rs | 17 +-- src/jabber.rs | 369 +++++++++++++++++++++------------------------- src/lib.rs | 18 ++- 6 files changed, 396 insertions(+), 278 deletions(-) create mode 100644 src/client.rs diff --git a/Cargo.toml b/Cargo.toml index e9c12b5..687b980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ tracing = "0.1.40" trust-dns-resolver = "0.22.0" try_map = "0.3.1" peanuts = { version = "0.1.0", path = "../peanuts" } +futures = "0.3.31" [dev-dependencies] test-log = { version = "0.2", features = ["trace"] } 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, + server: String, +} + +pub enum JabberState { + Disconnected, + InsecureConnectionEstablised(Unencrypted), + InsecureStreamStarted(JabberStream), + InsecureGotFeatures((Features, JabberStream)), + StartTls(JabberStream), + ConnectionEstablished(Tls), + StreamStarted(JabberStream), + GotFeatures((Features, JabberStream)), + Sasl(Mechanisms, JabberStream), + Bind(JabberStream), + // when it's bound, can stream stanzas and sink stanzas + Bound(JabberStream), +} + +impl JabberState { + pub async fn advance_state( + self, + jid: &mut JID, + auth: Arc, + server: &mut String, + ) -> Result { + 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 { + 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), + PreAuthenticated(JabberStream), + Authenticated(Tls), + PreBound(JabberStream), + Bound(JabberStream), +} + +impl Stream for JabberClient { + type Item = Stanza; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + todo!() + } +} + +impl Sink for JabberClient { + type Error = Error; + + fn poll_ready( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + 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> { + todo!() + } + + fn poll_close( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + 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), - Unencrypted(Jabber), + Encrypted(Tls), + Unencrypted(Unencrypted), } impl Connection { - #[instrument] + // #[instrument] /// stream not started - pub async fn ensure_tls(self) -> Result> { - 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 ensure_tls(self) -> Result> { + // 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, password: String) -> Result { + pub async fn connect_user(jid: impl AsRef) -> Result { 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, - auth: Option>, - ) -> Result { - info!("connecting to {}", server); - let sockets = Self::get_sockets(&server).await; + pub async fn connect(server: impl AsRef + std::fmt::Debug) -> Result { + 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 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 { +// open stream (streams started) +pub struct JabberStream { reader: Reader>, writer: Writer>, - jid: Option, - auth: Option>, - server: String, } -impl Jabber +impl JabberStream where - S: AsyncRead + AsyncWrite + Unpin, + S: AsyncRead + AsyncWrite + Unpin + Send + std::fmt::Debug, + JabberStream: std::fmt::Debug, { - pub fn new( - reader: ReadHalf, - writer: WriteHalf, - jid: Option, - auth: Option>, - server: String, - ) -> Self { - let reader = Reader::new(reader); - let writer = Writer::new(writer); - Self { - reader, - writer, - jid, - auth, - server, - } - } -} - -impl Jabber -where - S: AsyncRead + AsyncWrite + Unpin + Send, - Jabber: std::fmt::Debug, -{ - pub async fn sasl( - &mut self, - mechanisms: Mechanisms, - sasl_config: Arc, - ) -> Result<()> { + pub async fn sasl(mut self, mechanisms: Mechanisms, sasl_config: Arc) -> Result { 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 { 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 { // 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 { + 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 { - pub async fn negotiate(mut self) -> Result> { - 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); - } - } +impl JabberStream { + // pub async fn negotiate( + // mut self, + // features: Features, + // ) -> Result { + // // 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 { - 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); - } - } + // #[async_recursion] + // pub async fn negotiate_tls_optional(mut self) -> Result { + // 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 { - #[async_recursion] - pub async fn negotiate(mut self) -> Result> { - self.start_stream().await?; - let features = self.get_features().await?.features; +impl JabberStream { + // #[async_recursion] + // pub async fn negotiate(mut self) -> Result> { + // 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); - } - } + // 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 { - pub async fn starttls(mut self) -> Result> { +impl JabberStream { + pub async fn starttls(mut self, domain: impl AsRef) -> Result { self.writer .write_full(&StartTls { required: false }) .await?; @@ -370,43 +349,31 @@ impl Jabber { 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 { +impl std::fmt::Debug for JabberStream { 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 { +impl std::fmt::Debug for JabberStream { 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 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 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 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 } } diff --git a/src/lib.rs b/src/lib.rs index 681d1d0..9c8d968 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 = std::result::Result; -pub async fn login, P: AsRef>(jid: J, password: P) -> Result> { - Ok(Connection::connect_user(jid, password.as_ref().to_string()) - .await? - .ensure_tls() - .await? - .negotiate() - .await?) +pub async fn login, P: AsRef>(jid: J, password: P) -> Result> { + todo!() + // Ok(Connection::connect_user(jid, password.as_ref().to_string()) + // .await? + // .ensure_tls() + // .await? + // .negotiate() + // .await?) } #[cfg(test)]