2022-09-07 22:06:33 +00:00
|
|
|
// Copyright (C) 2022-2099 The crate authors.
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
|
|
// under the terms of the GNU Affero General Public License as published by the
|
|
|
|
// Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
// option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
|
|
|
// for more details.
|
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
|
2022-09-08 08:49:16 +00:00
|
|
|
use crate::component::ComponentTrait;
|
2022-09-07 22:06:33 +00:00
|
|
|
use crate::error::Error;
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::iter::IntoIterator;
|
|
|
|
|
2022-09-10 15:56:24 +00:00
|
|
|
use chrono;
|
2022-09-07 22:06:33 +00:00
|
|
|
use log::debug;
|
|
|
|
use xmpp_parsers::{
|
2022-09-10 15:56:24 +00:00
|
|
|
date::DateTime,
|
|
|
|
delay::Delay,
|
2022-09-07 22:06:33 +00:00
|
|
|
message::{Message, MessageType, Subject},
|
|
|
|
muc::{
|
|
|
|
user::{Affiliation, Item as MucItem, Role, Status as MucStatus},
|
|
|
|
MucUser,
|
|
|
|
},
|
|
|
|
presence::{Presence, Type as PresenceType},
|
2022-09-08 08:49:16 +00:00
|
|
|
BareJid, FullJid, Jid,
|
2022-09-07 22:06:33 +00:00
|
|
|
};
|
|
|
|
|
2022-09-10 16:33:57 +00:00
|
|
|
pub type Nick = String;
|
2022-09-07 22:06:33 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2022-09-10 16:33:57 +00:00
|
|
|
pub struct Room {
|
2022-09-11 08:52:14 +00:00
|
|
|
pub jid: BareJid,
|
|
|
|
pub occupants: HashMap<BareJid, Occupant>,
|
2022-09-11 14:33:44 +00:00
|
|
|
// TODO: Subject struct.
|
|
|
|
// TODO: Store subject lang
|
|
|
|
pub subject: Option<(String, Occupant, DateTime)>,
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Room {
|
2022-09-10 16:33:57 +00:00
|
|
|
pub fn new(jid: BareJid) -> Self {
|
2022-09-07 22:06:33 +00:00
|
|
|
Room {
|
|
|
|
jid,
|
|
|
|
occupants: HashMap::new(),
|
2022-09-11 14:33:44 +00:00
|
|
|
subject: None,
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-10 16:33:57 +00:00
|
|
|
pub async fn add_session<C: ComponentTrait>(
|
2022-09-07 22:06:33 +00:00
|
|
|
&mut self,
|
2022-09-08 08:49:16 +00:00
|
|
|
component: &mut C,
|
2022-09-07 22:06:33 +00:00
|
|
|
realjid: FullJid,
|
|
|
|
nick: Nick,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let bare = BareJid::from(realjid.clone());
|
|
|
|
if let Some(occupant) = self.occupants.get_mut(&bare) {
|
|
|
|
occupant.add_session(realjid)?;
|
|
|
|
} else {
|
|
|
|
debug!("{} is joining {}", realjid, self.jid);
|
|
|
|
|
2022-09-11 14:33:44 +00:00
|
|
|
let new_occupant = Occupant::new(&self, realjid.clone(), nick.clone());
|
|
|
|
|
2022-09-07 22:06:33 +00:00
|
|
|
// Ensure nick isn't already assigned
|
|
|
|
let _ = self.occupants.iter().try_for_each(|(_, occupant)| {
|
|
|
|
let nick = nick.clone();
|
|
|
|
if occupant.nick == nick {
|
|
|
|
return Err(Error::NickAlreadyAssigned(nick));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})?;
|
|
|
|
|
|
|
|
// Send occupants
|
|
|
|
debug!("Sending occupants for {}", realjid);
|
2022-09-11 14:33:44 +00:00
|
|
|
let presence = Presence::new(PresenceType::None)
|
|
|
|
// New occupant with a single session
|
|
|
|
.with_to(new_occupant.sessions[0].clone())
|
|
|
|
.with_payloads(vec![MucUser {
|
|
|
|
status: Vec::new(),
|
|
|
|
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
|
|
|
|
}.into()]);
|
2022-09-07 22:06:33 +00:00
|
|
|
for (_, occupant) in self.occupants.iter() {
|
2022-09-11 14:33:44 +00:00
|
|
|
let presence = presence.clone()
|
|
|
|
.with_from(occupant.participant.clone());
|
|
|
|
component.send_stanza(presence).await?;
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add into occupants
|
2022-09-11 14:33:44 +00:00
|
|
|
let _ = self.occupants.insert(bare.clone(), new_occupant.clone());
|
2022-09-07 22:06:33 +00:00
|
|
|
|
|
|
|
// Self-presence
|
|
|
|
debug!("Sending self-presence for {}", realjid);
|
|
|
|
let participant: FullJid = self.jid.clone().with_resource(nick);
|
|
|
|
let status = vec![MucStatus::SelfPresence, MucStatus::AssignedNick];
|
|
|
|
let items = vec![MucItem::new(Affiliation::Owner, Role::Moderator)];
|
|
|
|
let self_presence = Presence::new(PresenceType::None)
|
2022-09-10 10:35:27 +00:00
|
|
|
.with_from(participant.clone())
|
2022-09-07 22:06:33 +00:00
|
|
|
.with_to(realjid.clone())
|
|
|
|
.with_payloads(vec![MucUser { status, items }.into()]);
|
2022-09-08 08:49:16 +00:00
|
|
|
component.send_stanza(self_presence).await?;
|
2022-09-07 22:06:33 +00:00
|
|
|
|
|
|
|
// Send subject
|
|
|
|
debug!("Sending subject!");
|
2022-09-11 14:33:44 +00:00
|
|
|
if self.subject.is_none() {
|
|
|
|
let subject = String::from("");
|
|
|
|
let setter = new_occupant;
|
|
|
|
let stamp = DateTime::from_utc(chrono::Utc::now());
|
|
|
|
self.subject = Some((subject, setter, stamp));
|
|
|
|
}
|
|
|
|
|
2022-09-07 22:06:33 +00:00
|
|
|
let mut subject = Message::new(Some(Jid::Full(realjid)));
|
2022-09-11 14:33:44 +00:00
|
|
|
subject.from = Some(Jid::Full(
|
|
|
|
self.subject.as_ref().unwrap().1.participant.clone(),
|
|
|
|
));
|
|
|
|
subject.subjects.insert(
|
|
|
|
String::from("en"),
|
|
|
|
Subject(self.subject.as_ref().unwrap().0.clone()),
|
|
|
|
);
|
2022-09-07 22:06:33 +00:00
|
|
|
subject.type_ = MessageType::Groupchat;
|
2022-09-10 15:56:24 +00:00
|
|
|
subject.payloads = vec![Delay {
|
|
|
|
from: Some(Jid::Bare(self.jid.clone())),
|
2022-09-11 14:33:44 +00:00
|
|
|
stamp: self.subject.as_ref().unwrap().2.clone(),
|
2022-09-10 15:56:24 +00:00
|
|
|
data: None,
|
|
|
|
}
|
|
|
|
.into()];
|
2022-09-08 08:49:16 +00:00
|
|
|
component.send_stanza(subject).await?;
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
2022-09-10 16:33:57 +00:00
|
|
|
pub struct Occupant {
|
2022-09-11 14:33:44 +00:00
|
|
|
/// Public Jid for the Occupant
|
|
|
|
real: BareJid,
|
|
|
|
participant: FullJid,
|
2022-09-07 22:06:33 +00:00
|
|
|
nick: Nick,
|
|
|
|
sessions: Vec<FullJid>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Occupant {
|
2022-09-11 14:33:44 +00:00
|
|
|
fn new(room: &Room, real: FullJid, nick: Nick) -> Occupant {
|
2022-09-07 22:06:33 +00:00
|
|
|
Occupant {
|
2022-09-11 14:33:44 +00:00
|
|
|
real: BareJid::from(real.clone()),
|
|
|
|
participant: room.jid.clone().with_resource(nick.clone()),
|
2022-09-11 08:50:56 +00:00
|
|
|
nick,
|
2022-09-11 14:33:44 +00:00
|
|
|
sessions: vec![real],
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-11 14:33:44 +00:00
|
|
|
pub fn add_session(&mut self, real: FullJid) -> Result<(), Error> {
|
|
|
|
if BareJid::from(real.clone()) != self.real {
|
|
|
|
return Err(Error::MismatchJids(Jid::from(real.clone())));
|
2022-09-07 22:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoIterator for Occupant {
|
|
|
|
type Item = FullJid;
|
|
|
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
self.sessions.into_iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Occupant {
|
|
|
|
fn iter(&self) -> std::slice::Iter<'_, FullJid> {
|
|
|
|
self.sessions.iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use std::str::FromStr;
|
2022-09-11 14:33:44 +00:00
|
|
|
use xmpp_parsers::{BareJid, FullJid};
|
2022-09-07 22:06:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn occupant_ignore_dup_session() {
|
2022-09-11 14:33:44 +00:00
|
|
|
let room = Room::new(BareJid::from_str("room@muc").unwrap());
|
|
|
|
let real = FullJid::from_str("foo@bar/meh").unwrap();
|
|
|
|
let mut occupant = Occupant::new(&room, real.clone(), String::from("nick"));
|
|
|
|
occupant.add_session(real.clone()).unwrap();
|
2022-09-07 22:06:33 +00:00
|
|
|
assert_eq!(occupant.iter().count(), 1);
|
|
|
|
}
|
|
|
|
}
|