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