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