1use std::collections::{HashMap, BTreeMap};
2use std::collections::hash_map::Entry;
3use try_from::TryFrom;
4use std::sync::{Mutex, Arc};
5
6use plugin::PluginProxy;
7use event::{Event, Priority, Propagation};
8use jid::Jid;
9
10use plugins::stanza::Iq;
11use plugins::disco::DiscoPlugin;
12use xmpp_parsers::iq::{IqType, IqSetPayload};
13use xmpp_parsers::ibb::{Open, Data, Close, Stanza};
14use xmpp_parsers::stanza_error::{StanzaError, ErrorType, DefinedCondition};
15use xmpp_parsers::ns;
16
17#[derive(Debug, Clone)]
18pub struct Session {
19 stanza: Stanza,
20 block_size: u16,
21 cur_seq: u16,
22}
23
24#[derive(Debug)]
25pub struct IbbOpen {
26 pub session: Session,
27}
28
29#[derive(Debug)]
30pub struct IbbData {
31 pub session: Session,
32 pub data: Vec<u8>,
33}
34
35#[derive(Debug)]
36pub struct IbbClose {
37 pub session: Session,
38}
39
40impl Event for IbbOpen {}
41impl Event for IbbData {}
42impl Event for IbbClose {}
43
44fn generate_error(type_: ErrorType, defined_condition: DefinedCondition, text: &str) -> StanzaError {
45 StanzaError {
46 type_: type_,
47 defined_condition: defined_condition,
48 texts: {
49 let mut texts = BTreeMap::new();
50 texts.insert(String::new(), String::from(text));
51 texts
52 },
53 by: None,
54 other: None,
55 }
56}
57
58pub struct IbbPlugin {
59 proxy: PluginProxy,
60 sessions: Arc<Mutex<HashMap<(Jid, String), Session>>>,
61}
62
63impl IbbPlugin {
64 pub fn new() -> IbbPlugin {
65 IbbPlugin {
66 proxy: PluginProxy::new(),
67 sessions: Arc::new(Mutex::new(HashMap::new())),
68 }
69 }
70
71 // TODO: make that called automatically after plugins are created.
72 pub fn init(&self) {
73 if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
74 disco.add_feature(ns::IBB);
75 } else {
76 panic!("Please handle dependencies in the correct order.");
77 }
78 }
79
80 // TODO: make that called automatically before removal.
81 pub fn deinit(&self) {
82 if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
83 disco.remove_feature(ns::IBB);
84 } else {
85 panic!("Please handle dependencies in the correct order.");
86 }
87 }
88
89 fn handle_ibb_open(&self, from: Jid, open: Open) -> Result<(), StanzaError> {
90 let mut sessions = self.sessions.lock().unwrap();
91 let Open { block_size, sid, stanza } = open;
92 match sessions.entry((from.clone(), sid.clone())) {
93 Entry::Vacant(_) => Ok(()),
94 Entry::Occupied(_) => Err(generate_error(
95 ErrorType::Cancel,
96 DefinedCondition::NotAcceptable,
97 "This session is already open."
98 )),
99 }?;
100 let session = Session {
101 stanza,
102 block_size,
103 cur_seq: 65535u16,
104 };
105 sessions.insert((from, sid), session.clone());
106 self.proxy.dispatch(IbbOpen {
107 session: session,
108 });
109 Ok(())
110 }
111
112 fn handle_ibb_data(&self, from: Jid, data: Data) -> Result<(), StanzaError> {
113 let mut sessions = self.sessions.lock().unwrap();
114 let Data { seq, sid, data } = data;
115 let entry = match sessions.entry((from, sid)) {
116 Entry::Occupied(entry) => Ok(entry),
117 Entry::Vacant(_) => Err(generate_error(
118 ErrorType::Cancel,
119 DefinedCondition::ItemNotFound,
120 "This session doesn’t exist."
121 )),
122 }?;
123 let session = entry.into_mut();
124 if session.stanza != Stanza::Iq {
125 return Err(generate_error(
126 ErrorType::Cancel,
127 DefinedCondition::NotAcceptable,
128 "Wrong stanza type."
129 ))
130 }
131 let cur_seq = session.cur_seq.wrapping_add(1);
132 if seq != cur_seq {
133 return Err(generate_error(
134 ErrorType::Cancel,
135 DefinedCondition::NotAcceptable,
136 "Wrong seq number."
137 ))
138 }
139 session.cur_seq = cur_seq;
140 self.proxy.dispatch(IbbData {
141 session: session.clone(),
142 data,
143 });
144 Ok(())
145 }
146
147 fn handle_ibb_close(&self, from: Jid, close: Close) -> Result<(), StanzaError> {
148 let mut sessions = self.sessions.lock().unwrap();
149 let Close { sid } = close;
150 let entry = match sessions.entry((from, sid)) {
151 Entry::Occupied(entry) => Ok(entry),
152 Entry::Vacant(_) => Err(generate_error(
153 ErrorType::Cancel,
154 DefinedCondition::ItemNotFound,
155 "This session doesn’t exist."
156 )),
157 }?;
158 let session = entry.remove();
159 self.proxy.dispatch(IbbClose {
160 session,
161 });
162 Ok(())
163 }
164
165 fn handle_iq(&self, iq: &Iq) -> Propagation {
166 let iq = iq.clone();
167 if let IqType::Set(payload) = iq.payload {
168 let from = iq.from.unwrap();
169 let id = iq.id.unwrap();
170 // TODO: use an intermediate plugin to parse this payload.
171 let payload = match IqSetPayload::try_from(payload) {
172 Ok(IqSetPayload::IbbOpen(open)) => {
173 match self.handle_ibb_open(from.clone(), open) {
174 Ok(_) => IqType::Result(None),
175 Err(error) => IqType::Error(error),
176 }
177 },
178 Ok(IqSetPayload::IbbData(data)) => {
179 match self.handle_ibb_data(from.clone(), data) {
180 Ok(_) => IqType::Result(None),
181 Err(error) => IqType::Error(error),
182 }
183 },
184 Ok(IqSetPayload::IbbClose(close)) => {
185 match self.handle_ibb_close(from.clone(), close) {
186 Ok(_) => IqType::Result(None),
187 Err(error) => IqType::Error(error),
188 }
189 },
190 Err(err) => IqType::Error(generate_error(
191 ErrorType::Cancel,
192 DefinedCondition::NotAcceptable,
193 format!("{:?}", err).as_ref()
194 )),
195 Ok(_) => return Propagation::Continue,
196 };
197 self.proxy.send(Iq {
198 from: None,
199 to: Some(from),
200 id: Some(id),
201 payload: payload,
202 }.into());
203 Propagation::Stop
204 } else {
205 Propagation::Continue
206 }
207 }
208}
209
210impl_plugin!(IbbPlugin, proxy, [
211 (Iq, Priority::Default) => handle_iq,
212]);