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