add a plugin to query and cache caps

This commit is contained in:
Emmanuel Gil Peyrot 2017-06-11 19:36:55 +01:00
parent 1b21ebc6b0
commit 0fbe09ad59
3 changed files with 143 additions and 0 deletions

View file

@ -8,6 +8,7 @@ use xmpp::plugins::unhandled_iq::UnhandledIqPlugin;
use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent}; use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent};
use xmpp::plugins::presence::{PresencePlugin, Type, Show}; use xmpp::plugins::presence::{PresencePlugin, Type, Show};
use xmpp::plugins::disco::DiscoPlugin; use xmpp::plugins::disco::DiscoPlugin;
use xmpp::plugins::caps::CapsPlugin;
use xmpp::plugins::ibb::IbbPlugin; use xmpp::plugins::ibb::IbbPlugin;
use xmpp::plugins::ping::PingPlugin; use xmpp::plugins::ping::PingPlugin;
use xmpp::event::{Priority, Propagation}; use xmpp::event::{Priority, Propagation};
@ -29,6 +30,7 @@ fn main() {
client.register_plugin(MessagingPlugin::new()); client.register_plugin(MessagingPlugin::new());
client.register_plugin(PresencePlugin::new()); client.register_plugin(PresencePlugin::new());
client.register_plugin(DiscoPlugin::new("client", "bot", "en", "xmpp-rs")); client.register_plugin(DiscoPlugin::new("client", "bot", "en", "xmpp-rs"));
client.register_plugin(CapsPlugin::new());
client.register_plugin(IbbPlugin::new()); client.register_plugin(IbbPlugin::new());
client.register_plugin(PingPlugin::new()); client.register_plugin(PingPlugin::new());
client.plugin::<PingPlugin>().init(); client.plugin::<PingPlugin>().init();

140
src/plugins/caps.rs Normal file
View file

@ -0,0 +1,140 @@
use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::{Mutex, Arc};
use plugin::PluginProxy;
use event::{Event, Priority, Propagation};
use jid::Jid;
use base64;
use plugins::stanza::{Presence, Iq};
use plugins::disco::DiscoInfoResult;
use xmpp_parsers::presence::Type as PresenceType;
use xmpp_parsers::iq::IqType;
use xmpp_parsers::disco::Disco;
use xmpp_parsers::caps::Caps;
#[derive(Debug)]
pub struct DiscoInfoRequest {
pub from: Jid,
pub id: String,
pub node: Option<String>,
}
impl Event for DiscoInfoRequest {}
pub struct CapsPlugin {
proxy: PluginProxy,
pending: Arc<Mutex<HashMap<Jid, (String, String)>>>,
cache: Arc<Mutex<HashMap<(Jid, String), Disco>>>,
}
impl CapsPlugin {
pub fn new() -> CapsPlugin {
CapsPlugin {
proxy: PluginProxy::new(),
pending: Arc::new(Mutex::new(HashMap::new())),
cache: Arc::new(Mutex::new(HashMap::new())),
}
}
fn handle_presence(&self, presence: &Presence) -> Propagation {
let presence = presence.clone();
match presence.type_ {
PresenceType::None => for payload in presence.payloads {
let caps = match Caps::try_from(payload) {
Ok(caps) => caps,
Err(_) => continue,
};
let recipient = presence.from.unwrap();
let node = format!("{}#{}", caps.node, base64::encode(&caps.hash.hash));
{
let cache = self.cache.lock().unwrap();
if cache.contains_key(&(recipient.clone(), node.clone())) {
break;
}
}
let id = self.proxy.gen_id();
{
let mut pending = self.pending.lock().unwrap();
pending.insert(recipient.clone(), (id.clone(), node.clone()));
}
let disco = Disco {
node: Some(node),
identities: vec!(),
features: vec!(),
extensions: vec!(),
};
self.proxy.send(Iq {
to: Some(recipient),
from: None,
id: Some(id),
payload: IqType::Get(disco.into()),
}.into());
break;
},
PresenceType::Unavailable
| PresenceType::Error => {
let recipient = presence.from.unwrap();
let mut pending = self.pending.lock().unwrap();
let previous = pending.remove(&recipient);
if previous.is_none() {
// This wasnt one of our requests.
return Propagation::Continue;
}
// TODO: maybe add a negative cache?
},
_ => (),
}
Propagation::Continue
}
fn handle_result(&self, result: &DiscoInfoResult) -> Propagation {
let from = result.from.clone();
let mut pending = self.pending.lock().unwrap();
let previous = pending.remove(&from.clone());
if let Some((id, node)) = previous {
if id != result.id {
return Propagation::Continue;
}
if Some(node.clone()) != result.disco.node {
// TODO: make that a debug log.
println!("Wrong node in result!");
return Propagation::Continue;
}
{
let mut cache = self.cache.lock().unwrap();
cache.insert((from, node), result.disco.clone());
}
} else {
// TODO: make that a debug log.
println!("No such request from us.");
return Propagation::Continue;
}
Propagation::Stop
}
// This is only for errors.
// TODO: also do the same thing for timeouts.
fn handle_iq(&self, iq: &Iq) -> Propagation {
let iq = iq.clone();
if let IqType::Error(_) = iq.payload {
let from = iq.from.unwrap();
let mut pending = self.pending.lock().unwrap();
let previous = pending.remove(&from.clone());
if previous.is_none() {
// This wasnt one of our requests.
return Propagation::Continue;
}
// TODO: maybe add a negative cache?
return Propagation::Stop;
}
Propagation::Continue
}
}
impl_plugin!(CapsPlugin, proxy, [
(Presence, Priority::Default) => handle_presence,
(Iq, Priority::Default) => handle_iq,
(DiscoInfoResult, Priority::Default) => handle_result,
]);

View file

@ -2,6 +2,7 @@ pub mod messaging;
pub mod presence; pub mod presence;
pub mod roster; pub mod roster;
pub mod disco; pub mod disco;
pub mod caps;
pub mod ping; pub mod ping;
pub mod ibb; pub mod ibb;
pub mod stanza; pub mod stanza;