diff --git a/src/handlers.rs b/src/handlers/mod.rs similarity index 61% rename from src/handlers.rs rename to src/handlers/mod.rs index 697f181..c98e77b 100644 --- a/src/handlers.rs +++ b/src/handlers/mod.rs @@ -13,12 +13,15 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +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( component.send_stanza(iq).await } -async fn handle_presence( - component: &mut C, - presence: Presence, - rooms: &mut HashMap, -) -> 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( _component: &mut C, _message: Message, diff --git a/src/handlers/presence.rs b/src/handlers/presence.rs new file mode 100644 index 0000000..7af8e78 --- /dev/null +++ b/src/handlers/presence.rs @@ -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 . + +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( + component: &mut C, + presence: Presence, + rooms: &mut HashMap, +) -> 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( + component: &mut C, + presence: PresenceFull, + rooms: &mut HashMap, +) -> 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( + component: &mut C, + presence: PresenceFull, + rooms: &mut HashMap, +) -> 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(()) +}