use futures::{sink, Async, Future, Poll, Stream}; use std::mem::replace; use tokio_io::{AsyncRead, AsyncWrite}; use xmpp_parsers::component::Handshake; use crate::xmpp_codec::Packet; use crate::xmpp_stream::XMPPStream; use crate::{AuthError, Error}; const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; pub struct ComponentAuth { state: ComponentAuthState, } enum ComponentAuthState { WaitSend(sink::Send>), WaitRecv(XMPPStream), Invalid, } impl ComponentAuth { // TODO: doesn't have to be a Result<> actually pub fn new(stream: XMPPStream, password: String) -> Result { // FIXME: huge hack, shouldn’t be an element! let sid = stream.stream_features.name().to_owned(); let mut this = ComponentAuth { state: ComponentAuthState::Invalid, }; this.send( stream, Handshake::from_password_and_stream_id(&password, &sid), ); Ok(this) } fn send(&mut self, stream: XMPPStream, handshake: Handshake) { let nonza = handshake; let send = stream.send_stanza(nonza); self.state = ComponentAuthState::WaitSend(send); } } impl Future for ComponentAuth { type Item = XMPPStream; type Error = Error; fn poll(&mut self) -> Poll { let state = replace(&mut self.state, ComponentAuthState::Invalid); match state { ComponentAuthState::WaitSend(mut send) => match send.poll() { Ok(Async::Ready(stream)) => { self.state = ComponentAuthState::WaitRecv(stream); self.poll() } Ok(Async::NotReady) => { self.state = ComponentAuthState::WaitSend(send); Ok(Async::NotReady) } Err(e) => Err(e)?, }, ComponentAuthState::WaitRecv(mut stream) => match stream.poll() { Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.is("handshake", NS_JABBER_COMPONENT_ACCEPT) => { self.state = ComponentAuthState::Invalid; Ok(Async::Ready(stream)) } Ok(Async::Ready(Some(Packet::Stanza(ref stanza)))) if stanza.is("error", "http://etherx.jabber.org/streams") => { Err(AuthError::ComponentFail.into()) } Ok(Async::Ready(_event)) => { // println!("ComponentAuth ignore {:?}", _event); Ok(Async::NotReady) } Ok(_) => { self.state = ComponentAuthState::WaitRecv(stream); Ok(Async::NotReady) } Err(e) => Err(e)?, }, ComponentAuthState::Invalid => unreachable!(), } } }