caps.rs

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