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::{IBB, 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(&self, from: Jid, ibb: IBB) -> Result<(), StanzaError> {
90 let mut sessions = self.sessions.lock().unwrap();
91 match ibb {
92 IBB::Open { block_size, sid, stanza } => {
93 match sessions.entry((from.clone(), sid.clone())) {
94 Entry::Vacant(_) => Ok(()),
95 Entry::Occupied(_) => Err(generate_error(
96 ErrorType::Cancel,
97 DefinedCondition::NotAcceptable,
98 "This session is already open."
99 )),
100 }?;
101 let session = Session {
102 stanza,
103 block_size,
104 cur_seq: 65535u16,
105 };
106 sessions.insert((from, sid), session.clone());
107 self.proxy.dispatch(IbbOpen {
108 session: session,
109 });
110 },
111 IBB::Data { seq, sid, data } => {
112 let entry = match sessions.entry((from, sid)) {
113 Entry::Occupied(entry) => Ok(entry),
114 Entry::Vacant(_) => Err(generate_error(
115 ErrorType::Cancel,
116 DefinedCondition::ItemNotFound,
117 "This session doesn’t exist."
118 )),
119 }?;
120 let mut session = entry.into_mut();
121 if session.stanza != Stanza::Iq {
122 return Err(generate_error(
123 ErrorType::Cancel,
124 DefinedCondition::NotAcceptable,
125 "Wrong stanza type."
126 ))
127 }
128 let cur_seq = session.cur_seq.wrapping_add(1);
129 if seq != cur_seq {
130 return Err(generate_error(
131 ErrorType::Cancel,
132 DefinedCondition::NotAcceptable,
133 "Wrong seq number."
134 ))
135 }
136 session.cur_seq = cur_seq;
137 self.proxy.dispatch(IbbData {
138 session: session.clone(),
139 data,
140 });
141 },
142 IBB::Close { sid } => {
143 let entry = match sessions.entry((from, sid)) {
144 Entry::Occupied(entry) => Ok(entry),
145 Entry::Vacant(_) => Err(generate_error(
146 ErrorType::Cancel,
147 DefinedCondition::ItemNotFound,
148 "This session doesn’t exist."
149 )),
150 }?;
151 let session = entry.remove();
152 self.proxy.dispatch(IbbClose {
153 session,
154 });
155 },
156 }
157 Ok(())
158 }
159
160 fn handle_iq(&self, iq: &Iq) -> Propagation {
161 let iq = iq.clone();
162 if let IqType::Set(payload) = iq.payload {
163 let from = iq.from.unwrap();
164 let id = iq.id.unwrap();
165 // TODO: use an intermediate plugin to parse this payload.
166 let payload = match IqSetPayload::try_from(payload) {
167 Ok(IqSetPayload::IBB(ibb)) => {
168 match self.handle_ibb(from.clone(), ibb) {
169 Ok(_) => IqType::Result(None),
170 Err(error) => IqType::Error(error),
171 }
172 },
173 Err(err) => IqType::Error(generate_error(
174 ErrorType::Cancel,
175 DefinedCondition::NotAcceptable,
176 format!("{:?}", err).as_ref()
177 )),
178 Ok(_) => return Propagation::Continue,
179 };
180 self.proxy.send(Iq {
181 from: None,
182 to: Some(from),
183 id: Some(id),
184 payload: payload,
185 }.into());
186 Propagation::Stop
187 } else {
188 Propagation::Continue
189 }
190 }
191}
192
193impl_plugin!(IbbPlugin, proxy, [
194 (Iq, Priority::Default) => handle_iq,
195]);