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