From d7c4bcd87bb5e45df7185b37c9aa02baf0874ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 28 Sep 2022 18:07:25 +0200 Subject: [PATCH] Broadcast presence updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- src/error.rs | 2 ++ src/handlers.rs | 46 +++++++++++++++++++++++------------------- src/room.rs | 30 +++++++++++++++++++++++++++ src/tests/presence.rs | 31 ++++++++++++++++++++++++++-- src/tests/templates.rs | 13 ++++++++++++ 5 files changed, 99 insertions(+), 23 deletions(-) diff --git a/src/error.rs b/src/error.rs index c64403b..e5883c9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,6 +25,7 @@ pub enum Error { NickAlreadyAssigned(String), NonexistantSession(FullJid), SessionAlreadyExists(FullJid), + ParticipantNotFound(String), Xmpp(Box), Parser(Box), } @@ -38,6 +39,7 @@ impl fmt::Display for Error { Error::NickAlreadyAssigned(err) => write!(f, "Nickname already assigned: {}", err), Error::NonexistantSession(err) => write!(f, "Session doesn't exist: {}", err), Error::SessionAlreadyExists(err) => write!(f, "Session already exist: {}", err), + Error::ParticipantNotFound(err) => write!(f, "Participant not found: {}", err), Error::Xmpp(err) => write!(f, "XMPP error: {}", err), Error::Parser(err) => write!(f, "Parser error: {}", err), } diff --git a/src/handlers.rs b/src/handlers.rs index 06e3dcc..026bbeb 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -195,11 +195,7 @@ async fn handle_presence( _ => ControlFlow::Continue(()), }); - // TODO: Handle presence probes, remove the return here. // TODO: check for features in the Muc element. - if let ControlFlow::Continue(_) = muc { - return Ok(()); - } // Presences to MUC come from resources not accounts if let Jid::Full(realjid) = presence.from.clone().unwrap() && @@ -211,23 +207,31 @@ async fn handle_presence( // Room already exists if let Some(room) = rooms.get_mut(&roomjid) { debug!("Presence received to existing room: {}", &roomjid); - match room.add_session(component, realjid.clone(), nick).await { - Ok(_) => (), - Err(Error::NickAlreadyAssigned(nick)) => { - let error = Presence::new(PresenceType::Error) - .with_from(participant) - .with_to(realjid) - .with_payloads(vec![ - StanzaError::new( - ErrorType::Cancel, - DefinedCondition::Conflict, - "en", - format!("Nickname conflict: {}", nick), - ).into() - ]); - component.send_stanza(error).await?; - }, - err => err.unwrap(), + if let ControlFlow::Break(_) = muc { + match room.add_session(component, realjid.clone(), nick).await { + Ok(_) => (), + Err(Error::NickAlreadyAssigned(nick)) => { + let error = Presence::new(PresenceType::Error) + .with_from(participant) + .with_to(realjid) + .with_payloads(vec![ + StanzaError::new( + ErrorType::Cancel, + DefinedCondition::Conflict, + "en", + format!("Nickname conflict: {}", nick), + ).into() + ]); + component.send_stanza(error).await?; + }, + err => err.unwrap(), + } + } else if let ControlFlow::Continue(_) = muc { + if room.is_joined(&realjid) { + room.update_presence(component, realjid.clone(), nick).await? + } else { + // TODO: We don't want to support GC1. Error + } } } else { debug!("Presence received to new room: {}", &roomjid); diff --git a/src/room.rs b/src/room.rs index 797d92a..481ddf2 100644 --- a/src/room.rs +++ b/src/room.rs @@ -286,6 +286,22 @@ impl Room { Ok(()) } + pub async fn update_presence( + &mut self, + component: &mut C, + realjid: Session, + nick: Nick, + ) -> Result<(), Error> { + self.broadcast_presence( + component, + self.get_occupant(&realjid, &nick)?, + &realjid, + BroadcastPresence::Update, + ) + .await?; + Ok(()) + } + pub async fn remove_session( &mut self, component: &mut C, @@ -305,6 +321,20 @@ impl Room { Ok(()) } + /// Looks for the occupant associated with the provided nick and ensure the session is part of + /// it. + pub fn get_occupant(&self, realjid: &Session, nick: &Nick) -> Result<&Occupant, Error> { + if let Some(occupant) = self.occupants.get(nick) { + if occupant.sessions.contains(realjid) { + Ok(occupant) + } else { + Err(Error::NonexistantSession(realjid.clone())) + } + } else { + Err(Error::ParticipantNotFound(nick.clone())) + } + } + pub fn is_joined>(&self, _jid: &J) -> bool { true } diff --git a/src/tests/presence.rs b/src/tests/presence.rs index fa9c49a..f6a0466 100644 --- a/src/tests/presence.rs +++ b/src/tests/presence.rs @@ -17,8 +17,8 @@ use crate::component::TestComponent; use crate::handlers::handle_stanza; use crate::room::Room; use crate::tests::templates::{ - LOUISE_FULL1, LOUISE_FULL2, LOUISE_NICK, LOUISE_ROOM1_PART, ROOM1_BARE, SUGAKO_FULL1, - SUGAKO_ROOM1_PART, + two_participant_room, LOUISE_FULL1, LOUISE_FULL2, LOUISE_NICK, LOUISE_ROOM1_PART, ROOM1_BARE, + SUGAKO_FULL1, SUGAKO_ROOM1_PART, }; use std::collections::{BTreeMap, HashMap}; @@ -513,3 +513,30 @@ async fn test_join_msn() { None => panic!(), } } + +#[tokio::test] +async fn test_presence_update_joined() { + let mut rooms: HashMap = HashMap::new(); + rooms.insert(ROOM1_BARE.clone(), two_participant_room(ROOM1_BARE.clone())); + + let update1: Element = Presence::new(PresenceType::None) + .with_from(Jid::Full(SUGAKO_FULL1.clone())) + .with_to(Jid::Full(SUGAKO_ROOM1_PART.clone())) + .with_show(PresenceShow::Away) + .into(); + + let mut component = TestComponent::new(vec![update1]); + + component.expect( + Presence::new(PresenceType::None) + .with_from(Jid::Full(SUGAKO_ROOM1_PART.clone())) + .with_to(Jid::Full(LOUISE_FULL1.clone())) + .with_payloads(vec![MucUser { + status: Vec::new(), + items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)], + } + .into()]), + ); + + handle_stanza(&mut component, &mut rooms).await.unwrap(); +} diff --git a/src/tests/templates.rs b/src/tests/templates.rs index f665768..24189d7 100644 --- a/src/tests/templates.rs +++ b/src/tests/templates.rs @@ -75,3 +75,16 @@ pub fn one_participant_room(roomjid: BareJid) -> Room { ); room } + +pub fn two_participant_room(roomjid: BareJid) -> Room { + let mut room = Room::new(roomjid.clone()); + room.occupants.insert( + String::from(LOUISE_NICK), + Occupant::new(&room, LOUISE_FULL1.clone(), String::from(LOUISE_NICK)), + ); + room.occupants.insert( + String::from(SUGAKO_NICK), + Occupant::new(&room, SUGAKO_FULL1.clone(), String::from(SUGAKO_NICK)), + ); + room +}