Broadcast presence updates

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2022-09-28 18:07:25 +02:00
parent 222e5a1793
commit d7c4bcd87b
5 changed files with 99 additions and 23 deletions

View file

@ -25,6 +25,7 @@ pub enum Error {
NickAlreadyAssigned(String),
NonexistantSession(FullJid),
SessionAlreadyExists(FullJid),
ParticipantNotFound(String),
Xmpp(Box<TokioXMPPError>),
Parser(Box<ParserError>),
}
@ -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),
}

View file

@ -195,11 +195,7 @@ async fn handle_presence<C: ComponentTrait>(
_ => 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<C: ComponentTrait>(
// 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);

View file

@ -286,6 +286,22 @@ impl Room {
Ok(())
}
pub async fn update_presence<C: ComponentTrait>(
&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<C: ComponentTrait>(
&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<J: Into<Jid>>(&self, _jid: &J) -> bool {
true
}

View file

@ -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<BareJid, Room> = 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();
}

View file

@ -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
}