disco.rs

  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]);