roster.rs

  1use std::collections::HashMap;
  2use try_from::TryFrom;
  3use std::sync::Mutex;
  4
  5use plugin::PluginProxy;
  6use event::{Event, Priority, Propagation};
  7use jid::Jid;
  8
  9use plugins::stanza::Iq;
 10use plugins::disco::DiscoPlugin;
 11use xmpp_parsers::iq::{IqType, IqSetPayload, IqResultPayload};
 12use xmpp_parsers::roster::{Roster, Item, Subscription};
 13use xmpp_parsers::ns;
 14
 15#[derive(Debug)]
 16pub struct RosterReceived {
 17    pub ver: Option<String>,
 18    pub jids: HashMap<Jid, Item>,
 19}
 20
 21#[derive(Debug)]
 22pub enum RosterPush {
 23    Added(Item),
 24    Modified(Item),
 25    Removed(Item),
 26}
 27
 28impl Event for RosterReceived {}
 29impl Event for RosterPush {}
 30
 31pub struct RosterPlugin {
 32    proxy: PluginProxy,
 33    current_version: Mutex<Option<String>>,
 34    // TODO: allow for a different backing store.
 35    jids: Mutex<HashMap<Jid, Item>>,
 36}
 37
 38impl RosterPlugin {
 39    pub fn new(ver: Option<String>) -> RosterPlugin {
 40        RosterPlugin {
 41            proxy: PluginProxy::new(),
 42            current_version: Mutex::new(ver),
 43            jids: Mutex::new(HashMap::new()),
 44        }
 45    }
 46
 47    // TODO: make that called automatically after plugins are created.
 48    pub fn init(&self) {
 49        if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
 50            disco.add_feature(ns::IBB);
 51        } else {
 52            panic!("Please handle dependencies in the correct order.");
 53        }
 54    }
 55
 56    // TODO: make that called automatically before removal.
 57    pub fn deinit(&self) {
 58        if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
 59            disco.remove_feature(ns::IBB);
 60        } else {
 61            panic!("Please handle dependencies in the correct order.");
 62        }
 63    }
 64
 65    pub fn send_roster_get(&self, ver: Option<String>) {
 66        let iq = Iq {
 67            from: None,
 68            to: None,
 69            id: Some(self.proxy.gen_id()),
 70            payload: IqType::Get(Roster {
 71                ver,
 72                items: vec!(),
 73            }.into()),
 74        };
 75        self.proxy.send(iq.into());
 76    }
 77
 78    // TODO: use a better error type.
 79    pub fn send_roster_set(&self, to: Option<Jid>, item: Item) -> Result<(), String> {
 80        if item.subscription.is_some() && item.subscription != Some(Subscription::Remove) {
 81            return Err(String::from("Subscription must be either nothing or Remove."));
 82        }
 83        let iq = Iq {
 84            from: None,
 85            to,
 86            id: Some(self.proxy.gen_id()),
 87            payload: IqType::Set(Roster {
 88                ver: None,
 89                items: vec!(item),
 90            }.into()),
 91        };
 92        self.proxy.send(iq.into());
 93        Ok(())
 94    }
 95
 96    fn handle_roster_reply(&self, roster: Roster) {
 97        // TODO: handle the same-ver case!
 98        let mut current_version = self.current_version.lock().unwrap();
 99        *current_version = roster.ver;
100        let mut jids = self.jids.lock().unwrap();
101        jids.clear();
102        for item in roster.items {
103            jids.insert(item.jid.clone(), item);
104        }
105        self.proxy.dispatch(RosterReceived {
106            ver: current_version.clone(),
107            jids: jids.clone(),
108        });
109    }
110
111    fn handle_roster_push(&self, roster: Roster) -> Result<(), String> {
112        let item = roster.items.get(0);
113        if item.is_none() || roster.items.len() != 1 {
114            return Err(String::from("Server sent an invalid roster push!"));
115        }
116        let item = item.unwrap().clone();
117        let mut jids = self.jids.lock().unwrap();
118        let previous = jids.insert(item.jid.clone(), item.clone());
119        if previous.is_none() {
120            assert!(item.subscription != Some(Subscription::Remove));
121            self.proxy.dispatch(RosterPush::Added(item));
122        } else {
123            if item.subscription == Some(Subscription::Remove) {
124                self.proxy.dispatch(RosterPush::Removed(item));
125            } else {
126                self.proxy.dispatch(RosterPush::Modified(item));
127            }
128        }
129        Ok(())
130    }
131
132    fn handle_iq(&self, iq: &Iq) -> Propagation {
133        let jid = self.proxy.get_own_jid();
134        let jid = Jid::bare(jid.node.unwrap(), jid.domain);
135        if iq.from.is_some() && iq.from != Some(jid) {
136            // Not from our roster.
137            return Propagation::Continue;
138        }
139        let iq = iq.clone();
140        let id = iq.id.unwrap();
141        match iq.payload {
142            IqType::Result(Some(payload)) => {
143                match IqResultPayload::try_from(payload) {
144                    Ok(IqResultPayload::Roster(roster)) => {
145                        self.handle_roster_reply(roster);
146                        Propagation::Stop
147                    },
148                    Ok(_)
149                  | Err(_) => Propagation::Continue,
150                }
151            },
152            IqType::Set(payload) => {
153                match IqSetPayload::try_from(payload) {
154                    Ok(IqSetPayload::Roster(roster)) => {
155                        let payload = match self.handle_roster_push(roster) {
156                            Ok(_) => IqType::Result(None),
157                            Err(string) => {
158                                // The specification says that the server should ignore an error.
159                                println!("{}", string);
160                                IqType::Result(None)
161                            },
162                        };
163                        self.proxy.send(Iq {
164                            from: None,
165                            to: None,
166                            id: Some(id),
167                            payload: payload,
168                        }.into());
169                        Propagation::Stop
170                    },
171                    Ok(_)
172                  | Err(_) => return Propagation::Continue,
173                }
174            },
175            IqType::Result(None)
176          | IqType::Get(_)
177          | IqType::Error(_) => {
178                Propagation::Continue
179            },
180        }
181    }
182}
183
184impl_plugin!(RosterPlugin, proxy, [
185    (Iq, Priority::Default) => handle_iq,
186]);