add an ibb plugin

Emmanuel Gil Peyrot created

Change summary

examples/client.rs |   2 
src/plugins/ibb.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++++
src/plugins/mod.rs |   1 
3 files changed, 178 insertions(+)

Detailed changes

examples/client.rs 🔗

@@ -6,6 +6,7 @@ use xmpp::plugins::stanza::StanzaPlugin;
 use xmpp::plugins::unhandled_iq::UnhandledIqPlugin;
 use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent};
 use xmpp::plugins::presence::{PresencePlugin, Type};
+use xmpp::plugins::ibb::IbbPlugin;
 use xmpp::plugins::ping::{PingPlugin, PingEvent};
 use xmpp::event::{Priority, Propagation};
 
@@ -22,6 +23,7 @@ fn main() {
     client.register_plugin(UnhandledIqPlugin::new());
     client.register_plugin(MessagingPlugin::new());
     client.register_plugin(PresencePlugin::new());
+    client.register_plugin(IbbPlugin::new());
     client.register_plugin(PingPlugin::new());
     client.register_handler(Priority::Max, |e: &MessageEvent| {
         println!("{:?}", e);

src/plugins/ibb.rs 🔗

@@ -0,0 +1,175 @@
+use std::collections::{HashMap, BTreeMap};
+use std::collections::hash_map::Entry;
+use std::convert::TryFrom;
+use std::sync::{Mutex, Arc};
+
+use plugin::PluginProxy;
+use event::{Event, Priority, Propagation};
+use jid::Jid;
+
+use plugins::stanza::Iq;
+use xmpp_parsers::iq::{IqType, IqPayload};
+use xmpp_parsers::ibb::{IBB, Stanza};
+use xmpp_parsers::stanza_error::{StanzaError, ErrorType, DefinedCondition};
+
+#[derive(Debug, Clone)]
+pub struct Session {
+    stanza: Stanza,
+    block_size: u16,
+    cur_seq: u16,
+}
+
+#[derive(Debug)]
+pub struct IbbOpen {
+    pub session: Session,
+}
+
+#[derive(Debug)]
+pub struct IbbData {
+    pub session: Session,
+    pub data: Vec<u8>,
+}
+
+#[derive(Debug)]
+pub struct IbbClose {
+    pub session: Session,
+}
+
+impl Event for IbbOpen {}
+impl Event for IbbData {}
+impl Event for IbbClose {}
+
+fn generate_error(type_: ErrorType, defined_condition: DefinedCondition, text: &str) -> StanzaError {
+    StanzaError {
+        type_: type_,
+        defined_condition: defined_condition,
+        texts: {
+            let mut texts = BTreeMap::new();
+            texts.insert(String::new(), String::from(text));
+            texts
+        },
+        by: None,
+        other: None,
+    }
+}
+
+pub struct IbbPlugin {
+    proxy: PluginProxy,
+    sessions: Arc<Mutex<HashMap<(Jid, String), Session>>>,
+}
+
+impl IbbPlugin {
+    pub fn new() -> IbbPlugin {
+        IbbPlugin {
+            proxy: PluginProxy::new(),
+            sessions: Arc::new(Mutex::new(HashMap::new())),
+        }
+    }
+
+    fn handle_ibb(&self, from: Jid, ibb: IBB) -> Result<(), StanzaError> {
+        let mut sessions = self.sessions.lock().unwrap();
+        match ibb {
+            IBB::Open { block_size, sid, stanza } => {
+                match sessions.entry((from.clone(), sid.clone())) {
+                    Entry::Vacant(_) => Ok(()),
+                    Entry::Occupied(_) => Err(generate_error(
+                        ErrorType::Cancel,
+                        DefinedCondition::NotAcceptable,
+                        "This session is already open."
+                    )),
+                }?;
+                let session = Session {
+                    stanza,
+                    block_size,
+                    cur_seq: 65535u16,
+                };
+                sessions.insert((from, sid), session.clone());
+                self.proxy.dispatch(IbbOpen {
+                    session: session,
+                });
+            },
+            IBB::Data { seq, sid, data } => {
+                let entry = match sessions.entry((from, sid)) {
+                    Entry::Occupied(entry) => Ok(entry),
+                    Entry::Vacant(_) => Err(generate_error(
+                        ErrorType::Cancel,
+                        DefinedCondition::ItemNotFound,
+                        "This session doesn’t exist."
+                    )),
+                }?;
+                let mut session = entry.into_mut();
+                if session.stanza != Stanza::Iq {
+                    return Err(generate_error(
+                        ErrorType::Cancel,
+                        DefinedCondition::NotAcceptable,
+                        "Wrong stanza type."
+                    ))
+                }
+                let cur_seq = session.cur_seq.wrapping_add(1);
+                if seq != cur_seq {
+                    return Err(generate_error(
+                        ErrorType::Cancel,
+                        DefinedCondition::NotAcceptable,
+                        "Wrong seq number."
+                    ))
+                }
+                session.cur_seq = cur_seq;
+                self.proxy.dispatch(IbbData {
+                    session: session.clone(),
+                    data,
+                });
+            },
+            IBB::Close { sid } => {
+                let entry = match sessions.entry((from, sid)) {
+                    Entry::Occupied(entry) => Ok(entry),
+                    Entry::Vacant(_) => Err(generate_error(
+                        ErrorType::Cancel,
+                        DefinedCondition::ItemNotFound,
+                        "This session doesn’t exist."
+                    )),
+                }?;
+                let session = entry.remove();
+                self.proxy.dispatch(IbbClose {
+                    session,
+                });
+            },
+        }
+        Ok(())
+    }
+
+    fn handle_iq(&self, iq: &Iq) -> Propagation {
+        let iq = iq.clone();
+        if let IqType::Set(payload) = iq.payload {
+            let from = iq.from.unwrap();
+            let id = iq.id.unwrap();
+            // TODO: use an intermediate plugin to parse this payload.
+            let payload = match IqPayload::try_from(payload) {
+                Ok(IqPayload::IBB(ibb)) => {
+                    match self.handle_ibb(from.clone(), ibb) {
+                        Ok(_) => IqType::Result(None),
+                        Err(error) => IqType::Error(error),
+                    }
+                },
+                Err(err) => IqType::Error(generate_error(
+                    ErrorType::Cancel,
+                    DefinedCondition::NotAcceptable,
+                    format!("{:?}", err).as_ref()
+                )),
+                Ok(_) => return Propagation::Continue,
+            };
+            self.proxy.send(Iq {
+                from: None,
+                to: Some(from),
+                id: Some(id),
+                payload: payload,
+            }.into());
+            Propagation::Stop
+        } else {
+            Propagation::Continue
+        }
+    }
+}
+
+impl_plugin!(IbbPlugin, proxy, [
+    (Iq, Priority::Default) => handle_iq,
+]);

src/plugins/mod.rs 🔗

@@ -1,6 +1,7 @@
 pub mod messaging;
 pub mod presence;
 pub mod ping;
+pub mod ibb;
 pub mod stanza;
 pub mod stanza_debug;
 pub mod unhandled_iq;