1use std::convert::TryFrom;
2use std::sync::{Mutex, Arc};
3
4use plugin::PluginProxy;
5use event::{Event, Priority, Propagation};
6use jid::Jid;
7
8use plugins::stanza::Iq;
9use xmpp_parsers::iq::{IqType, IqPayload};
10use xmpp_parsers::disco::{Disco, Identity, Feature};
11use xmpp_parsers::data_forms::DataForm;
12use xmpp_parsers::ns;
13
14#[derive(Debug)]
15pub struct DiscoInfoRequest {
16 pub from: Jid,
17 pub id: String,
18 pub node: Option<String>,
19}
20
21impl Event for DiscoInfoRequest {}
22
23pub struct DiscoPlugin {
24 proxy: PluginProxy,
25 cached_disco: Arc<Mutex<Disco>>,
26}
27
28impl DiscoPlugin {
29 pub fn new(category: &str, type_: &str, lang: &str, name: &str) -> DiscoPlugin {
30 DiscoPlugin {
31 proxy: PluginProxy::new(),
32 cached_disco: Arc::new(Mutex::new(Disco {
33 node: None,
34 identities: vec!(Identity {
35 category: category.to_owned(),
36 type_: type_.to_owned(),
37 lang: Some(lang.to_owned()),
38 name: Some(name.to_owned())
39 }),
40 features: vec!(Feature { var: String::from(ns::DISCO_INFO) }),
41 extensions: vec!(),
42 })),
43 }
44 }
45
46 pub fn add_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
47 let mut cached_disco = self.cached_disco.lock().unwrap();
48 cached_disco.identities.push(Identity {
49 category: category.to_owned(),
50 type_: type_.to_owned(),
51 lang: lang.and_then(|lang| Some(lang.to_owned())),
52 name: name.and_then(|name| Some(name.to_owned())),
53 });
54 }
55
56 pub fn remove_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
57 let mut cached_disco = self.cached_disco.lock().unwrap();
58 cached_disco.identities.retain(|identity| {
59 identity.category != category ||
60 identity.type_ != type_ ||
61 identity.lang != lang.and_then(|lang| Some(lang.to_owned())) ||
62 identity.name != name.and_then(|name| Some(name.to_owned()))
63 });
64 }
65
66 pub fn add_feature(&self, var: &str) {
67 let mut cached_disco = self.cached_disco.lock().unwrap();
68 cached_disco.features.push(Feature { var: String::from(var) });
69 }
70
71 pub fn remove_feature(&self, var: &str) {
72 let mut cached_disco = self.cached_disco.lock().unwrap();
73 cached_disco.features.retain(|feature| feature.var != var);
74 }
75
76 pub fn add_extension(&self, extension: DataForm) {
77 let mut cached_disco = self.cached_disco.lock().unwrap();
78 cached_disco.extensions.push(extension);
79 }
80
81 pub fn remove_extension(&self, form_type: &str) {
82 let mut cached_disco = self.cached_disco.lock().unwrap();
83 cached_disco.extensions.retain(|extension| {
84 extension.form_type != Some(form_type.to_owned())
85 });
86 }
87
88 fn handle_iq(&self, iq: &Iq) -> Propagation {
89 let iq = iq.clone();
90 if let IqType::Get(payload) = iq.payload {
91 // TODO: use an intermediate plugin to parse this payload.
92 if let Ok(IqPayload::Disco(disco)) = IqPayload::try_from(payload) {
93 self.proxy.dispatch(DiscoInfoRequest { // TODO: safety!!!
94 from: iq.from.unwrap(),
95 id: iq.id.unwrap(),
96 node: disco.node,
97 });
98 return Propagation::Stop;
99 }
100 }
101 Propagation::Continue
102 }
103
104 fn reply_disco_info(&self, request: &DiscoInfoRequest) -> Propagation {
105 let payload = if request.node.is_none() {
106 let cached_disco = self.cached_disco.lock().unwrap().clone();
107 IqType::Result(Some(cached_disco.into()))
108 } else {
109 // TODO: handle the requests on nodes too.
110 return Propagation::Continue;
111 };
112 self.proxy.send(Iq {
113 from: None,
114 to: Some(request.from.to_owned()),
115 id: Some(request.id.to_owned()),
116 payload,
117 }.into());
118 Propagation::Stop
119 }
120}
121
122impl_plugin!(DiscoPlugin, proxy, [
123 (Iq, Priority::Default) => handle_iq,
124 (DiscoInfoRequest, Priority::Default) => reply_disco_info,
125]);