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(())
+}