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}