bind.rs

  1use std::mem::replace;
  2use std::error::Error;
  3use std::str::FromStr;
  4use futures::{Future, Poll, Async, sink, Sink, Stream};
  5use tokio_io::{AsyncRead, AsyncWrite};
  6use jid::Jid;
  7use minidom::Element;
  8use xmpp_parsers::bind::Bind;
  9
 10use xmpp_codec::Packet;
 11use xmpp_stream::XMPPStream;
 12
 13const NS_XMPP_BIND: &str = "urn:ietf:params:xml:ns:xmpp-bind";
 14const BIND_REQ_ID: &str = "resource-bind";
 15
 16pub enum ClientBind<S: AsyncWrite> {
 17    Unsupported(XMPPStream<S>),
 18    WaitSend(sink::Send<XMPPStream<S>>),
 19    WaitRecv(XMPPStream<S>),
 20    Invalid,
 21}
 22
 23impl<S: AsyncWrite> ClientBind<S> {
 24    /// Consumes and returns the stream to express that you cannot use
 25    /// the stream for anything else until the resource binding
 26    /// req/resp are done.
 27    pub fn new(stream: XMPPStream<S>) -> Self {
 28        match stream.stream_features.get_child("bind", NS_XMPP_BIND) {
 29            None =>
 30                // No resource binding available,
 31                // return the (probably // usable) stream immediately
 32                ClientBind::Unsupported(stream),
 33            Some(_) => {
 34                let resource = stream.jid.resource.clone();
 35                let iq = Bind::new(resource);
 36                let send = stream.send_stanza(iq);
 37                ClientBind::WaitSend(send)
 38            },
 39        }
 40    }
 41}
 42
 43impl<S: AsyncRead + AsyncWrite> Future for ClientBind<S> {
 44    type Item = XMPPStream<S>;
 45    type Error = String;
 46
 47    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
 48        let state = replace(self, ClientBind::Invalid);
 49
 50        match state {
 51            ClientBind::Unsupported(stream) =>
 52                Ok(Async::Ready(stream)),
 53            ClientBind::WaitSend(mut send) => {
 54                match send.poll() {
 55                    Ok(Async::Ready(stream)) => {
 56                        replace(self, ClientBind::WaitRecv(stream));
 57                        self.poll()
 58                    },
 59                    Ok(Async::NotReady) => {
 60                        replace(self, ClientBind::WaitSend(send));
 61                        Ok(Async::NotReady)
 62                    },
 63                    Err(e) =>
 64                        Err(e.description().to_owned()),
 65                }
 66            },
 67            ClientBind::WaitRecv(mut stream) => {
 68                match stream.poll() {
 69                    Ok(Async::Ready(Some(Packet::Stanza(ref iq))))
 70                        if iq.name() == "iq"
 71                        && iq.attr("id") == Some(BIND_REQ_ID) => {
 72                            match iq.attr("type") {
 73                                Some("result") => {
 74                                    get_bind_response_jid(iq)
 75                                        .map(|jid| stream.jid = jid);
 76                                    Ok(Async::Ready(stream))
 77                                },
 78                                _ =>
 79                                    Err("resource bind response".to_owned()),
 80                            }
 81                        },
 82                    Ok(Async::Ready(_)) => {
 83                        replace(self, ClientBind::WaitRecv(stream));
 84                        self.poll()
 85                    },
 86                    Ok(Async::NotReady) => {
 87                        replace(self, ClientBind::WaitRecv(stream));
 88                        Ok(Async::NotReady)
 89                    },
 90                    Err(e) =>
 91                        Err(e.description().to_owned()),
 92                }
 93            },
 94            ClientBind::Invalid =>
 95                unreachable!(),
 96        }
 97    }
 98}
 99
100fn get_bind_response_jid(iq: &Element) -> Option<Jid> {
101    iq.get_child("bind", NS_XMPP_BIND)
102        .and_then(|bind_el|
103                  bind_el.get_child("jid", NS_XMPP_BIND)
104        )
105        .and_then(|jid_el|
106                  Jid::from_str(&jid_el.text())
107                  .ok()
108        )
109}