1use try_from::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;
10use xmpp_parsers::disco::{DiscoInfoQuery, DiscoInfoResult as DiscoInfoResult_, 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
21#[derive(Debug)]
22pub struct DiscoInfoResult {
23 pub from: Jid,
24 pub id: String,
25 pub disco: DiscoInfoResult_,
26}
27
28impl Event for DiscoInfoRequest {}
29impl Event for DiscoInfoResult {}
30
31pub struct DiscoPlugin {
32 proxy: PluginProxy,
33 cached_disco: Arc<Mutex<DiscoInfoResult_>>,
34}
35
36impl DiscoPlugin {
37 pub fn new(category: &str, type_: &str, lang: &str, name: &str) -> DiscoPlugin {
38 DiscoPlugin {
39 proxy: PluginProxy::new(),
40 cached_disco: Arc::new(Mutex::new(DiscoInfoResult_ {
41 node: None,
42 identities: vec!(Identity {
43 category: category.to_owned(),
44 type_: type_.to_owned(),
45 lang: Some(lang.to_owned()),
46 name: Some(name.to_owned())
47 }),
48 features: vec!(Feature { var: String::from(ns::DISCO_INFO) }),
49 extensions: vec!(),
50 })),
51 }
52 }
53
54 pub fn add_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
55 let mut cached_disco = self.cached_disco.lock().unwrap();
56 cached_disco.identities.push(Identity {
57 category: category.to_owned(),
58 type_: type_.to_owned(),
59 lang: lang.and_then(|lang| Some(lang.to_owned())),
60 name: name.and_then(|name| Some(name.to_owned())),
61 });
62 }
63
64 pub fn remove_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
65 let mut cached_disco = self.cached_disco.lock().unwrap();
66 cached_disco.identities.retain(|identity| {
67 identity.category != category ||
68 identity.type_ != type_ ||
69 identity.lang != lang.and_then(|lang| Some(lang.to_owned())) ||
70 identity.name != name.and_then(|name| Some(name.to_owned()))
71 });
72 }
73
74 pub fn add_feature(&self, var: &str) {
75 let mut cached_disco = self.cached_disco.lock().unwrap();
76 cached_disco.features.push(Feature { var: String::from(var) });
77 }
78
79 pub fn remove_feature(&self, var: &str) {
80 let mut cached_disco = self.cached_disco.lock().unwrap();
81 cached_disco.features.retain(|feature| feature.var != var);
82 }
83
84 pub fn add_extension(&self, extension: DataForm) {
85 let mut cached_disco = self.cached_disco.lock().unwrap();
86 cached_disco.extensions.push(extension);
87 }
88
89 pub fn remove_extension(&self, form_type: &str) {
90 let mut cached_disco = self.cached_disco.lock().unwrap();
91 cached_disco.extensions.retain(|extension| {
92 extension.form_type != Some(form_type.to_owned())
93 });
94 }
95
96 fn handle_iq(&self, iq: &Iq) -> Propagation {
97 let iq = iq.clone();
98 if let IqType::Get(payload) = iq.payload {
99 if let Ok(disco) = DiscoInfoQuery::try_from(payload) {
100 self.proxy.dispatch(DiscoInfoRequest {
101 from: iq.from.unwrap(),
102 id: iq.id.unwrap(),
103 node: disco.node,
104 });
105 return Propagation::Stop;
106 }
107 } else if let IqType::Result(Some(payload)) = iq.payload {
108 if let Ok(disco) = DiscoInfoResult_::try_from(payload) {
109 self.proxy.dispatch(DiscoInfoResult {
110 from: iq.from.unwrap(),
111 id: iq.id.unwrap(),
112 disco: disco,
113 });
114 return Propagation::Stop;
115 }
116 }
117 Propagation::Continue
118 }
119
120 fn reply_disco_info(&self, request: &DiscoInfoRequest) -> Propagation {
121 let payload = if request.node.is_none() {
122 let cached_disco = self.cached_disco.lock().unwrap().clone();
123 IqType::Result(Some(cached_disco.into()))
124 } else {
125 // TODO: handle the requests on nodes too.
126 return Propagation::Continue;
127 };
128 self.proxy.send(Iq {
129 from: None,
130 to: Some(request.from.to_owned()),
131 id: Some(request.id.to_owned()),
132 payload,
133 }.into());
134 Propagation::Stop
135 }
136}
137
138impl_plugin!(DiscoPlugin, proxy, [
139 (Iq, Priority::Default) => handle_iq,
140 (DiscoInfoRequest, Priority::Default) => reply_disco_info,
141]);