disco.rs

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