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