use std::mem::replace; use futures::{Future, Poll, Async, sink, Sink, Stream}; use tokio_io::{AsyncRead, AsyncWrite}; use minidom::Element; use sha1::{Sha1, Digest}; use xmpp_codec::Packet; use xmpp_stream::XMPPStream; const NS_JABBER_COMPONENT_ACCEPT: &str = "jabber:component:accept"; pub struct ComponentAuth { state: ComponentAuthState, } enum ComponentAuthState { WaitSend(sink::Send>), WaitRecv(XMPPStream), Invalid, } impl ComponentAuth { 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", // TODO: sha1(sid + password) &format!("{:x}", Sha1::digest((sid + &password).as_bytes())) ); return Ok(this); } fn send(&mut self, stream: XMPPStream, nonza_name: &str, handshake: &str) { let nonza = Element::builder(nonza_name) .ns(NS_JABBER_COMPONENT_ACCEPT) .append(handshake) .build(); let send = stream.send(Packet::Stanza(nonza)); self.state = ComponentAuthState::WaitSend(send); } } impl Future for ComponentAuth { type Item = XMPPStream; type Error = String; 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(format!("{}", 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") => { let e = "Authentication failure"; Err(e.to_owned()) }, Ok(Async::Ready(event)) => { println!("ComponentAuth ignore {:?}", event); Ok(Async::NotReady) }, Ok(_) => { self.state = ComponentAuthState::WaitRecv(stream); Ok(Async::NotReady) }, Err(e) => Err(format!("{}", e)), }, ComponentAuthState::Invalid => unreachable!(), } } }