140 lines
4.6 KiB
Rust
140 lines
4.6 KiB
Rust
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 wasn’t 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 wasn’t 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,
|
||
]);
|