client_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 xml;
  8use jid::Jid;
  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", Some(NS_XMPP_BIND)) {
 29            None =>
 30                // No resource binding available,
 31                // return the (probably // usable) stream immediately
 32                ClientBind::Unsupported(stream),
 33            Some(_) => {
 34                println!("Bind is supported!");
 35
 36                let iq = make_bind_request(stream.jid.resource.as_ref());
 37                println!("Send {}", iq);
 38                let send = stream.send(Packet::Stanza(iq));
 39                ClientBind::WaitSend(send)
 40            },
 41        }
 42    }
 43}
 44
 45fn make_bind_request(resource: Option<&String>) -> xml::Element {
 46    let mut iq = xml::Element::new(
 47        "iq".to_owned(),
 48        None,
 49        vec![("type".to_owned(), None, "set".to_owned()),
 50             ("id".to_owned(), None, BIND_REQ_ID.to_owned())]
 51    );
 52    {
 53        let bind_el = iq.tag(
 54            xml::Element::new(
 55                "bind".to_owned(),
 56                Some(NS_XMPP_BIND.to_owned()),
 57                vec![]
 58            ));
 59        resource.map(|resource| {
 60            let resource_el = bind_el.tag(
 61                xml::Element::new(
 62                    "resource".to_owned(),
 63                    Some(NS_XMPP_BIND.to_owned()),
 64                    vec![]
 65                ));
 66            resource_el.text(resource.clone());
 67        });
 68    }
 69    iq
 70}
 71
 72impl<S: AsyncRead + AsyncWrite> Future for ClientBind<S> {
 73    type Item = XMPPStream<S>;
 74    type Error = String;
 75
 76    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
 77        let state = replace(self, ClientBind::Invalid);
 78
 79        match state {
 80            ClientBind::Unsupported(stream) =>
 81                Ok(Async::Ready(stream)),
 82            ClientBind::WaitSend(mut send) => {
 83                match send.poll() {
 84                    Ok(Async::Ready(stream)) => {
 85                        replace(self, ClientBind::WaitRecv(stream));
 86                        self.poll()
 87                    },
 88                    Ok(Async::NotReady) => {
 89                        replace(self, ClientBind::WaitSend(send));
 90                        Ok(Async::NotReady)
 91                    },
 92                    Err(e) =>
 93                        Err(e.description().to_owned()),
 94                }
 95            },
 96            ClientBind::WaitRecv(mut stream) => {
 97                match stream.poll() {
 98                    Ok(Async::Ready(Some(Packet::Stanza(ref iq))))
 99                        if iq.name == "iq"
100                        && iq.get_attribute("id", None) == Some(BIND_REQ_ID) => {
101                            match iq.get_attribute("type", None) {
102                                Some("result") => {
103                                    get_bind_response_jid(&iq)
104                                        .map(|jid| stream.jid = jid);
105                                    Ok(Async::Ready(stream))
106                                },
107                                _ =>
108                                    Err("resource bind response".to_owned()),
109                            }
110                        },
111                    Ok(Async::Ready(_)) => {
112                        replace(self, ClientBind::WaitRecv(stream));
113                        self.poll()
114                    },
115                    Ok(_) => {
116                        replace(self, ClientBind::WaitRecv(stream));
117                        Ok(Async::NotReady)
118                    },
119                    Err(e) =>
120                        Err(e.description().to_owned()),
121                }
122            },
123            ClientBind::Invalid =>
124                unreachable!(),
125        }
126    }
127}
128
129fn get_bind_response_jid(iq: &xml::Element) -> Option<Jid> {
130    iq.get_child("bind", Some(NS_XMPP_BIND))
131        .and_then(|bind_el|
132                  bind_el.get_child("jid", Some(NS_XMPP_BIND))
133        )
134        .and_then(|jid_el|
135                  Jid::from_str(&jid_el.content_str())
136                  .ok()
137        )
138}