1use std::collections::HashMap;
2use std::convert::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::Disco;
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), Disco>>>,
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 = Disco {
63 node: Some(node),
64 identities: vec!(),
65 features: vec!(),
66 extensions: vec!(),
67 };
68 self.proxy.send(Iq {
69 to: Some(recipient),
70 from: None,
71 id: Some(id),
72 payload: IqType::Get(disco.into()),
73 }.into());
74 break;
75 },
76 PresenceType::Unavailable
77 | PresenceType::Error => {
78 let recipient = presence.from.unwrap();
79 let mut pending = self.pending.lock().unwrap();
80 let previous = pending.remove(&recipient);
81 if previous.is_none() {
82 // This wasn’t one of our requests.
83 return Propagation::Continue;
84 }
85 // TODO: maybe add a negative cache?
86 },
87 _ => (),
88 }
89 Propagation::Continue
90 }
91
92 fn handle_result(&self, result: &DiscoInfoResult) -> Propagation {
93 let from = result.from.clone();
94 let mut pending = self.pending.lock().unwrap();
95 let previous = pending.remove(&from.clone());
96 if let Some((id, node)) = previous {
97 if id != result.id {
98 return Propagation::Continue;
99 }
100 if Some(node.clone()) != result.disco.node {
101 // TODO: make that a debug log.
102 println!("Wrong node in result!");
103 return Propagation::Continue;
104 }
105 {
106 let mut cache = self.cache.lock().unwrap();
107 cache.insert((from, node), result.disco.clone());
108 }
109 } else {
110 // TODO: make that a debug log.
111 println!("No such request from us.");
112 return Propagation::Continue;
113 }
114 Propagation::Stop
115 }
116
117 // This is only for errors.
118 // TODO: also do the same thing for timeouts.
119 fn handle_iq(&self, iq: &Iq) -> Propagation {
120 let iq = iq.clone();
121 if let IqType::Error(_) = iq.payload {
122 let from = iq.from.unwrap();
123 let mut pending = self.pending.lock().unwrap();
124 let previous = pending.remove(&from.clone());
125 if previous.is_none() {
126 // This wasn’t one of our requests.
127 return Propagation::Continue;
128 }
129 // TODO: maybe add a negative cache?
130 return Propagation::Stop;
131 }
132 Propagation::Continue
133 }
134}
135
136impl_plugin!(CapsPlugin, proxy, [
137 (Presence, Priority::Default) => handle_presence,
138 (Iq, Priority::Default) => handle_iq,
139 (DiscoInfoResult, Priority::Default) => handle_result,
140]);