From a3e6ef8c3ab1dc1214042dbe843fb0538dabaf1b Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 03:59:44 +0200 Subject: [PATCH] =?UTF-8?q?Move=20from=20XEP-0048=20to=20Bookmarks=C2=A02?= =?UTF-8?q?=20(This=20Time=20it=E2=80=99s=20Serious).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/hello_bot.rs | 12 +++-- src/lib.rs | 62 +++++++--------------- src/{ => pubsub}/avatar.rs | 0 src/pubsub/mod.rs | 106 +++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 47 deletions(-) rename src/{ => pubsub}/avatar.rs (100%) create mode 100644 src/pubsub/mod.rs diff --git a/examples/hello_bot.rs b/examples/hello_bot.rs index dcfd060d..7d13ba0a 100644 --- a/examples/hello_bot.rs +++ b/examples/hello_bot.rs @@ -54,9 +54,15 @@ fn main() { Event::ContactChanged(contact) => { println!("Contact {} changed.", contact.jid); }, - Event::OpenRoomBookmark(bookmark) => { - println!("Joining room “{}” ({})…", bookmark.name, bookmark.jid); - agent.join_room(bookmark.jid, bookmark.nick, bookmark.password, "en", "Yet another bot!"); + Event::JoinRoom(jid, conference) => { + println!("Joining room {} ({:?})…", jid, conference.name); + agent.join_room(jid, conference.nick, conference.password, "en", "Yet another bot!"); + }, + Event::LeaveRoom(jid) => { + println!("Leaving room {}…", jid); + }, + Event::LeaveAllRooms => { + println!("Leaving all rooms…"); }, Event::RoomJoined(jid) => { println!("Joined room {}.", jid); diff --git a/src/lib.rs b/src/lib.rs index 7731db56..521c53f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,11 +17,7 @@ use tokio_xmpp::{ Packet, }; use xmpp_parsers::{ - bookmarks::{ - Autojoin, - Conference as ConferenceBookmark, - Storage as Bookmarks, - }, + bookmarks2::Conference, caps::{compute_disco, hash_caps, Caps}, disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}, hashes::Algo, @@ -33,16 +29,13 @@ use xmpp_parsers::{ }, ns, presence::{Presence, Type as PresenceType}, - pubsub::{ - event::PubSubEvent, - pubsub::PubSub, - }, + pubsub::pubsub::{PubSub, Items}, roster::{Roster, Item as RosterItem}, stanza_error::{StanzaError, ErrorType, DefinedCondition}, Jid, BareJid, FullJid, JidParseError, }; -mod avatar; +mod pubsub; pub type Error = tokio_xmpp::Error; @@ -84,7 +77,9 @@ pub enum Event { ContactRemoved(RosterItem), ContactChanged(RosterItem), AvatarRetrieved(Jid, String), - OpenRoomBookmark(ConferenceBookmark), + JoinRoom(BareJid, Conference), + LeaveRoom(BareJid), + LeaveAllRooms, RoomJoined(BareJid), RoomLeft(BareJid), } @@ -141,7 +136,7 @@ impl ClientBuilder<'_> { features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA))); } if self.features.contains(&ClientFeature::JoinRooms) { - features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS))); + features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2))); } DiscoInfoResult { node: None, @@ -209,6 +204,10 @@ impl ClientBuilder<'_> { let iq = Iq::from_get("roster", Roster { ver: None, items: vec![] }) .into(); sender_tx.unbounded_send(Packet::Stanza(iq)).unwrap(); + // TODO: only send this when the JoinRooms feature is enabled. + let iq = Iq::from_get("bookmarks", PubSub::Items(Items::new(ns::BOOKMARKS2))) + .into(); + sender_tx.unbounded_send(Packet::Stanza(iq)).unwrap(); } TokioXmppEvent::Disconnected => { events.push(Event::Disconnected); @@ -216,6 +215,8 @@ impl ClientBuilder<'_> { TokioXmppEvent::Stanza(stanza) => { if stanza.is("iq", "jabber:client") { let iq = Iq::try_from(stanza).unwrap(); + let from = + iq.from.clone().unwrap_or_else(|| Jid::from_str(&jid).unwrap()); if let IqType::Get(payload) = iq.payload { if payload.is("query", ns::DISCO_INFO) { let query = DiscoInfoQuery::try_from(payload); @@ -245,15 +246,8 @@ impl ClientBuilder<'_> { events.push(Event::ContactAdded(item)); } } else if payload.is("pubsub", ns::PUBSUB) { - let pubsub = PubSub::try_from(payload).unwrap(); - let from = - iq.from.clone().unwrap_or_else(|| Jid::from_str(&jid).unwrap()); - if let PubSub::Items(items) = pubsub { - if items.node.0 == ns::AVATAR_DATA { - let new_events = avatar::handle_data_pubsub_iq(&from, &items); - events.extend(new_events); - } - } + let new_events = pubsub::handle_iq_result(&from, payload); + events.extend(new_events); } } else if let IqType::Set(_) = iq.payload { // We MUST answer unhandled set iqs with a service-unavailable error. @@ -264,28 +258,8 @@ impl ClientBuilder<'_> { let from = message.from.clone().unwrap(); for child in message.payloads { if child.is("event", ns::PUBSUB_EVENT) { - let event = PubSubEvent::try_from(child).unwrap(); - if let PubSubEvent::PublishedItems { node, items } = event { - if node.0 == ns::AVATAR_METADATA { - let new_events = avatar::handle_metadata_pubsub_event(&from, &mut sender_tx, items); - events.extend(new_events); - } else if node.0 == ns::BOOKMARKS { - // TODO: Check that our bare JID is the sender. - assert_eq!(items.len(), 1); - let item = items.clone().pop().unwrap(); - let payload = item.payload.clone().unwrap(); - let bookmarks = match Bookmarks::try_from(payload) { - Ok(bookmarks) => bookmarks, - // XXX: Don’t panic… - Err(err) => panic!("… {}", err), - }; - for bookmark in bookmarks.conferences { - if bookmark.autojoin == Autojoin::True { - events.push(Event::OpenRoomBookmark(bookmark)); - } - } - } - } + let new_events = pubsub::handle_event(&from, child, &mut sender_tx); + events.extend(new_events); } } } else if stanza.is("presence", "jabber:client") { @@ -362,7 +336,7 @@ impl Agent { let nick = nick.unwrap_or_else(|| self.default_nick.borrow().clone()); let room_jid = room.with_resource(nick); let mut presence = Presence::new(PresenceType::None) - .with_to(Some(Jid::Full(room_jid))); + .with_to(Jid::Full(room_jid)); presence.add_payload(muc); presence.set_status(String::from(lang), String::from(status)); let presence = presence.into(); diff --git a/src/avatar.rs b/src/pubsub/avatar.rs similarity index 100% rename from src/avatar.rs rename to src/pubsub/avatar.rs diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs new file mode 100644 index 00000000..683179d3 --- /dev/null +++ b/src/pubsub/mod.rs @@ -0,0 +1,106 @@ +// Copyright (c) 2019 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::Event; +use futures::sync::mpsc; +use std::convert::TryFrom; +use std::str::FromStr; +use tokio_xmpp::Packet; +use xmpp_parsers::{ + Jid, BareJid, + Element, + ns, + bookmarks2::{ + Autojoin, + Conference, + }, + pubsub::event::PubSubEvent, + pubsub::pubsub::PubSub, +}; + +pub(crate) mod avatar; + +pub(crate) fn handle_event(from: &Jid, elem: Element, mut tx: &mut mpsc::UnboundedSender) -> impl IntoIterator { + let mut events = Vec::new(); + match PubSubEvent::try_from(elem) { + Ok(PubSubEvent::PublishedItems { node, items }) => { + match node.0 { + node if node == ns::AVATAR_METADATA => { + let new_events = avatar::handle_metadata_pubsub_event(&from, &mut tx, items); + events.extend(new_events); + }, + node if node == ns::BOOKMARKS2 => { + // TODO: Check that our bare JID is the sender. + assert_eq!(items.len(), 1); + let item = items.clone().pop().unwrap(); + let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap(); + let payload = item.payload.clone().unwrap(); + match Conference::try_from(payload) { + Ok(conference) => { + if conference.autojoin == Autojoin::True { + events.push(Event::JoinRoom(jid, conference)); + } else { + events.push(Event::LeaveRoom(jid)); + } + }, + Err(err) => println!("not bookmark: {}", err) + } + }, + node => unimplemented!("node {}", node) + } + } + Ok(PubSubEvent::RetractedItems { node, items }) => { + match node.0 { + node if node == ns::BOOKMARKS2 => { + // TODO: Check that our bare JID is the sender. + assert_eq!(items.len(), 1); + let item = items.clone().pop().unwrap(); + let jid = BareJid::from_str(&item.0).unwrap(); + events.push(Event::LeaveRoom(jid)); + }, + node => unimplemented!("node {}", node) + } + } + Ok(PubSubEvent::Purge { node }) => { + match node.0 { + node if node == ns::BOOKMARKS2 => { + // TODO: Check that our bare JID is the sender. + events.push(Event::LeaveAllRooms); + }, + node => unimplemented!("node {}", node) + } + } + _ => unimplemented!() + } + events +} + +pub(crate) fn handle_iq_result(from: &Jid, elem: Element) -> impl IntoIterator { + let mut events = Vec::new(); + let pubsub = PubSub::try_from(elem).unwrap(); + if let PubSub::Items(items) = pubsub { + if items.node.0 == ns::AVATAR_DATA { + let new_events = avatar::handle_data_pubsub_iq(&from, &items); + events.extend(new_events); + } else if items.node.0 == ns::BOOKMARKS2 { + events.push(Event::LeaveAllRooms); + for item in items.items { + let item = item.0; + let jid = BareJid::from_str(&item.id.clone().unwrap().0).unwrap(); + let payload = item.payload.clone().unwrap(); + match Conference::try_from(payload) { + Ok(conference) => { + if let Autojoin::True = conference.autojoin { + events.push(Event::JoinRoom(jid, conference)); + } + }, + Err(err) => panic!("Wrong payload type in bookmarks 2 item: {}", err), + } + } + } + } + events +}