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/// 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}