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