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