ibb.rs

  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]);