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 != 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 != Subscription::Remove);
121 self.proxy.dispatch(RosterPush::Added(item));
122 } else {
123 if item.subscription == 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]);