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}