use std::str; use quick_xml::{ events::{BytesDecl, Event}, name::QName, Reader, Writer, }; use rsasl::prelude::{Mechname, SASLClient}; use tokio::io::{BufReader, ReadHalf, WriteHalf}; use tokio::net::TcpStream; use tokio_native_tls::TlsStream; use crate::stanza::{ sasl::{Auth, Response}, stream::{Stream, StreamFeature}, }; use crate::stanza::{ sasl::{Challenge, Success}, Element, }; use crate::Jabber; use crate::Result; pub struct JabberClient<'j> { reader: Reader>>>, writer: Writer>>, jabber: &'j mut Jabber<'j>, } impl<'j> JabberClient<'j> { pub fn new( reader: Reader>>>, writer: Writer>>, jabber: &'j mut Jabber<'j>, ) -> Self { Self { reader, writer, jabber, } } pub async fn start_stream(&mut self) -> Result<()> { // client to server let declaration = BytesDecl::new("1.0", None, None); 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(()) } pub async fn get_features(&mut self) -> Result> { Element::read(&mut self.reader).await?.try_into() } pub async fn negotiate(&mut self) -> Result<()> { loop { println!("loop"); let features = self.get_features().await?; println!("features: {:?}", features); match &features[0] { StreamFeature::Sasl(sasl) => { println!("sasl?"); self.sasl(&sasl).await?; } StreamFeature::Bind => todo!(), x => println!("{:?}", x), } } } pub async fn sasl(&mut self, mechanisms: &Vec) -> Result<()> { println!("{:?}", mechanisms); let sasl = SASLClient::new(self.jabber.auth.clone()); let mut offered_mechs: Vec<&Mechname> = Vec::new(); for mechanism in mechanisms { offered_mechs.push(Mechname::parse(mechanism.as_bytes())?) } println!("{:?}", offered_mechs); let mut session = sasl.start_suggested(&offered_mechs)?; let selected_mechanism = session.get_mechname().as_str().to_owned(); println!("selected mech: {:?}", selected_mechanism); let mut data: Option> = None; if !session.are_we_first() { // if not first mention the mechanism then get challenge data // mention mechanism let auth = Auth { mechanism: selected_mechanism.as_str(), sasl_data: "=", }; Into::::into(auth).write(&mut self.writer).await?; // get challenge data let challenge = &Element::read(&mut self.reader).await?; let challenge: Challenge = challenge.try_into()?; println!("challenge: {:?}", challenge); data = Some(challenge.sasl_data.to_owned()); println!("we didn't go first"); } else { // if first, mention mechanism and send data let mut sasl_data = Vec::new(); session.step64(None, &mut sasl_data).unwrap(); let auth = Auth { mechanism: selected_mechanism.as_str(), sasl_data: str::from_utf8(&sasl_data)?, }; println!("{:?}", auth); Into::::into(auth).write(&mut self.writer).await?; let server_response = Element::read(&mut self.reader).await?; println!("server_response: {:#?}", server_response); match TryInto::::try_into(&server_response) { Ok(challenge) => data = Some(challenge.sasl_data.to_owned()), Err(_) => { let success = TryInto::::try_into(&server_response)?; if let Some(sasl_data) = success.sasl_data { data = Some(sasl_data.to_owned()) } } } println!("we went first"); } // stepping the authentication exchange to completion if data != None { println!("data: {:?}", data); let mut sasl_data = Vec::new(); while { // decide if need to send more data over let state = session .step64(data.as_deref(), &mut sasl_data) .expect("step errored!"); state.is_running() } { // While we aren't finished, receive more data from the other party let response = Response { sasl_data: str::from_utf8(&sasl_data)?, }; println!("response: {:?}", response); Into::::into(response) .write(&mut self.writer) .await?; let server_response = Element::read(&mut self.reader).await?; println!("server_response: {:?}", server_response); match TryInto::::try_into(&server_response) { Ok(challenge) => data = Some(challenge.sasl_data.to_owned()), Err(_) => { let success = TryInto::::try_into(&server_response)?; if let Some(sasl_data) = success.sasl_data { data = Some(sasl_data.to_owned()) } } } } } self.start_stream().await?; Ok(()) } }