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}