component.rs

  1use jid::Jid;
  2use transport::{Transport, PlainTransport};
  3use error::Error;
  4use ns;
  5use plugin::{Plugin, PluginProxyBinding};
  6use event::AbstractEvent;
  7use connection::{Connection, Component2S};
  8use openssl::hash::{hash, MessageDigest};
  9
 10use minidom::Element;
 11
 12use xml::reader::XmlEvent as ReaderEvent;
 13
 14use std::sync::mpsc::{Receiver, channel};
 15
 16/// A builder for `Component`s.
 17pub struct ComponentBuilder {
 18    jid: Jid,
 19    secret: String,
 20    host: Option<String>,
 21    port: u16,
 22}
 23
 24impl ComponentBuilder {
 25    /// Creates a new builder for an XMPP component that will connect to `jid` with default parameters.
 26    pub fn new(jid: Jid) -> ComponentBuilder {
 27        ComponentBuilder {
 28            jid: jid,
 29            secret: "".to_owned(),
 30            host: None,
 31            port: 5347,
 32        }
 33    }
 34
 35    /// Sets the host to connect to.
 36    pub fn host(mut self, host: String) -> ComponentBuilder {
 37        self.host = Some(host);
 38        self
 39    }
 40
 41    /// Sets the port to connect to.
 42    pub fn port(mut self, port: u16) -> ComponentBuilder {
 43        self.port = port;
 44        self
 45    }
 46
 47    /// Sets the password to use.
 48    pub fn password<P: Into<String>>(mut self, password: P) -> ComponentBuilder {
 49        self.secret = password.into();
 50        self
 51    }
 52
 53    /// Connects to the server and returns a `Component` when succesful.
 54    pub fn connect(self) -> Result<Component, Error> {
 55        let host = &self.host.unwrap_or(self.jid.domain.clone());
 56        let mut transport = PlainTransport::connect(host, self.port)?;
 57        Component2S::init(&mut transport, &self.jid.domain, "stream_opening")?;
 58        let (sender_out, sender_in) = channel();
 59        let (dispatcher_out, dispatcher_in) = channel();
 60        let mut component = Component {
 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        component.connect(self.secret)?;
 69        Ok(component)
 70    }
 71}
 72
 73/// An XMPP component.
 74pub struct Component {
 75    jid: Jid,
 76    transport: PlainTransport,
 77    plugins: Vec<Box<Plugin>>,
 78    binding: PluginProxyBinding,
 79    sender_in: Receiver<Element>,
 80    dispatcher_in: Receiver<AbstractEvent>,
 81}
 82
 83impl Component {
 84    /// Returns a reference to the `Jid` associated with this `Component`.
 85    pub fn jid(&self) -> &Jid {
 86        &self.jid
 87    }
 88
 89    /// Registers a plugin.
 90    pub fn register_plugin<P: Plugin + 'static>(&mut self, mut plugin: P) {
 91        plugin.bind(self.binding.clone());
 92        self.plugins.push(Box::new(plugin));
 93    }
 94
 95    /// Returns the plugin given by the type parameter, if it exists, else panics.
 96    pub fn plugin<P: Plugin>(&self) -> &P {
 97        for plugin in &self.plugins {
 98            let any = plugin.as_any();
 99            if let Some(ret) = any.downcast_ref::<P>() {
100                return ret;
101            }
102        }
103        panic!("plugin does not exist!");
104    }
105
106    /// Returns the next event and flush the send queue.
107    pub fn next_event(&mut self) -> Result<AbstractEvent, Error> {
108        self.flush_send_queue()?;
109        loop {
110            if let Ok(evt) = self.dispatcher_in.try_recv() {
111                return Ok(evt);
112            }
113            let elem = self.transport.read_element()?;
114            for plugin in self.plugins.iter_mut() {
115                plugin.handle(&elem);
116                // TODO: handle plugin return
117            }
118            self.flush_send_queue()?;
119        }
120    }
121
122    /// Flushes the send queue, sending all queued up stanzas.
123    pub fn flush_send_queue(&mut self) -> Result<(), Error> { // TODO: not sure how great of an
124                                                              //       idea it is to flush in this
125                                                              //       manner…
126        while let Ok(elem) = self.sender_in.try_recv() {
127            self.transport.write_element(&elem)?;
128        }
129        Ok(())
130    }
131
132    fn connect(&mut self, secret: String) -> Result<(), Error> {
133        // TODO: this is very ugly
134        let mut sid = String::new();
135        loop {
136            let e = self.transport.read_event()?;
137            match e {
138                ReaderEvent::StartElement { attributes, .. } => {
139                    for attribute in attributes {
140                        if attribute.name.namespace == None && attribute.name.local_name == "id" {
141                            sid = attribute.value;
142                        }
143                    }
144                    break;
145                },
146                _ => (),
147            }
148        }
149        let concatenated = format!("{}{}", sid, secret);
150        let hash = hash(MessageDigest::sha1(), concatenated.as_bytes())?;
151        let mut handshake = String::new();
152        for byte in hash {
153            // TODO: probably terrible perfs!
154            handshake = format!("{}{:x}", handshake, byte);
155        }
156        let mut elem = Element::builder("handshake")
157                               .ns(ns::COMPONENT_ACCEPT)
158                               .build();
159        elem.append_text_node(handshake);
160        self.transport.write_element(&elem)?;
161        loop {
162            let n = self.transport.read_element()?;
163            if n.is("handshake", ns::COMPONENT_ACCEPT) {
164                return Ok(());
165            }
166        }
167    }
168}