Occupant now takes a presence; new Session struct
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
43d2697aaa
commit
fc880c7423
6 changed files with 329 additions and 124 deletions
19
src/error.rs
19
src/error.rs
|
@ -13,11 +13,13 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::room::Session;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
|
||||
use tokio_xmpp::Error as TokioXMPPError;
|
||||
use xmpp_parsers::{Error as ParserError, FullJid, Jid, JidParseError};
|
||||
use xmpp_parsers::{Error as ParserError, Jid, JidParseError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -27,10 +29,15 @@ pub enum Error {
|
|||
NickAlreadyAssigned(String),
|
||||
/// Raised when editing or fetching an occupant with a session that isn't associated with the
|
||||
/// occupant.
|
||||
NonexistantSession(FullJid),
|
||||
SessionAlreadyExists(FullJid),
|
||||
NonexistantSession(Session),
|
||||
SessionAlreadyExists(Session),
|
||||
/// Raised when fetching an occupant with a nickname that isn't assigned in the room.
|
||||
ParticipantNotFound(String),
|
||||
/// Raised whenever an operation is applied to a session which isn't the primary session, used
|
||||
/// for Multi-Session Nick support.
|
||||
SecondarySession(Session),
|
||||
/// Raised when a JID was supposed to be present
|
||||
MissingJid,
|
||||
/// Jid Parse errors
|
||||
Jid(Box<JidParseError>),
|
||||
/// TokioXMPP errors
|
||||
|
@ -46,9 +53,11 @@ impl fmt::Display for Error {
|
|||
match self {
|
||||
Error::MismatchJids(jid1, jid2) => write!(f, "Mismatch Jids: {}, {}", jid1, jid2),
|
||||
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::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::SecondarySession(err) => write!(f, "Secondary session: {:?}", err),
|
||||
Error::MissingJid => write!(f, "Missing JID"),
|
||||
Error::Jid(err) => write!(f, "Jid Parse error: {}", err),
|
||||
Error::Xmpp(err) => write!(f, "XMPP error: {}", err),
|
||||
Error::Parser(err) => write!(f, "Parser error: {}", err),
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
use crate::component::ComponentTrait;
|
||||
use crate::error::Error;
|
||||
use crate::room::{Nick, Room};
|
||||
use crate::room::Room;
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::ops::ControlFlow;
|
||||
|
@ -189,6 +189,7 @@ async fn handle_presence<C: ComponentTrait>(
|
|||
let muc =
|
||||
presence
|
||||
.payloads
|
||||
.clone()
|
||||
.into_iter()
|
||||
.try_for_each(|payload| match Muc::try_from(payload) {
|
||||
Ok(muc) => ControlFlow::Break(muc),
|
||||
|
@ -202,13 +203,12 @@ async fn handle_presence<C: ComponentTrait>(
|
|||
let Jid::Full(participant) = presence.to.clone().unwrap() {
|
||||
|
||||
let roomjid = BareJid::from(participant.clone());
|
||||
let nick: Nick = participant.resource.clone();
|
||||
|
||||
// Room already exists
|
||||
if let Some(room) = rooms.get_mut(&roomjid) {
|
||||
debug!("Presence received to existing room: {}", &roomjid);
|
||||
if let ControlFlow::Break(_) = muc {
|
||||
match room.add_session(component, realjid.clone(), nick).await {
|
||||
match room.add_session(component, presence.clone()).await {
|
||||
Ok(_) => (),
|
||||
Err(Error::NickAlreadyAssigned(nick)) => {
|
||||
let error = Presence::new(PresenceType::Error)
|
||||
|
@ -228,7 +228,7 @@ async fn handle_presence<C: ComponentTrait>(
|
|||
}
|
||||
} else if let ControlFlow::Continue(_) = muc {
|
||||
if room.is_joined(&realjid) {
|
||||
room.update_presence(component, realjid.clone(), nick).await?
|
||||
room.update_presence(component, presence.clone()).await?
|
||||
} else {
|
||||
// TODO: We don't want to support GC1. Error
|
||||
}
|
||||
|
@ -236,13 +236,13 @@ async fn handle_presence<C: ComponentTrait>(
|
|||
} else {
|
||||
debug!("Presence received to new room: {}", &roomjid);
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
room.add_session(component, realjid, nick).await.unwrap();
|
||||
room.add_session(component, presence.clone()).await.unwrap();
|
||||
let _ = rooms.insert(roomjid, room);
|
||||
}
|
||||
}
|
||||
} else if presence.type_ == PresenceType::Unavailable &&
|
||||
let Jid::Full(realjid) = presence.from.unwrap() &&
|
||||
let Jid::Full(participant) = presence.to.unwrap() {
|
||||
let Jid::Full(realjid) = presence.from.clone().unwrap() &&
|
||||
let Jid::Full(participant) = presence.to.clone().unwrap() {
|
||||
|
||||
let roomjid = BareJid::from(participant.clone());
|
||||
|
||||
|
@ -258,7 +258,7 @@ async fn handle_presence<C: ComponentTrait>(
|
|||
).into()
|
||||
]);
|
||||
if let Some(mut room) = rooms.remove(&roomjid) {
|
||||
match room.remove_session(component, realjid, participant.resource.clone()).await {
|
||||
match room.remove_session(component, presence.clone()).await {
|
||||
Ok(()) => (),
|
||||
Err(Error::NonexistantSession(_)) => {
|
||||
component.send_stanza(error).await.unwrap();
|
||||
|
|
372
src/room.rs
372
src/room.rs
|
@ -34,7 +34,6 @@ use xmpp_parsers::{
|
|||
};
|
||||
|
||||
pub type Nick = String;
|
||||
type Session = FullJid;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BroadcastPresence {
|
||||
|
@ -71,20 +70,22 @@ impl Room {
|
|||
&self,
|
||||
component: &mut C,
|
||||
own_occupant: &Occupant,
|
||||
own_session: &Session,
|
||||
presence: Presence,
|
||||
mode: BroadcastPresence,
|
||||
) -> Result<(), Error> {
|
||||
let leave = matches!(mode, BroadcastPresence::Leave);
|
||||
let sync = matches!(mode, BroadcastPresence::Join | BroadcastPresence::Resync);
|
||||
let update = matches!(mode, BroadcastPresence::Join | BroadcastPresence::Update);
|
||||
|
||||
let own_session = Session::try_from(presence)?;
|
||||
|
||||
// All participants to new participant
|
||||
let presence_to_new = Presence::new(if leave {
|
||||
PresenceType::Unavailable
|
||||
} else {
|
||||
PresenceType::None
|
||||
})
|
||||
.with_to(own_session.clone())
|
||||
.with_to(Jid::Full(own_session.real.clone()))
|
||||
.with_payloads(vec![MucUser {
|
||||
status: Vec::new(),
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
|
||||
|
@ -97,7 +98,7 @@ impl Room {
|
|||
} else {
|
||||
PresenceType::None
|
||||
})
|
||||
.with_from(Jid::Full(own_occupant.participant.clone()))
|
||||
.with_from(Jid::Full(own_session.participant.clone()))
|
||||
.with_payloads(vec![MucUser {
|
||||
status: Vec::new(),
|
||||
items: vec![MucItem::new(
|
||||
|
@ -108,6 +109,7 @@ impl Room {
|
|||
.into()]);
|
||||
|
||||
for (_, other) in self.occupants.iter() {
|
||||
// Skip sending to all our sessions, we do it later.
|
||||
if own_occupant.nick == other.nick {
|
||||
continue;
|
||||
}
|
||||
|
@ -122,11 +124,13 @@ impl Room {
|
|||
// Send presence from participant to others.
|
||||
for session in other.iter() {
|
||||
// Skip sending if it's us.
|
||||
if session == own_session {
|
||||
if session.real == own_session.real {
|
||||
continue;
|
||||
}
|
||||
|
||||
let presence = presence_to_old.clone().with_to(Jid::Full(session.clone()));
|
||||
let presence = presence_to_old
|
||||
.clone()
|
||||
.with_to(Jid::Full(session.real.clone()));
|
||||
component.send_stanza(presence).await?;
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +142,7 @@ impl Room {
|
|||
.map(|session| MucItem {
|
||||
affiliation: Affiliation::Owner,
|
||||
role: if leave { Role::None } else { Role::Moderator },
|
||||
jid: Some(session.clone()),
|
||||
jid: Some(session.real.clone()),
|
||||
nick: None,
|
||||
actor: None,
|
||||
continue_: None,
|
||||
|
@ -156,12 +160,12 @@ impl Room {
|
|||
.with_from(Jid::Full(own_occupant.participant.clone()));
|
||||
|
||||
for session in own_occupant.iter() {
|
||||
if session == own_session {
|
||||
if session == &own_session {
|
||||
continue;
|
||||
}
|
||||
let presence = session_presence
|
||||
.clone()
|
||||
.with_to(Jid::Full(session.clone()))
|
||||
.with_to(Jid::Full(session.real.clone()))
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![],
|
||||
items: self_items.clone(),
|
||||
|
@ -179,18 +183,14 @@ impl Room {
|
|||
PresenceType::None
|
||||
})
|
||||
.with_from(Jid::Full(own_occupant.participant.clone()))
|
||||
.with_to(own_session.clone())
|
||||
.with_to(own_session.real.clone())
|
||||
.with_payloads(vec![MucUser {
|
||||
status: if leave {
|
||||
vec![MucStatus::SelfPresence]
|
||||
} else {
|
||||
vec![MucStatus::SelfPresence, MucStatus::AssignedNick]
|
||||
},
|
||||
items: if leave {
|
||||
vec![MucItem::new(Affiliation::Owner, Role::None)]
|
||||
} else {
|
||||
self_items
|
||||
},
|
||||
items: self_items,
|
||||
}
|
||||
.into()]);
|
||||
component.send_stanza(self_presence).await?;
|
||||
|
@ -202,7 +202,7 @@ impl Room {
|
|||
pub async fn send_subject<C: ComponentTrait>(
|
||||
&mut self,
|
||||
component: &mut C,
|
||||
realjid: Session,
|
||||
session: Session,
|
||||
occupant: Occupant,
|
||||
) -> Result<(), Error> {
|
||||
debug!("Sending subject!");
|
||||
|
@ -213,7 +213,7 @@ impl Room {
|
|||
self.subject = Some((subject, setter, stamp));
|
||||
}
|
||||
|
||||
let mut subject = Message::new(Some(Jid::Full(realjid)));
|
||||
let mut subject = Message::new(Some(Jid::Full(session.real)));
|
||||
subject.from = Some(Jid::Full(
|
||||
self.subject.as_ref().unwrap().1.participant.clone(),
|
||||
));
|
||||
|
@ -234,9 +234,12 @@ impl Room {
|
|||
pub async fn add_session<C: ComponentTrait>(
|
||||
&mut self,
|
||||
component: &mut C,
|
||||
realjid: Session,
|
||||
new_nick: Nick,
|
||||
presence: Presence,
|
||||
) -> Result<(), Error> {
|
||||
let new_session = Session::try_from(presence)?;
|
||||
let new_nick = new_session.participant.resource.clone();
|
||||
let realjid = new_session.real.clone();
|
||||
|
||||
// Ensure nick isn't already assigned
|
||||
self.occupants.iter().try_for_each(|(nick, occupant)| {
|
||||
let new_nick = new_nick.as_str();
|
||||
|
@ -248,7 +251,7 @@ impl Room {
|
|||
|
||||
let mode: Option<BroadcastPresence> = {
|
||||
if let Some(occupant) = self.occupants.get_mut(&new_nick) {
|
||||
match occupant.add_session(realjid.clone()) {
|
||||
match occupant.add_session(new_session.presence.clone()) {
|
||||
Ok(_) => Some(BroadcastPresence::Join),
|
||||
Err(Error::SessionAlreadyExists(_)) => Some(BroadcastPresence::Resync),
|
||||
Err(err) => return Err(err),
|
||||
|
@ -261,22 +264,32 @@ impl Room {
|
|||
if !self.occupants.contains_key(&new_nick) {
|
||||
let _ = self.occupants.insert(
|
||||
new_nick.clone(),
|
||||
Occupant::new(self, realjid.clone(), new_nick.clone()),
|
||||
Occupant::new(new_session.presence.clone())?,
|
||||
);
|
||||
}
|
||||
let occupant = self.occupants.get(&new_nick).unwrap();
|
||||
|
||||
match mode {
|
||||
Some(BroadcastPresence::Resync) => {
|
||||
self.broadcast_presence(component, occupant, &realjid, BroadcastPresence::Resync)
|
||||
self.broadcast_presence(
|
||||
component,
|
||||
&occupant,
|
||||
new_session.presence,
|
||||
BroadcastPresence::Resync,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Some(BroadcastPresence::Join) => {
|
||||
debug!("{} is joining {}", realjid, self.jid);
|
||||
|
||||
self.broadcast_presence(component, occupant, &realjid, BroadcastPresence::Join)
|
||||
self.broadcast_presence(
|
||||
component,
|
||||
&occupant,
|
||||
new_session.presence.clone(),
|
||||
BroadcastPresence::Join,
|
||||
)
|
||||
.await?;
|
||||
self.send_subject(component, realjid, occupant.clone())
|
||||
self.send_subject(component, new_session, occupant.clone())
|
||||
.await?;
|
||||
}
|
||||
_ => (),
|
||||
|
@ -288,13 +301,21 @@ impl Room {
|
|||
pub async fn update_presence<C: ComponentTrait>(
|
||||
&mut self,
|
||||
component: &mut C,
|
||||
realjid: Session,
|
||||
nick: Nick,
|
||||
presence: Presence,
|
||||
) -> Result<(), Error> {
|
||||
let session = Session::try_from(presence)?;
|
||||
|
||||
let occupant = self.get_mut_occupant(&session)?;
|
||||
match occupant.update_presence(session.presence.clone()) {
|
||||
Ok(_) | Err(Error::SecondarySession(_)) => (),
|
||||
err => err?,
|
||||
}
|
||||
|
||||
let occupant = self.get_occupant(&session)?;
|
||||
self.broadcast_presence(
|
||||
component,
|
||||
self.get_occupant(&realjid, &nick)?,
|
||||
&realjid,
|
||||
&occupant,
|
||||
session.presence,
|
||||
BroadcastPresence::Update,
|
||||
)
|
||||
.await?;
|
||||
|
@ -304,15 +325,20 @@ impl Room {
|
|||
pub async fn remove_session<C: ComponentTrait>(
|
||||
&mut self,
|
||||
component: &mut C,
|
||||
realjid: Session,
|
||||
nick: Nick,
|
||||
presence: Presence,
|
||||
) -> Result<(), Error> {
|
||||
let session = Session::try_from(presence)?;
|
||||
// If occupant doesn't exist, ignore.
|
||||
if let Some(mut occupant) = self.occupants.remove(&nick) {
|
||||
self.broadcast_presence(component, &occupant, &realjid, BroadcastPresence::Leave)
|
||||
if let Some(mut occupant) = self.occupants.remove(&session.participant.resource) {
|
||||
self.broadcast_presence(
|
||||
component,
|
||||
&occupant,
|
||||
session.presence.clone(),
|
||||
BroadcastPresence::Leave,
|
||||
)
|
||||
.await?;
|
||||
|
||||
occupant.remove_session(realjid)?;
|
||||
occupant.remove_session(session.presence)?;
|
||||
} else {
|
||||
// TODO: Error
|
||||
}
|
||||
|
@ -322,15 +348,33 @@ impl Room {
|
|||
|
||||
/// Fetch 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) {
|
||||
pub fn get_occupant(&self, session: &Session) -> Result<&Occupant, Error> {
|
||||
if let Some(occupant) = self.occupants.get(&session.participant.resource) {
|
||||
if occupant.contains(&session.real) {
|
||||
Ok(occupant)
|
||||
} else {
|
||||
Err(Error::NonexistantSession(realjid.clone()))
|
||||
Err(Error::NonexistantSession(session.clone()))
|
||||
}
|
||||
} else {
|
||||
Err(Error::ParticipantNotFound(nick.clone()))
|
||||
Err(Error::ParticipantNotFound(
|
||||
session.participant.resource.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a mutable reference of the occupant associated with the provided nick and ensure the
|
||||
/// session is part of it.
|
||||
pub fn get_mut_occupant(&mut self, session: &Session) -> Result<&mut Occupant, Error> {
|
||||
if let Some(occupant) = self.occupants.get_mut(&session.participant.resource) {
|
||||
if occupant.contains(&session.real) {
|
||||
Ok(occupant)
|
||||
} else {
|
||||
Err(Error::NonexistantSession(session.clone()))
|
||||
}
|
||||
} else {
|
||||
Err(Error::ParticipantNotFound(
|
||||
session.participant.resource.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,6 +383,67 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
/// An occupant session
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct Session {
|
||||
pub presence: Presence,
|
||||
pub real: FullJid,
|
||||
pub participant: FullJid,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Ensure presence doesn't contain payloads that would impersonate us
|
||||
fn filter_presence(presence: Presence) -> Presence {
|
||||
presence
|
||||
}
|
||||
|
||||
/// Instanciate a Session from a presence which is adressed to the room JID.
|
||||
fn with_nick<N: Into<Nick>>(presence: Presence, nick: N) -> Result<Session, Error> {
|
||||
let presence = Session::filter_presence(presence);
|
||||
Ok(Session {
|
||||
real: presence
|
||||
.from
|
||||
.clone()
|
||||
.map(FullJid::try_from)
|
||||
.ok_or(Error::MissingJid)??,
|
||||
participant: presence
|
||||
.to
|
||||
.clone()
|
||||
.map(BareJid::from)
|
||||
.ok_or(Error::MissingJid)?
|
||||
.with_resource(nick.into()),
|
||||
presence,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Session {
|
||||
fn eq(&self, other: &Session) -> bool {
|
||||
self.real == other.real && self.participant == other.participant
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Presence> for Session {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(presence: Presence) -> Result<Session, Error> {
|
||||
let presence = Session::filter_presence(presence);
|
||||
Ok(Session {
|
||||
real: presence
|
||||
.from
|
||||
.clone()
|
||||
.map(FullJid::try_from)
|
||||
.ok_or(Error::MissingJid)??,
|
||||
participant: presence
|
||||
.to
|
||||
.clone()
|
||||
.map(FullJid::try_from)
|
||||
.ok_or(Error::MissingJid)??,
|
||||
presence,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An occupant in a room. May contain multiple sessions (Multi-Session Nicks)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Occupant {
|
||||
|
@ -346,68 +451,85 @@ pub struct Occupant {
|
|||
pub real: BareJid,
|
||||
pub participant: FullJid,
|
||||
pub nick: Nick,
|
||||
pub sessions: Vec<Presence>,
|
||||
pub sessions: Vec<Session>,
|
||||
}
|
||||
|
||||
impl Occupant {
|
||||
/// New occupant
|
||||
pub fn new(room: &Room, real: FullJid, nick: Nick) -> Occupant {
|
||||
Occupant {
|
||||
real: BareJid::from(real.clone()),
|
||||
participant: room.jid.clone().with_resource(nick.clone()),
|
||||
nick,
|
||||
sessions: vec![real],
|
||||
}
|
||||
pub fn new(presence: Presence) -> Result<Occupant, Error> {
|
||||
let session = Session::try_from(presence)?;
|
||||
Ok(Occupant {
|
||||
real: BareJid::from(session.real.clone()),
|
||||
participant: session.participant.clone(),
|
||||
nick: session.participant.resource.clone(),
|
||||
sessions: vec![session],
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a new session to the occupant
|
||||
pub fn add_session(&mut self, real: FullJid) -> Result<(), Error> {
|
||||
if BareJid::from(real.clone()) != self.real {
|
||||
pub fn add_session(&mut self, presence: Presence) -> Result<(), Error> {
|
||||
let new_session = Session::try_from(presence)?;
|
||||
if BareJid::from(new_session.real.clone()) != self.real {
|
||||
return Err(Error::MismatchJids(
|
||||
Jid::from(self.real.clone()),
|
||||
Jid::from(real),
|
||||
Jid::from(new_session.real),
|
||||
));
|
||||
}
|
||||
|
||||
for session in &self.sessions {
|
||||
if &real == session {
|
||||
return Err(Error::SessionAlreadyExists(real));
|
||||
if &new_session.real == &session.real {
|
||||
return Err(Error::SessionAlreadyExists(new_session));
|
||||
}
|
||||
}
|
||||
|
||||
self.sessions.push(real);
|
||||
self.sessions.push(new_session);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a session from the occupant
|
||||
pub fn remove_session(&mut self, real: FullJid) -> Result<(), Error> {
|
||||
if BareJid::from(real.clone()) != self.real {
|
||||
return Err(Error::MismatchJids(
|
||||
Jid::from(self.real.clone()),
|
||||
Jid::from(real.clone()),
|
||||
));
|
||||
}
|
||||
pub fn remove_session(&mut self, presence: Presence) -> Result<(), Error> {
|
||||
let own_session = Session::try_from(presence)?;
|
||||
|
||||
let len = self.sessions.len();
|
||||
self.sessions.retain(|session| session != &real);
|
||||
self.sessions
|
||||
.retain(|session| session.real != own_session.real);
|
||||
|
||||
// An item has been removed
|
||||
if len != self.sessions.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NonexistantSession(real))
|
||||
Err(Error::NonexistantSession(own_session))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update session presence
|
||||
pub fn update_presence(&mut self, presence: Presence) -> Result<(), Error> {
|
||||
let own_session = Session::with_nick(presence, self.participant.resource.clone())?;
|
||||
|
||||
for (i, session) in self.sessions.iter().enumerate() {
|
||||
if &own_session == session {
|
||||
if i == 0 {
|
||||
// Leader session
|
||||
self.sessions[0] = own_session;
|
||||
return Ok(());
|
||||
}
|
||||
return Err(Error::SecondarySession(own_session));
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::NonexistantSession(own_session))
|
||||
}
|
||||
|
||||
/// Return whether a Jid matches the occupant. If FullJid, compare with each session, otherwise
|
||||
/// see if the BareJid matches.
|
||||
// TODO: We may want to split checking for Bare/Full as this can be error prone
|
||||
pub fn contains<J: Into<Jid>>(&self, _jid: &J) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Occupant {
|
||||
type Item = FullJid;
|
||||
type Item = Session;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
|
@ -416,7 +538,7 @@ impl IntoIterator for Occupant {
|
|||
}
|
||||
|
||||
impl Occupant {
|
||||
fn iter(&self) -> std::slice::Iter<'_, FullJid> {
|
||||
fn iter(&self) -> std::slice::Iter<'_, Session> {
|
||||
self.sessions.iter()
|
||||
}
|
||||
}
|
||||
|
@ -425,13 +547,17 @@ impl Occupant {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::component::TestComponent;
|
||||
use crate::tests::templates::{
|
||||
LOUISE_FULL1, LOUISE_FULL2, LOUISE_NICK, LOUISE_ROOM1_PART, ROOM1_BARE, ROSA_FULL1,
|
||||
ROSA_NICK, ROSA_ROOM1_PART, SUGAKO_FULL1, SUGAKO_NICK, SUGAKO_ROOM1_PART,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use xmpp_parsers::{
|
||||
muc::{
|
||||
user::{Affiliation, Item as MucItem, Role, Status as MucStatus},
|
||||
MucUser,
|
||||
Muc, MucUser,
|
||||
},
|
||||
presence::{Presence, Type as PresenceType},
|
||||
presence::{Presence, Show as PresenceShow, Type as PresenceType},
|
||||
BareJid, Element,
|
||||
};
|
||||
|
||||
|
@ -445,18 +571,30 @@ mod tests {
|
|||
let realjid3 = FullJid::from_str("bar@qxx/foo").unwrap();
|
||||
let participant3 = roomjid.clone().with_resource(String::from("nick3"));
|
||||
|
||||
let presence1 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid1.clone())
|
||||
.with_to(participant1.clone());
|
||||
|
||||
let presence2 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid2.clone())
|
||||
.with_to(participant2.clone());
|
||||
|
||||
let presence3 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid3.clone())
|
||||
.with_to(participant3.clone());
|
||||
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
|
||||
room.occupants = BTreeMap::new();
|
||||
room.occupants.insert(
|
||||
participant1.resource.clone(),
|
||||
Occupant::new(&room, realjid1.clone(), String::from("nick1")),
|
||||
Occupant::new(presence1).unwrap(),
|
||||
);
|
||||
room.occupants.insert(
|
||||
participant2.resource.clone(),
|
||||
Occupant::new(&room, realjid2.clone(), String::from("nick2")),
|
||||
Occupant::new(presence2).unwrap(),
|
||||
);
|
||||
let occupant3 = Occupant::new(&room, realjid3.clone(), String::from("nick3"));
|
||||
let occupant3 = Occupant::new(presence3.clone()).unwrap();
|
||||
room.occupants
|
||||
.insert(participant3.resource.clone(), occupant3.clone());
|
||||
|
||||
|
@ -505,7 +643,7 @@ mod tests {
|
|||
room.broadcast_presence(
|
||||
&mut component,
|
||||
&occupant3,
|
||||
&realjid3,
|
||||
presence3,
|
||||
BroadcastPresence::Resync,
|
||||
)
|
||||
.await
|
||||
|
@ -522,18 +660,30 @@ mod tests {
|
|||
let realjid3 = FullJid::from_str("bar@qxx/foo").unwrap();
|
||||
let participant3 = roomjid.clone().with_resource(String::from("nick3"));
|
||||
|
||||
let presence1 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid1.clone())
|
||||
.with_to(participant1.clone());
|
||||
|
||||
let presence2 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid2.clone())
|
||||
.with_to(participant2.clone());
|
||||
|
||||
let presence3 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid3.clone())
|
||||
.with_to(participant3.clone());
|
||||
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
|
||||
room.occupants = BTreeMap::new();
|
||||
room.occupants.insert(
|
||||
participant1.resource.clone(),
|
||||
Occupant::new(&room, realjid1.clone(), String::from("nick1")),
|
||||
Occupant::new(presence1).unwrap(),
|
||||
);
|
||||
room.occupants.insert(
|
||||
participant2.resource.clone(),
|
||||
Occupant::new(&room, realjid2.clone(), String::from("nick2")),
|
||||
Occupant::new(presence2).unwrap(),
|
||||
);
|
||||
let occupant3 = Occupant::new(&room, realjid3.clone(), String::from("nick3"));
|
||||
let occupant3 = Occupant::new(presence3.clone()).unwrap();
|
||||
room.occupants
|
||||
.insert(participant3.resource.clone(), occupant3.clone());
|
||||
|
||||
|
@ -565,7 +715,7 @@ mod tests {
|
|||
room.broadcast_presence(
|
||||
&mut component,
|
||||
&occupant3,
|
||||
&realjid3,
|
||||
presence3,
|
||||
BroadcastPresence::Update,
|
||||
)
|
||||
.await
|
||||
|
@ -582,18 +732,30 @@ mod tests {
|
|||
let realjid3 = FullJid::from_str("bar@qxx/foo").unwrap();
|
||||
let participant3 = roomjid.clone().with_resource(String::from("nick3"));
|
||||
|
||||
let presence1 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid1.clone())
|
||||
.with_to(participant1.clone());
|
||||
|
||||
let presence2 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid2.clone())
|
||||
.with_to(participant2.clone());
|
||||
|
||||
let presence3 = Presence::new(PresenceType::None)
|
||||
.with_from(realjid3.clone())
|
||||
.with_to(participant3.clone());
|
||||
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
|
||||
room.occupants = BTreeMap::new();
|
||||
room.occupants.insert(
|
||||
participant1.resource.clone(),
|
||||
Occupant::new(&room, realjid1.clone(), String::from("nick1")),
|
||||
Occupant::new(presence1.clone()).unwrap(),
|
||||
);
|
||||
room.occupants.insert(
|
||||
participant2.resource.clone(),
|
||||
Occupant::new(&room, realjid2.clone(), String::from("nick2")),
|
||||
Occupant::new(presence2.clone()).unwrap(),
|
||||
);
|
||||
let occupant3 = Occupant::new(&room, realjid3.clone(), String::from("nick3"));
|
||||
let occupant3 = Occupant::new(presence3.clone()).unwrap();
|
||||
room.occupants
|
||||
.insert(participant3.resource.clone(), occupant3.clone());
|
||||
|
||||
|
@ -664,7 +826,7 @@ mod tests {
|
|||
room.broadcast_presence(
|
||||
&mut component,
|
||||
&occupant3,
|
||||
&realjid3,
|
||||
presence3,
|
||||
BroadcastPresence::Join,
|
||||
)
|
||||
.await
|
||||
|
@ -673,36 +835,40 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_broadcast_presence_leave() {
|
||||
let roomjid = BareJid::from_str("room@muc").unwrap();
|
||||
let realjid1 = FullJid::from_str("foo@bar/qxx").unwrap();
|
||||
let participant1 = roomjid.clone().with_resource(String::from("nick1"));
|
||||
let realjid2 = FullJid::from_str("qxx@foo/bar").unwrap();
|
||||
let participant2 = roomjid.clone().with_resource(String::from("nick2"));
|
||||
let realjid3 = FullJid::from_str("bar@qxx/foo").unwrap();
|
||||
let participant3 = roomjid.clone().with_resource(String::from("nick3"));
|
||||
let presence_louise = Presence::new(PresenceType::None)
|
||||
.with_from(LOUISE_FULL1.clone())
|
||||
.with_to(LOUISE_ROOM1_PART.clone());
|
||||
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
let presence_sugako = Presence::new(PresenceType::None)
|
||||
.with_from(SUGAKO_FULL1.clone())
|
||||
.with_to(SUGAKO_ROOM1_PART.clone());
|
||||
|
||||
let presence_rosa = Presence::new(PresenceType::None)
|
||||
.with_from(ROSA_FULL1.clone())
|
||||
.with_to(ROSA_ROOM1_PART.clone());
|
||||
|
||||
let mut room = Room::new(ROOM1_BARE.clone());
|
||||
|
||||
room.occupants = BTreeMap::new();
|
||||
room.occupants.insert(
|
||||
participant1.resource.clone(),
|
||||
Occupant::new(&room, realjid1.clone(), String::from("nick1")),
|
||||
String::from(LOUISE_NICK),
|
||||
Occupant::new(presence_louise).unwrap(),
|
||||
);
|
||||
room.occupants.insert(
|
||||
participant2.resource.clone(),
|
||||
Occupant::new(&room, realjid2.clone(), String::from("nick2")),
|
||||
String::from(SUGAKO_NICK),
|
||||
Occupant::new(presence_sugako).unwrap(),
|
||||
);
|
||||
let occupant3 = Occupant::new(&room, realjid3.clone(), String::from("nick3"));
|
||||
let occupant3 = Occupant::new(presence_rosa).unwrap();
|
||||
room.occupants
|
||||
.insert(participant3.resource.clone(), occupant3.clone());
|
||||
.insert(String::from(ROSA_NICK), occupant3.clone());
|
||||
|
||||
// BroadcastPresence::Leave
|
||||
let mut component = TestComponent::new(vec![]);
|
||||
|
||||
component.expect(
|
||||
Presence::new(PresenceType::Unavailable)
|
||||
.with_from(participant3.clone())
|
||||
.with_to(realjid1.clone())
|
||||
.with_from(ROSA_ROOM1_PART.clone())
|
||||
.with_to(LOUISE_FULL1.clone())
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![],
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::None)],
|
||||
|
@ -712,8 +878,8 @@ mod tests {
|
|||
|
||||
component.expect(
|
||||
Presence::new(PresenceType::Unavailable)
|
||||
.with_from(participant3.clone())
|
||||
.with_to(realjid2.clone())
|
||||
.with_from(ROSA_ROOM1_PART.clone())
|
||||
.with_to(SUGAKO_FULL1.clone())
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![],
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::None)],
|
||||
|
@ -723,19 +889,27 @@ mod tests {
|
|||
|
||||
component.expect(
|
||||
Presence::new(PresenceType::Unavailable)
|
||||
.with_from(participant3.clone())
|
||||
.with_to(realjid3.clone())
|
||||
.with_from(ROSA_ROOM1_PART.clone())
|
||||
.with_to(ROSA_FULL1.clone())
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![MucStatus::SelfPresence],
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::None)],
|
||||
items: vec![{
|
||||
let mut item = MucItem::new(Affiliation::Owner, Role::None);
|
||||
item.jid = Some(ROSA_FULL1.clone());
|
||||
item
|
||||
}],
|
||||
}
|
||||
.into()]),
|
||||
);
|
||||
|
||||
let presence_leave = Presence::new(PresenceType::Unavailable)
|
||||
.with_from(ROSA_FULL1.clone())
|
||||
.with_to(ROSA_ROOM1_PART.clone());
|
||||
|
||||
room.broadcast_presence(
|
||||
&mut component,
|
||||
&occupant3,
|
||||
&realjid3,
|
||||
presence_leave,
|
||||
BroadcastPresence::Leave,
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
mod iq;
|
||||
mod presence;
|
||||
#[allow(dead_code)]
|
||||
mod templates;
|
||||
pub mod templates;
|
||||
|
|
|
@ -31,7 +31,7 @@ use xmpp_parsers::{
|
|||
user::{Affiliation, Item as MucItem, Role, Status as MucStatus},
|
||||
Muc, MucUser,
|
||||
},
|
||||
presence::{Presence, Type as PresenceType},
|
||||
presence::{Presence, Show as PresenceShow, Type as PresenceType},
|
||||
stanza_error::{DefinedCondition, ErrorType, StanzaError},
|
||||
BareJid, Element, Jid,
|
||||
};
|
||||
|
@ -350,7 +350,11 @@ async fn test_leave_last_participant() {
|
|||
.with_to(Jid::Full(LOUISE_FULL1.clone()))
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![MucStatus::SelfPresence],
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::None)],
|
||||
items: vec![{
|
||||
let mut item = MucItem::new(Affiliation::Owner, Role::None);
|
||||
item.jid = Some(LOUISE_FULL1.clone());
|
||||
item
|
||||
}],
|
||||
}
|
||||
.into()]),
|
||||
);
|
||||
|
@ -407,7 +411,11 @@ async fn test_leave_room_not_last() {
|
|||
.with_to(Jid::Full(SUGAKO_FULL1.clone()))
|
||||
.with_payloads(vec![MucUser {
|
||||
status: vec![MucStatus::SelfPresence],
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::None)],
|
||||
items: vec![{
|
||||
let mut item = MucItem::new(Affiliation::Owner, Role::None);
|
||||
item.jid = Some(SUGAKO_FULL1.clone());
|
||||
item
|
||||
}],
|
||||
}
|
||||
.into()]),
|
||||
);
|
||||
|
@ -514,6 +522,7 @@ async fn test_join_msn() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[tokio::test]
|
||||
async fn test_presence_update_joined() {
|
||||
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
|
||||
|
@ -535,7 +544,8 @@ async fn test_presence_update_joined() {
|
|||
status: Vec::new(),
|
||||
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
|
||||
}
|
||||
.into()]),
|
||||
.into()])
|
||||
.with_show(PresenceShow::Away),
|
||||
);
|
||||
|
||||
handle_stanza(&mut component, &mut rooms).await.unwrap();
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
use crate::room::{Occupant, Room};
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use xmpp_parsers::{BareJid, FullJid};
|
||||
use xmpp_parsers::{
|
||||
presence::{Presence, Type as PresenceType},
|
||||
BareJid, FullJid,
|
||||
};
|
||||
|
||||
pub const COMPONENT_JID: LazyLock<BareJid> =
|
||||
LazyLock::new(|| BareJid::from_str("commons.social").unwrap());
|
||||
|
@ -69,22 +72,31 @@ pub const PETER_ROOM1_PART: LazyLock<FullJid> =
|
|||
|
||||
pub fn one_participant_room(roomjid: BareJid) -> Room {
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
let presence_louise = Presence::new(PresenceType::None)
|
||||
.with_from(LOUISE_FULL1.clone())
|
||||
.with_to(LOUISE_ROOM1_PART.clone());
|
||||
room.occupants.insert(
|
||||
String::from(LOUISE_NICK),
|
||||
Occupant::new(&room, LOUISE_FULL1.clone(), String::from(LOUISE_NICK)),
|
||||
Occupant::new(presence_louise).unwrap(),
|
||||
);
|
||||
room
|
||||
}
|
||||
|
||||
pub fn two_participant_room(roomjid: BareJid) -> Room {
|
||||
let mut room = Room::new(roomjid.clone());
|
||||
let presence_louise = Presence::new(PresenceType::None)
|
||||
.with_from(LOUISE_FULL1.clone())
|
||||
.with_to(LOUISE_ROOM1_PART.clone());
|
||||
let presence_sugako = Presence::new(PresenceType::None)
|
||||
.with_from(SUGAKO_FULL1.clone())
|
||||
.with_to(SUGAKO_ROOM1_PART.clone());
|
||||
room.occupants.insert(
|
||||
String::from(LOUISE_NICK),
|
||||
Occupant::new(&room, LOUISE_FULL1.clone(), String::from(LOUISE_NICK)),
|
||||
Occupant::new(presence_louise).unwrap(),
|
||||
);
|
||||
room.occupants.insert(
|
||||
String::from(SUGAKO_NICK),
|
||||
Occupant::new(&room, SUGAKO_FULL1.clone(), String::from(SUGAKO_NICK)),
|
||||
Occupant::new(presence_sugako).unwrap(),
|
||||
);
|
||||
room
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue