bind.rs

  1use std::mem::replace;
  2use std::error::Error;
  3use std::str::FromStr;
  4use futures::*;
  5use futures::sink;
  6use tokio_io::{AsyncRead, AsyncWrite};
  7use jid::Jid;
  8use minidom::Element;
  9
 10use xmpp_codec::*;
 11use xmpp_stream::*;
 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 iq = make_bind_request(stream.jid.resource.as_ref());
 35                let send = stream.send(Packet::Stanza(iq));
 36                ClientBind::WaitSend(send)
 37            },
 38        }
 39    }
 40}
 41
 42fn make_bind_request(resource: Option<&String>) -> Element {
 43    let iq = Element::builder("iq")
 44        .attr("type", "set")
 45        .attr("id", BIND_REQ_ID);
 46    let mut bind_el = Element::builder("bind")
 47        .ns(NS_XMPP_BIND);
 48    match resource {
 49        Some(resource) => {
 50            let resource_el = Element::builder("resource")
 51                .append(resource);
 52            bind_el = bind_el.append(resource_el.build());
 53        },
 54        None => (),
 55    }
 56    iq.append(bind_el.build())
 57        .build()
 58}
 59
 60impl<S: AsyncRead + AsyncWrite> Future for ClientBind<S> {
 61    type Item = XMPPStream<S>;
 62    type Error = String;
 63
 64    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
 65        let state = replace(self, ClientBind::Invalid);
 66
 67        match state {
 68            ClientBind::Unsupported(stream) =>
 69                Ok(Async::Ready(stream)),
 70            ClientBind::WaitSend(mut send) => {
 71                match send.poll() {
 72                    Ok(Async::Ready(stream)) => {
 73                        replace(self, ClientBind::WaitRecv(stream));
 74                        self.poll()
 75                    },
 76                    Ok(Async::NotReady) => {
 77                        replace(self, ClientBind::WaitSend(send));
 78                        Ok(Async::NotReady)
 79                    },
 80                    Err(e) =>
 81                        Err(e.description().to_owned()),
 82                }
 83            },
 84            ClientBind::WaitRecv(mut stream) => {
 85                match stream.poll() {
 86                    Ok(Async::Ready(Some(Packet::Stanza(ref iq))))
 87                        if iq.name() == "iq"
 88                        && iq.attr("id") == Some(BIND_REQ_ID) => {
 89                            match iq.attr("type") {
 90                                Some("result") => {
 91                                    get_bind_response_jid(&iq)
 92                                        .map(|jid| stream.jid = jid);
 93                                    Ok(Async::Ready(stream))
 94                                },
 95                                _ =>
 96                                    Err("resource bind response".to_owned()),
 97                            }
 98                        },
 99                    Ok(Async::Ready(_)) => {
100                        replace(self, ClientBind::WaitRecv(stream));
101                        self.poll()
102                    },
103                    Ok(Async::NotReady) => {
104                        replace(self, ClientBind::WaitRecv(stream));
105                        Ok(Async::NotReady)
106                    },
107                    Err(e) =>
108                        Err(e.description().to_owned()),
109                }
110            },
111            ClientBind::Invalid =>
112                unreachable!(),
113        }
114    }
115}
116
117fn get_bind_response_jid(iq: &Element) -> Option<Jid> {
118    iq.get_child("bind", NS_XMPP_BIND)
119        .and_then(|bind_el|
120                  bind_el.get_child("jid", NS_XMPP_BIND)
121        )
122        .and_then(|jid_el|
123                  Jid::from_str(&jid_el.text())
124                  .ok()
125        )
126}