diff --git a/src/handlers.rs b/src/handlers.rs index 3deff3c..d7b07fa 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -28,7 +28,7 @@ use xmpp_parsers::{ message::Message, muc::Muc, ns, - presence::Presence, + presence::{Presence, Type as PresenceType}, stanza_error::{DefinedCondition, ErrorType, StanzaError}, BareJid, Element, Jid, }; @@ -128,7 +128,24 @@ async fn handle_presence( // Room already exists if let Some(room) = rooms.get_mut(&roomjid) { debug!("Presence received to existing room: {}", &roomjid); - room.add_session(component, realjid, nick).await.unwrap(); + 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 { debug!("Presence received to new room: {}", &roomjid); let mut room = Room::new(roomjid.clone()); diff --git a/src/room.rs b/src/room.rs index ecc6517..84b00ce 100644 --- a/src/room.rs +++ b/src/room.rs @@ -83,7 +83,7 @@ impl Room { // Add into occupants let _ = self .occupants - .insert(bare.clone(), Occupant::new(realjid.clone())); + .insert(bare.clone(), Occupant::new(realjid.clone(), nick.clone())); // Self-presence debug!("Sending self-presence for {}", realjid); @@ -125,10 +125,10 @@ pub struct Occupant { } impl Occupant { - fn new(fulljid: FullJid) -> Occupant { + fn new(fulljid: FullJid, nick: Nick) -> Occupant { Occupant { jid: BareJid::from(fulljid.clone()), - nick: fulljid.resource.clone(), + nick, sessions: vec![fulljid], } } @@ -166,7 +166,7 @@ mod tests { #[test] fn occupant_ignore_dup_session() { let fulljid = FullJid::from_str("foo@bar/meh").unwrap(); - let mut occupant = Occupant::new(fulljid.clone()); + let mut occupant = Occupant::new(fulljid.clone(), String::from("nick")); occupant.add_session(fulljid.clone()).unwrap(); assert_eq!(occupant.iter().count(), 1); } diff --git a/src/tests.rs b/src/tests.rs index e558afd..f549615 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -146,3 +146,58 @@ async fn test_0045_join_presence_empty_room() { None => panic!(), } } + +#[tokio::test] +async fn test_0045_join_presence_nick_already_assigned() { + let nick = "nick"; + + let realjid1 = FullJid::from_str("foo@bar/qxx").unwrap(); + let realjid2 = FullJid::from_str("qxx@bar/foo").unwrap(); + let participant = COMPONENT_JID + .clone() + .with_node("room") + .with_resource(nick.clone()); + let roomjid = COMPONENT_JID.clone().with_node("room"); + + // + let join1: Element = Presence::new(PresenceType::None) + .with_from(Jid::Full(realjid1.clone())) + .with_to(Jid::Full(participant.clone())) + .with_payloads(vec![Muc::new().into()]) + .into(); + + let join2: Element = Presence::new(PresenceType::None) + .with_from(Jid::Full(realjid2.clone())) + .with_to(Jid::Full(participant.clone())) // Same participant JID + .with_payloads(vec![Muc::new().into()]) + .into(); + + let mut component = TestComponent::new(vec![join1, join2]); + let mut rooms: HashMap = HashMap::new(); + + // Ignore self-presence for first participant + component.expect_with(|_| ()); + // Ignore message subject for first participant + component.expect_with(|_| ()); + + component.expect( + Presence::new(PresenceType::Error) + .with_from(participant) + .with_to(realjid2) + .with_payloads(vec![StanzaError::new( + ErrorType::Cancel, + DefinedCondition::Conflict, + "en", + format!("Nickname conflict: {}", nick), + ) + .into()]), + ); + + handle_stanza(&mut component, &mut rooms).await.unwrap(); + component.assert(); + + match rooms.get(&roomjid) { + Some(_) => (), + None => panic!(), + } +}