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}