client.rs

  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/// A builder for `Client`s.
 19pub struct ClientBuilder {
 20    jid: Jid,
 21    host: Option<String>,
 22    port: u16,
 23}
 24
 25impl ClientBuilder {
 26    /// Creates a new builder for an XMPP client that will connect to `jid` with default parameters.
 27    pub fn new(jid: Jid) -> ClientBuilder {
 28        ClientBuilder {
 29            jid: jid,
 30            host: None,
 31            port: 5222,
 32        }
 33    }
 34
 35    /// Sets the host to connect to.
 36    pub fn host(mut self, host: String) -> ClientBuilder {
 37        self.host = Some(host);
 38        self
 39    }
 40
 41    /// Sets the port to connect to.
 42    pub fn port(mut self, port: u16) -> ClientBuilder {
 43        self.port = port;
 44        self
 45    }
 46
 47    /// Connects to the server and returns a `Client` when succesful.
 48    pub fn connect(self) -> Result<Client, Error> {
 49        let host = &self.host.unwrap_or(self.jid.domain.clone());
 50        let mut transport = SslTransport::connect(host, self.port)?;
 51        C2S::init(&mut transport, &self.jid.domain, "before_sasl")?;
 52        let (sender_out, sender_in) = channel();
 53        let (dispatcher_out, dispatcher_in) = channel();
 54        Ok(Client {
 55            jid: self.jid,
 56            transport: transport,
 57            plugins: Vec::new(),
 58            binding: PluginProxyBinding::new(sender_out, dispatcher_out),
 59            sender_in: sender_in,
 60            dispatcher_in: dispatcher_in,
 61        })
 62    }
 63}
 64
 65/// An XMPP client.
 66pub struct Client {
 67    jid: Jid,
 68    transport: SslTransport,
 69    plugins: Vec<Box<Plugin>>,
 70    binding: PluginProxyBinding,
 71    sender_in: Receiver<Element>,
 72    dispatcher_in: Receiver<AbstractEvent>,
 73}
 74
 75impl Client {
 76    /// Returns a reference to the `Jid` associated with this `Client`.
 77    pub fn jid(&self) -> &Jid {
 78        &self.jid
 79    }
 80
 81    /// Registers a plugin.
 82    pub fn register_plugin<P: Plugin + 'static>(&mut self, mut plugin: P) {
 83        plugin.bind(self.binding.clone());
 84        self.plugins.push(Box::new(plugin));
 85    }
 86
 87    /// Returns the plugin given by the type parameter, if it exists, else panics.
 88    pub fn plugin<P: Plugin>(&self) -> &P {
 89        for plugin in &self.plugins {
 90            let any = plugin.as_any();
 91            if let Some(ret) = any.downcast_ref::<P>() {
 92                return ret;
 93            }
 94        }
 95        panic!("plugin does not exist!");
 96    }
 97
 98    /// Returns the next event and flush the send queue.
 99    pub fn next_event(&mut self) -> Result<AbstractEvent, Error> {
100        self.flush_send_queue()?;
101        loop {
102            if let Ok(evt) = self.dispatcher_in.try_recv() {
103                return Ok(evt);
104            }
105            let elem = self.transport.read_element()?;
106            for plugin in self.plugins.iter_mut() {
107                plugin.handle(&elem);
108                // TODO: handle plugin return
109            }
110            self.flush_send_queue()?;
111        }
112    }
113
114    /// Flushes the send queue, sending all queued up stanzas.
115    pub fn flush_send_queue(&mut self) -> Result<(), Error> { // TODO: not sure how great of an
116                                                              //       idea it is to flush in this
117                                                              //       manner…
118        while let Ok(elem) = self.sender_in.try_recv() {
119            self.transport.write_element(&elem)?;
120        }
121        Ok(())
122    }
123
124    /// Connects using the specified SASL mechanism.
125    pub fn connect<S: SaslMechanism>(&mut self, mechanism: &mut S) -> Result<(), Error> {
126        // TODO: this is very ugly
127        loop {
128            let e = self.transport.read_event().unwrap();
129            match e {
130                ReaderEvent::StartElement { .. } => {
131                    break;
132                },
133                _ => (),
134            }
135        }
136        let mut did_sasl = false;
137        loop {
138            let n = self.transport.read_element().unwrap();
139            if n.is("features", ns::STREAM) {
140                if did_sasl {
141                    let mut elem = Element::builder("iq")
142                                           .attr("id", "bind")
143                                           .attr("type", "set")
144                                           .build();
145                    let bind = Element::builder("bind")
146                                       .ns(ns::BIND)
147                                       .build();
148                    elem.append_child(bind);
149                    self.transport.write_element(&elem)?;
150                }
151                else {
152                    let auth = mechanism.initial();
153                    let mut elem = Element::builder("auth")
154                                           .ns(ns::SASL)
155                                           .attr("mechanism", "PLAIN")
156                                           .build();
157                    if !auth.is_empty() {
158                        elem.append_text_node(base64::encode(&auth));
159                    }
160                    self.transport.write_element(&elem)?;
161                }
162            }
163            else if n.is("challenge", ns::SASL) {
164                let text = n.text();
165                let challenge = if text == "" {
166                    Vec::new()
167                }
168                else {
169                    base64::decode(&text)?
170                };
171                let response = mechanism.response(&challenge);
172                let mut elem = Element::builder("response")
173                                       .ns(ns::SASL)
174                                       .build();
175                if !response.is_empty() {
176                    elem.append_text_node(base64::encode(&response));
177                }
178                self.transport.write_element(&elem)?;
179            }
180            else if n.is("success", ns::SASL) {
181                did_sasl = true;
182                self.transport.reset_stream();
183                C2S::init(&mut self.transport, &self.jid.domain, "after_sasl")?;
184                loop {
185                    let e = self.transport.read_event()?;
186                    match e {
187                        ReaderEvent::StartElement { .. } => {
188                            break;
189                        },
190                        _ => (),
191                    }
192                }
193            }
194            else if n.is("failure", ns::SASL) {
195                let msg = n.text();
196                let inner = if msg == "" { None } else { Some(msg) };
197                return Err(Error::SaslError(inner));
198            }
199            else if n.is("iq", ns::CLIENT) && n.has_child("bind", ns::BIND) {
200                return Ok(());
201            }
202        }
203    }
204}