add a roster plugin
This commit is contained in:
parent
04a3afb621
commit
66bc970d90
2 changed files with 187 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
|||
pub mod messaging;
|
||||
pub mod presence;
|
||||
pub mod roster;
|
||||
pub mod disco;
|
||||
pub mod ping;
|
||||
pub mod ibb;
|
||||
|
|
186
src/plugins/roster.rs
Normal file
186
src/plugins/roster.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use plugin::PluginProxy;
|
||||
use event::{Event, Priority, Propagation};
|
||||
use jid::Jid;
|
||||
|
||||
use plugins::stanza::Iq;
|
||||
use plugins::disco::DiscoPlugin;
|
||||
use xmpp_parsers::iq::{IqType, IqPayload};
|
||||
use xmpp_parsers::roster::{Roster, Item, Subscription};
|
||||
use xmpp_parsers::ns;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RosterReceived {
|
||||
pub ver: Option<String>,
|
||||
pub jids: HashMap<Jid, Item>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RosterPush {
|
||||
Added(Item),
|
||||
Modified(Item),
|
||||
Removed(Item),
|
||||
}
|
||||
|
||||
impl Event for RosterReceived {}
|
||||
impl Event for RosterPush {}
|
||||
|
||||
pub struct RosterPlugin {
|
||||
proxy: PluginProxy,
|
||||
current_version: Mutex<Option<String>>,
|
||||
// TODO: allow for a different backing store.
|
||||
jids: Mutex<HashMap<Jid, Item>>,
|
||||
}
|
||||
|
||||
impl RosterPlugin {
|
||||
pub fn new(ver: Option<String>) -> RosterPlugin {
|
||||
RosterPlugin {
|
||||
proxy: PluginProxy::new(),
|
||||
current_version: Mutex::new(ver),
|
||||
jids: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make that called automatically after plugins are created.
|
||||
pub fn init(&self) {
|
||||
if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
|
||||
disco.add_feature(ns::IBB);
|
||||
} else {
|
||||
panic!("Please handle dependencies in the correct order.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make that called automatically before removal.
|
||||
pub fn deinit(&self) {
|
||||
if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
|
||||
disco.remove_feature(ns::IBB);
|
||||
} else {
|
||||
panic!("Please handle dependencies in the correct order.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_roster_get(&self, ver: Option<String>) {
|
||||
let iq = Iq {
|
||||
from: None,
|
||||
to: None,
|
||||
id: Some(self.proxy.gen_id()),
|
||||
payload: IqType::Get(Roster {
|
||||
ver,
|
||||
items: vec!(),
|
||||
}.into()),
|
||||
};
|
||||
self.proxy.send(iq.into());
|
||||
}
|
||||
|
||||
// TODO: use a better error type.
|
||||
pub fn send_roster_set(&self, to: Option<Jid>, item: Item) -> Result<(), String> {
|
||||
if item.subscription.is_some() && item.subscription != Some(Subscription::Remove) {
|
||||
return Err(String::from("Subscription must be either nothing or Remove."));
|
||||
}
|
||||
let iq = Iq {
|
||||
from: None,
|
||||
to,
|
||||
id: Some(self.proxy.gen_id()),
|
||||
payload: IqType::Set(Roster {
|
||||
ver: None,
|
||||
items: vec!(item),
|
||||
}.into()),
|
||||
};
|
||||
self.proxy.send(iq.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_roster_reply(&self, roster: Roster) {
|
||||
// TODO: handle the same-ver case!
|
||||
let mut current_version = self.current_version.lock().unwrap();
|
||||
*current_version = roster.ver;
|
||||
let mut jids = self.jids.lock().unwrap();
|
||||
jids.clear();
|
||||
for item in roster.items {
|
||||
jids.insert(item.jid.clone(), item);
|
||||
}
|
||||
self.proxy.dispatch(RosterReceived {
|
||||
ver: current_version.clone(),
|
||||
jids: jids.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_roster_push(&self, roster: Roster) -> Result<(), String> {
|
||||
let item = roster.items.get(0);
|
||||
if item.is_none() || roster.items.len() != 1 {
|
||||
return Err(String::from("Server sent an invalid roster push!"));
|
||||
}
|
||||
let item = item.unwrap().clone();
|
||||
let mut jids = self.jids.lock().unwrap();
|
||||
let previous = jids.insert(item.jid.clone(), item.clone());
|
||||
if previous.is_none() {
|
||||
assert!(item.subscription != Some(Subscription::Remove));
|
||||
self.proxy.dispatch(RosterPush::Added(item));
|
||||
} else {
|
||||
if item.subscription == Some(Subscription::Remove) {
|
||||
self.proxy.dispatch(RosterPush::Removed(item));
|
||||
} else {
|
||||
self.proxy.dispatch(RosterPush::Modified(item));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_iq(&self, iq: &Iq) -> Propagation {
|
||||
let jid = self.proxy.get_own_jid();
|
||||
let jid = Jid::bare(jid.node.unwrap(), jid.domain);
|
||||
if iq.from.is_some() && iq.from != Some(jid) {
|
||||
// Not from our roster.
|
||||
return Propagation::Continue;
|
||||
}
|
||||
let iq = iq.clone();
|
||||
let id = iq.id.unwrap();
|
||||
match iq.payload {
|
||||
IqType::Result(Some(payload)) => {
|
||||
match IqPayload::try_from(payload) {
|
||||
Ok(IqPayload::Roster(roster)) => {
|
||||
self.handle_roster_reply(roster);
|
||||
Propagation::Stop
|
||||
},
|
||||
Ok(_)
|
||||
| Err(_) => Propagation::Continue,
|
||||
}
|
||||
},
|
||||
IqType::Set(payload) => {
|
||||
match IqPayload::try_from(payload) {
|
||||
Ok(IqPayload::Roster(roster)) => {
|
||||
let payload = match self.handle_roster_push(roster) {
|
||||
Ok(_) => IqType::Result(None),
|
||||
Err(string) => {
|
||||
// The specification says that the server should ignore an error.
|
||||
println!("{}", string);
|
||||
IqType::Result(None)
|
||||
},
|
||||
};
|
||||
self.proxy.send(Iq {
|
||||
from: None,
|
||||
to: None,
|
||||
id: Some(id),
|
||||
payload: payload,
|
||||
}.into());
|
||||
Propagation::Stop
|
||||
},
|
||||
Ok(_)
|
||||
| Err(_) => return Propagation::Continue,
|
||||
}
|
||||
},
|
||||
IqType::Result(None)
|
||||
| IqType::Get(_)
|
||||
| IqType::Error(_) => {
|
||||
Propagation::Continue
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_plugin!(RosterPlugin, proxy, [
|
||||
(Iq, Priority::Default) => handle_iq,
|
||||
]);
|
Loading…
Reference in a new issue