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}