handlers: split presence handler

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2022-12-26 13:58:48 +01:00
parent 28186d77d2
commit 6e73112251
2 changed files with 188 additions and 120 deletions

View file

@ -13,12 +13,15 @@
// 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/>.
mod presence;
use crate::handlers::presence::handle_presence;
use crate::component::ComponentTrait;
use crate::error::Error;
use crate::room::Room;
use std::collections::{BTreeMap, HashMap};
use std::ops::ControlFlow;
use std::str::FromStr;
use futures::stream::StreamExt;
@ -27,13 +30,9 @@ use xmpp_parsers::{
disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity},
iq::{Iq, IqType},
message::Message,
muc::{
user::{Affiliation, Item as MucItem, Reason, Role, Status as MucStatus},
Muc, MucUser,
},
ns,
ping::Ping,
presence::{Presence, Type as PresenceType},
presence::Presence,
stanza_error::{DefinedCondition, ErrorType, StanzaError},
BareJid, Element, Jid,
};
@ -215,120 +214,6 @@ async fn handle_unhandled_iq<C: ComponentTrait>(
component.send_stanza(iq).await
}
async fn handle_presence<C: ComponentTrait>(
component: &mut C,
presence: Presence,
rooms: &mut HashMap<BareJid, Room>,
) -> Result<(), Error> {
if presence.type_ == PresenceType::None {
let muc =
presence
.payloads
.clone()
.into_iter()
.try_for_each(|payload| match Muc::try_from(payload) {
Ok(muc) => ControlFlow::Break(muc),
_ => ControlFlow::Continue(()),
});
// TODO: check for features in the Muc element.
// Presences to MUC come from resources not accounts
if let Jid::Full(realjid) = presence.from.clone().unwrap() &&
let Jid::Full(participant) = presence.to.clone().unwrap() {
let roomjid = BareJid::from(participant.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, presence.clone()).await {
Ok(_) => (),
Err(Error::NickAlreadyAssigned(nick)) => {
let error = Presence::new(PresenceType::Error)
.with_from(participant.clone())
.with_to(realjid.clone())
.with_payloads(vec![
StanzaError::new(
ErrorType::Cancel,
DefinedCondition::Conflict,
"en",
format!("Nickname conflict: {}", nick),
).into()
]);
component.send_stanza(error).await?;
},
err => err.unwrap(),
}
} else if let ControlFlow::Continue(_) = muc {
match room.update_presence(component, presence.clone()).await {
Ok(()) => (),
Err(Error::ParticipantNotFound(_)) => {
let error = Presence::new(PresenceType::Unavailable)
.with_from(participant)
.with_to(realjid)
.with_payloads(vec![MucUser {
status: vec![
MucStatus::SelfPresence,
MucStatus::Kicked,
MucStatus::ServiceErrorKick,
],
items: {
let mut item = MucItem::new(Affiliation::None, Role::None);
item.reason = Some(Reason(String::from("You are not in the room.")));
vec![item]
},
}
.into()]);
component.send_stanza(error).await?
},
err => err?,
}
}
} else {
debug!("Presence received to new room: {}", &roomjid);
let mut room = Room::new(roomjid.clone());
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.clone().unwrap() &&
let Jid::Full(participant) = presence.to.clone().unwrap() {
let roomjid = BareJid::from(participant.clone());
let error = Presence::new(PresenceType::Error)
.with_from(participant.clone())
.with_to(realjid.clone())
.with_payloads(vec![
StanzaError::new(
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"en",
"Occupant does not exist",
).into()
]);
if let Some(mut room) = rooms.remove(&roomjid) {
match room.remove_session(component, presence.clone()).await {
Ok(()) => (),
Err(Error::NonexistantSession(_)) => {
component.send_stanza(error).await.unwrap();
},
Err(err) => Err(err).unwrap(),
}
// Add the room back
if !room.occupants.is_empty() {
rooms.insert(roomjid, room);
}
}
}
Ok(())
}
async fn handle_message<C: ComponentTrait>(
_component: &mut C,
_message: Message,

183
src/handlers/presence.rs Normal file
View file

@ -0,0 +1,183 @@
// 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/>.
use crate::component::ComponentTrait;
use crate::error::Error;
use crate::presence::PresenceFull;
use crate::room::Room;
use std::collections::HashMap;
use std::ops::ControlFlow;
use log::debug;
use xmpp_parsers::{
muc::{
user::{Affiliation, Item as MucItem, Reason, Role, Status as MucStatus},
Muc, MucUser,
},
presence::{Presence, Type as PresenceType},
stanza_error::{DefinedCondition, ErrorType, StanzaError},
BareJid, FullJid,
};
pub async fn handle_presence<C: ComponentTrait>(
component: &mut C,
presence: Presence,
rooms: &mut HashMap<BareJid, Room>,
) -> Result<(), Error> {
if let Ok(presence) = PresenceFull::try_from(presence.clone()) {
match presence.type_ {
PresenceType::None => {
handle_presence_full_available(component, presence.clone(), rooms).await?
}
PresenceType::Unavailable => {
handle_presence_full_unavailable(component, presence.clone(), rooms).await?
}
_ => debug!("Unhandled Presence Type: {:?}", presence),
}
} else {
debug!("Unhandled Presence: {:?}", presence)
}
Ok(())
}
async fn handle_presence_full_available<C: ComponentTrait>(
component: &mut C,
presence: PresenceFull,
rooms: &mut HashMap<BareJid, Room>,
) -> Result<(), Error> {
let realjid: FullJid = presence
.from
.clone()
.map(FullJid::try_from)
.unwrap()
.unwrap();
let participant: FullJid = presence.to.clone().map(FullJid::try_from).unwrap().unwrap();
let roomjid: BareJid = BareJid::from(participant.clone());
let muc = presence
.payloads
.clone()
.into_iter()
.try_for_each(|payload| match Muc::try_from(payload) {
Ok(muc) => ControlFlow::Break(muc),
_ => ControlFlow::Continue(()),
});
// TODO: check for features in the Muc element.
// Room already exists
if let Some(room) = rooms.get_mut(&roomjid) {
debug!("Presence received to existing room: {}", &roomjid);
if let ControlFlow::Break(_) = muc {
// <{muc}x/> was found
match room.add_session(component, *presence.clone()).await {
Ok(_) => (),
Err(Error::NickAlreadyAssigned(nick)) => {
let error = Presence::new(PresenceType::Error)
.with_from(participant.clone())
.with_to(realjid.clone())
.with_payloads(vec![StanzaError::new(
ErrorType::Cancel,
DefinedCondition::Conflict,
"en",
format!("Nickname conflict: {}", nick),
)
.into()]);
component.send_stanza(error).await?;
}
err => err.unwrap(),
}
} else if let ControlFlow::Continue(_) = muc {
// <{muc}x/> wasn't found
match room.update_presence(component, *presence.clone()).await {
Ok(()) => (),
Err(Error::ParticipantNotFound(_)) => {
let error = Presence::new(PresenceType::Unavailable)
.with_from(participant)
.with_to(realjid)
.with_payloads(vec![MucUser {
status: vec![
MucStatus::SelfPresence,
MucStatus::Kicked,
MucStatus::ServiceErrorKick,
],
items: {
let mut item = MucItem::new(Affiliation::None, Role::None);
item.reason =
Some(Reason(String::from("You are not in the room.")));
vec![item]
},
}
.into()]);
component.send_stanza(error).await?
}
err => err?,
}
}
} else {
debug!("Presence received to new room: {}", &roomjid);
let mut room = Room::new(roomjid.clone());
room.add_session(component, *presence.clone())
.await
.unwrap();
let _ = rooms.insert(roomjid, room);
}
Ok(())
}
async fn handle_presence_full_unavailable<C: ComponentTrait>(
component: &mut C,
presence: PresenceFull,
rooms: &mut HashMap<BareJid, Room>,
) -> Result<(), Error> {
let realjid: FullJid = presence
.from
.clone()
.map(FullJid::try_from)
.unwrap()
.unwrap();
let participant: FullJid = presence.to.clone().map(FullJid::try_from).unwrap().unwrap();
let roomjid: BareJid = BareJid::from(participant.clone());
let error = Presence::new(PresenceType::Error)
.with_from(participant.clone())
.with_to(realjid.clone())
.with_payloads(vec![StanzaError::new(
ErrorType::Cancel,
DefinedCondition::ServiceUnavailable,
"en",
"Occupant does not exist",
)
.into()]);
if let Some(mut room) = rooms.remove(&roomjid) {
match room.remove_session(component, *presence.clone()).await {
Ok(()) => (),
Err(Error::NonexistantSession(_)) => {
component.send_stanza(error).await.unwrap();
}
Err(err) => Err(err).unwrap(),
}
// Add the room back
if !room.occupants.is_empty() {
rooms.insert(roomjid, room);
}
}
Ok(())
}