1use jid::Jid;
2use transport::{Transport, SslTransport};
3use error::Error;
4use ns;
5use plugin::{Plugin, PluginProxyBinding};
6use event::AbstractEvent;
7use connection::{Connection, C2S};
8use sasl::SaslMechanism;
9
10use base64;
11
12use minidom::Element;
13
14use xml::reader::XmlEvent as ReaderEvent;
15
16use std::sync::mpsc::{Receiver, channel};
17
18/// Struct that should be moved somewhere else and cleaned up.
19#[derive(Debug)]
20pub struct StreamFeatures {
21 pub sasl_mechanisms: Option<Vec<String>>,
22}
23
24/// A builder for `Client`s.
25pub struct ClientBuilder {
26 jid: Jid,
27 host: Option<String>,
28 port: u16,
29}
30
31impl ClientBuilder {
32 /// Creates a new builder for an XMPP client that will connect to `jid` with default parameters.
33 pub fn new(jid: Jid) -> ClientBuilder {
34 ClientBuilder {
35 jid: jid,
36 host: None,
37 port: 5222,
38 }
39 }
40
41 /// Sets the host to connect to.
42 pub fn host(mut self, host: String) -> ClientBuilder {
43 self.host = Some(host);
44 self
45 }
46
47 /// Sets the port to connect to.
48 pub fn port(mut self, port: u16) -> ClientBuilder {
49 self.port = port;
50 self
51 }
52
53 /// Connects to the server and returns a `Client` when succesful.
54 pub fn connect(self) -> Result<Client, Error> {
55 let host = &self.host.unwrap_or(self.jid.domain.clone());
56 let mut transport = SslTransport::connect(host, self.port)?;
57 C2S::init(&mut transport, &self.jid.domain, "before_sasl")?;
58 let (sender_out, sender_in) = channel();
59 let (dispatcher_out, dispatcher_in) = channel();
60 Ok(Client {
61 jid: self.jid,
62 transport: transport,
63 plugins: Vec::new(),
64 binding: PluginProxyBinding::new(sender_out, dispatcher_out),
65 sender_in: sender_in,
66 dispatcher_in: dispatcher_in,
67 })
68 }
69}
70
71/// An XMPP client.
72pub struct Client {
73 jid: Jid,
74 transport: SslTransport,
75 plugins: Vec<Box<Plugin>>,
76 binding: PluginProxyBinding,
77 sender_in: Receiver<Element>,
78 dispatcher_in: Receiver<AbstractEvent>,
79}
80
81impl Client {
82 /// Returns a reference to the `Jid` associated with this `Client`.
83 pub fn jid(&self) -> &Jid {
84 &self.jid
85 }
86
87 /// Registers a plugin.
88 pub fn register_plugin<P: Plugin + 'static>(&mut self, mut plugin: P) {
89 plugin.bind(self.binding.clone());
90 self.plugins.push(Box::new(plugin));
91 }
92
93 /// Returns the plugin given by the type parameter, if it exists, else panics.
94 pub fn plugin<P: Plugin>(&self) -> &P {
95 for plugin in &self.plugins {
96 let any = plugin.as_any();
97 if let Some(ret) = any.downcast_ref::<P>() {
98 return ret;
99 }
100 }
101 panic!("plugin does not exist!");
102 }
103
104 /// Returns the next event and flush the send queue.
105 pub fn next_event(&mut self) -> Result<AbstractEvent, Error> {
106 self.flush_send_queue()?;
107 loop {
108 if let Ok(evt) = self.dispatcher_in.try_recv() {
109 return Ok(evt);
110 }
111 let elem = self.transport.read_element()?;
112 for plugin in self.plugins.iter_mut() {
113 plugin.handle(&elem);
114 // TODO: handle plugin return
115 }
116 self.flush_send_queue()?;
117 }
118 }
119
120 /// Flushes the send queue, sending all queued up stanzas.
121 pub fn flush_send_queue(&mut self) -> Result<(), Error> { // TODO: not sure how great of an
122 // idea it is to flush in this
123 // manner…
124 while let Ok(elem) = self.sender_in.try_recv() {
125 self.transport.write_element(&elem)?;
126 }
127 Ok(())
128 }
129
130 /// Connects and authenticates using the specified SASL mechanism.
131 pub fn connect<S: SaslMechanism>(&mut self, mechanism: &mut S) -> Result<(), Error> {
132 self.wait_for_features()?;
133 let auth = mechanism.initial().map_err(|x| Error::SaslError(Some(x)))?;
134 let mut elem = Element::builder("auth")
135 .ns(ns::SASL)
136 .attr("mechanism", S::name())
137 .build();
138 if !auth.is_empty() {
139 elem.append_text_node(base64::encode(&auth));
140 }
141 self.transport.write_element(&elem)?;
142 loop {
143 let n = self.transport.read_element()?;
144 if n.is("challenge", ns::SASL) {
145 let text = n.text();
146 let challenge = if text == "" {
147 Vec::new()
148 }
149 else {
150 base64::decode(&text)?
151 };
152 let response = mechanism.response(&challenge).map_err(|x| Error::SaslError(Some(x)))?;
153 let mut elem = Element::builder("response")
154 .ns(ns::SASL)
155 .build();
156 if !response.is_empty() {
157 elem.append_text_node(base64::encode(&response));
158 }
159 self.transport.write_element(&elem)?;
160 }
161 else if n.is("success", ns::SASL) {
162 let text = n.text();
163 let data = if text == "" {
164 Vec::new()
165 }
166 else {
167 base64::decode(&text)?
168 };
169 mechanism.success(&data).map_err(|x| Error::SaslError(Some(x)))?;
170 self.transport.reset_stream();
171 C2S::init(&mut self.transport, &self.jid.domain, "after_sasl")?;
172 self.wait_for_features()?;
173 return Ok(());
174 }
175 else if n.is("failure", ns::SASL) {
176 let msg = n.text();
177 let inner = if msg == "" { None } else { Some(msg) };
178 return Err(Error::SaslError(inner));
179 }
180 }
181 }
182
183 pub fn bind(&mut self) -> Result<(), Error> {
184 let mut elem = Element::builder("iq")
185 .attr("id", "bind")
186 .attr("type", "set")
187 .build();
188 let mut bind = Element::builder("bind")
189 .ns(ns::BIND)
190 .build();
191 if let Some(ref resource) = self.jid.resource {
192 let res = Element::builder("resource")
193 .ns(ns::BIND)
194 .text(resource.to_owned())
195 .build();
196 bind.append_child(res);
197 }
198 elem.append_child(bind);
199 self.transport.write_element(&elem)?;
200 loop {
201 let n = self.transport.read_element()?;
202 if n.is("iq", ns::CLIENT) && n.has_child("bind", ns::BIND) {
203 return Ok(());
204 }
205 }
206 }
207
208 fn wait_for_features(&mut self) -> Result<StreamFeatures, Error> {
209 // TODO: this is very ugly
210 loop {
211 let e = self.transport.read_event()?;
212 match e {
213 ReaderEvent::StartElement { .. } => {
214 break;
215 },
216 _ => (),
217 }
218 }
219 loop {
220 let n = self.transport.read_element()?;
221 if n.is("features", ns::STREAM) {
222 let mut features = StreamFeatures {
223 sasl_mechanisms: None,
224 };
225 if let Some(ms) = n.get_child("mechanisms", ns::SASL) {
226 let mut res = Vec::new();
227 for cld in ms.children() {
228 res.push(cld.text());
229 }
230 features.sasl_mechanisms = Some(res);
231 }
232 return Ok(features);
233 }
234 }
235 }
236}