@@ -6,6 +6,7 @@ use xmpp::plugins::stanza::StanzaPlugin;
use xmpp::plugins::unhandled_iq::UnhandledIqPlugin;
use xmpp::plugins::messaging::{MessagingPlugin, MessageEvent};
use xmpp::plugins::presence::{PresencePlugin, Type};
+use xmpp::plugins::disco::DiscoPlugin;
use xmpp::plugins::ibb::IbbPlugin;
use xmpp::plugins::ping::PingPlugin;
use xmpp::event::{Priority, Propagation};
@@ -23,8 +24,11 @@ fn main() {
client.register_plugin(UnhandledIqPlugin::new());
client.register_plugin(MessagingPlugin::new());
client.register_plugin(PresencePlugin::new());
+ client.register_plugin(DiscoPlugin::new("client", "bot", "en", "xmpp-rs"));
client.register_plugin(IbbPlugin::new());
client.register_plugin(PingPlugin::new());
+ client.plugin::<PingPlugin>().init();
+ client.plugin::<IbbPlugin>().init();
client.register_handler(Priority::Max, |e: &MessageEvent| {
println!("{:?}", e);
Propagation::Continue
@@ -0,0 +1,125 @@
+use std::convert::TryFrom;
+use std::sync::{Mutex, Arc};
+
+use plugin::PluginProxy;
+use event::{Event, Priority, Propagation};
+use jid::Jid;
+
+use plugins::stanza::Iq;
+use xmpp_parsers::iq::{IqType, IqPayload};
+use xmpp_parsers::disco::{Disco, Identity, Feature};
+use xmpp_parsers::data_forms::DataForm;
+use xmpp_parsers::ns;
+
+#[derive(Debug)]
+pub struct DiscoInfoRequest {
+ pub from: Jid,
+ pub id: String,
+ pub node: Option<String>,
+}
+
+impl Event for DiscoInfoRequest {}
+
+pub struct DiscoPlugin {
+ proxy: PluginProxy,
+ cached_disco: Arc<Mutex<Disco>>,
+}
+
+impl DiscoPlugin {
+ pub fn new(category: &str, type_: &str, lang: &str, name: &str) -> DiscoPlugin {
+ DiscoPlugin {
+ proxy: PluginProxy::new(),
+ cached_disco: Arc::new(Mutex::new(Disco {
+ node: None,
+ identities: vec!(Identity {
+ category: category.to_owned(),
+ type_: type_.to_owned(),
+ lang: Some(lang.to_owned()),
+ name: Some(name.to_owned())
+ }),
+ features: vec!(Feature { var: String::from(ns::DISCO_INFO) }),
+ extensions: vec!(),
+ })),
+ }
+ }
+
+ pub fn add_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.identities.push(Identity {
+ category: category.to_owned(),
+ type_: type_.to_owned(),
+ lang: lang.and_then(|lang| Some(lang.to_owned())),
+ name: name.and_then(|name| Some(name.to_owned())),
+ });
+ }
+
+ pub fn remove_identity(&self, category: &str, type_: &str, lang: Option<&str>, name: Option<&str>) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.identities.retain(|identity| {
+ identity.category != category ||
+ identity.type_ != type_ ||
+ identity.lang != lang.and_then(|lang| Some(lang.to_owned())) ||
+ identity.name != name.and_then(|name| Some(name.to_owned()))
+ });
+ }
+
+ pub fn add_feature(&self, var: &str) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.features.push(Feature { var: String::from(var) });
+ }
+
+ pub fn remove_feature(&self, var: &str) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.features.retain(|feature| feature.var != var);
+ }
+
+ pub fn add_extension(&self, extension: DataForm) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.extensions.push(extension);
+ }
+
+ pub fn remove_extension(&self, form_type: &str) {
+ let mut cached_disco = self.cached_disco.lock().unwrap();
+ cached_disco.extensions.retain(|extension| {
+ extension.form_type != Some(form_type.to_owned())
+ });
+ }
+
+ fn handle_iq(&self, iq: &Iq) -> Propagation {
+ let iq = iq.clone();
+ if let IqType::Get(payload) = iq.payload {
+ // TODO: use an intermediate plugin to parse this payload.
+ if let Ok(IqPayload::Disco(disco)) = IqPayload::try_from(payload) {
+ self.proxy.dispatch(DiscoInfoRequest { // TODO: safety!!!
+ from: iq.from.unwrap(),
+ id: iq.id.unwrap(),
+ node: disco.node,
+ });
+ return Propagation::Stop;
+ }
+ }
+ Propagation::Continue
+ }
+
+ fn reply_disco_info(&self, request: &DiscoInfoRequest) -> Propagation {
+ let payload = if request.node.is_none() {
+ let cached_disco = self.cached_disco.lock().unwrap().clone();
+ IqType::Result(Some(cached_disco.into()))
+ } else {
+ // TODO: handle the requests on nodes too.
+ return Propagation::Continue;
+ };
+ self.proxy.send(Iq {
+ from: None,
+ to: Some(request.from.to_owned()),
+ id: Some(request.id.to_owned()),
+ payload,
+ }.into());
+ Propagation::Stop
+ }
+}
+
+impl_plugin!(DiscoPlugin, proxy, [
+ (Iq, Priority::Default) => handle_iq,
+ (DiscoInfoRequest, Priority::Default) => reply_disco_info,
+]);
@@ -8,9 +8,11 @@ use event::{Event, Priority, Propagation};
use jid::Jid;
use plugins::stanza::Iq;
+use plugins::disco::DiscoPlugin;
use xmpp_parsers::iq::{IqType, IqPayload};
use xmpp_parsers::ibb::{IBB, Stanza};
use xmpp_parsers::stanza_error::{StanzaError, ErrorType, DefinedCondition};
+use xmpp_parsers::ns;
#[derive(Debug, Clone)]
pub struct Session {
@@ -66,6 +68,24 @@ impl IbbPlugin {
}
}
+ // TODO: make that called automatically after plugins are created.
+ pub fn init(&self) {
+ if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
+ disco.add_feature(ns::IBB);
+ } else {
+ panic!("Please handle dependencies in the correct order.");
+ }
+ }
+
+ // TODO: make that called automatically before removal.
+ pub fn deinit(&self) {
+ if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
+ disco.remove_feature(ns::IBB);
+ } else {
+ panic!("Please handle dependencies in the correct order.");
+ }
+ }
+
fn handle_ibb(&self, from: Jid, ibb: IBB) -> Result<(), StanzaError> {
let mut sessions = self.sessions.lock().unwrap();
match ibb {
@@ -6,8 +6,10 @@ use error::Error;
use jid::Jid;
use plugins::stanza::Iq;
+use plugins::disco::DiscoPlugin;
use xmpp_parsers::iq::{IqType, IqPayload};
use xmpp_parsers::ping::Ping;
+use xmpp_parsers::ns;
#[derive(Debug)]
pub struct PingEvent {
@@ -28,6 +30,24 @@ impl PingPlugin {
}
}
+ // TODO: make that called automatically after plugins are created.
+ pub fn init(&self) {
+ if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
+ disco.add_feature(ns::PING);
+ } else {
+ panic!("Please handle dependencies in the correct order.");
+ }
+ }
+
+ // TODO: make that called automatically before removal.
+ pub fn deinit(&self) {
+ if let Some(disco) = self.proxy.plugin::<DiscoPlugin>() {
+ disco.remove_feature(ns::PING);
+ } else {
+ panic!("Please handle dependencies in the correct order.");
+ }
+ }
+
pub fn send_ping(&self, to: &Jid) -> Result<(), Error> {
let to = to.clone();
self.proxy.send(Iq {