ibb.rs

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