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}