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