1use std::collections::HashMap;
2use try_from::TryFrom;
3use std::sync::{Mutex, Arc};
4
5use plugin::PluginProxy;
6use event::{Event, Priority, Propagation};
7use jid::Jid;
8use base64;
9
10use plugins::stanza::{Presence, Iq};
11use plugins::disco::DiscoInfoResult;
12use xmpp_parsers::presence::Type as PresenceType;
13use xmpp_parsers::iq::IqType;
14use xmpp_parsers::disco::{DiscoInfoQuery, DiscoInfoResult as DiscoInfoResult_};
15use xmpp_parsers::caps::Caps;
16
17#[derive(Debug)]
18pub struct DiscoInfoRequest {
19 pub from: Jid,
20 pub id: String,
21 pub node: Option<String>,
22}
23
24impl Event for DiscoInfoRequest {}
25
26pub struct CapsPlugin {
27 proxy: PluginProxy,
28 pending: Arc<Mutex<HashMap<Jid, (String, String)>>>,
29 cache: Arc<Mutex<HashMap<(Jid, String), DiscoInfoResult_>>>,
30}
31
32impl CapsPlugin {
33 pub fn new() -> CapsPlugin {
34 CapsPlugin {
35 proxy: PluginProxy::new(),
36 pending: Arc::new(Mutex::new(HashMap::new())),
37 cache: Arc::new(Mutex::new(HashMap::new())),
38 }
39 }
40
41 fn handle_presence(&self, presence: &Presence) -> Propagation {
42 let presence = presence.clone();
43 match presence.type_ {
44 PresenceType::None => for payload in presence.payloads {
45 let caps = match Caps::try_from(payload) {
46 Ok(caps) => caps,
47 Err(_) => continue,
48 };
49 let recipient = presence.from.unwrap();
50 let node = format!("{}#{}", caps.node, base64::encode(&caps.hash.hash));
51 {
52 let cache = self.cache.lock().unwrap();
53 if cache.contains_key(&(recipient.clone(), node.clone())) {
54 break;
55 }
56 }
57 let id = self.proxy.gen_id();
58 {
59 let mut pending = self.pending.lock().unwrap();
60 pending.insert(recipient.clone(), (id.clone(), node.clone()));
61 }
62 let disco = DiscoInfoQuery {
63 node: Some(node),
64 };
65 self.proxy.send(Iq {
66 to: Some(recipient),
67 from: None,
68 id: Some(id),
69 payload: IqType::Get(disco.into()),
70 }.into());
71 break;
72 },
73 PresenceType::Unavailable
74 | PresenceType::Error => {
75 let recipient = presence.from.unwrap();
76 let mut pending = self.pending.lock().unwrap();
77 let previous = pending.remove(&recipient);
78 if previous.is_none() {
79 // This wasn’t one of our requests.
80 return Propagation::Continue;
81 }
82 // TODO: maybe add a negative cache?
83 },
84 _ => (),
85 }
86 Propagation::Continue
87 }
88
89 fn handle_result(&self, result: &DiscoInfoResult) -> Propagation {
90 let from = result.from.clone();
91 let mut pending = self.pending.lock().unwrap();
92 let previous = pending.remove(&from.clone());
93 if let Some((id, node)) = previous {
94 if id != result.id {
95 return Propagation::Continue;
96 }
97 if Some(node.clone()) != result.disco.node {
98 // TODO: make that a debug log.
99 println!("Wrong node in result!");
100 return Propagation::Continue;
101 }
102 {
103 let mut cache = self.cache.lock().unwrap();
104 cache.insert((from, node), result.disco.clone());
105 }
106 } else {
107 // TODO: make that a debug log.
108 println!("No such request from us.");
109 return Propagation::Continue;
110 }
111 Propagation::Stop
112 }
113
114 // This is only for errors.
115 // TODO: also do the same thing for timeouts.
116 fn handle_iq(&self, iq: &Iq) -> Propagation {
117 let iq = iq.clone();
118 if let IqType::Error(_) = iq.payload {
119 let from = iq.from.unwrap();
120 let mut pending = self.pending.lock().unwrap();
121 let previous = pending.remove(&from.clone());
122 if previous.is_none() {
123 // This wasn’t one of our requests.
124 return Propagation::Continue;
125 }
126 // TODO: maybe add a negative cache?
127 return Propagation::Stop;
128 }
129 Propagation::Continue
130 }
131}
132
133impl_plugin!(CapsPlugin, proxy, [
134 (Presence, Priority::Default) => handle_presence,
135 (Iq, Priority::Default) => handle_iq,
136 (DiscoInfoResult, Priority::Default) => handle_result,
137]);