//! Handling of all client2server presence stanzas use anyhow::Result; use quick_xml::events::Event; use lavina_core::room::RoomId; use proto_xmpp::bind::{Jid, Name, Resource, Server}; use proto_xmpp::client::Presence; use proto_xmpp::muc::{Delay, HistoryMessage, XUser}; use proto_xmpp::xml::{Ignore, ToXml}; use crate::XmppConnection; impl<'a> XmppConnection<'a> { pub async fn handle_presence(&mut self, output: &mut Vec>, p: Presence) -> Result<()> { match p.to { None => { self.self_presence(output, p.r#type.as_deref()).await; } Some(Jid { name: Some(name), server, // resources in MUCs are members' personas – not implemented (yet?) resource: Some(_), }) if server.0 == self.hostname_rooms => { let muc_join_response = self.muc_presence(&name).await?; muc_join_response.serialize(output); let history_on_join_response = self.send_history_on_join().await?; history_on_join_response.serialize(output) } _ => { // TODO other presence cases let response = Presence::<()>::default(); response.serialize(output); } } Ok(()) } async fn self_presence(&mut self, output: &mut Vec>, r#type: Option<&str>) { match r#type { Some("unavailable") => { // do not print anything } None => { let response = Presence::<()> { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(self.user.xmpp_name.clone()), server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), ..Default::default() }; response.serialize(output); } _ => todo!(), } } async fn muc_presence(&mut self, name: &Name) -> Result<(Presence)> { let a = self.user_handle.join_room(RoomId::from(name.0.clone())?).await?; // TODO handle bans let response = Presence { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(name.clone()), server: Server(self.hostname_rooms.clone()), resource: Some(self.user.xmpp_muc_name.clone()), }), custom: vec![XUser], ..Default::default() }; Ok(response) } async fn send_history_on_join(&self) -> Result<(HistoryMessage)> { Ok(HistoryMessage { id: "kek".to_string(), to: Jid { name: Some(Name("tester".into())), server: Server("localhost".into()), resource: Some(Resource("tester".into())), }, from: Jid { name: Some(Name("tester".into())), server: Server("localhost".into()), resource: Some(Resource("tester".into())), }, delay: Delay::new( Jid { name: Some(Name("tester".into())), server: Server("localhost".into()), resource: Some(Resource("tester".into())), }, "2021-10-10T10:10:10Z".to_string(), ), body: "Vasya Pupkin says hello.".to_string(), }) } } #[cfg(test)] mod tests { use anyhow::Result; use lavina_core::player::PlayerId; use proto_xmpp::bind::{Jid, Name, Resource, Server}; use proto_xmpp::client::Presence; use proto_xmpp::muc::XUser; use crate::testkit::{expect_user_authenticated, TestServer}; use crate::Authenticated; #[tokio::test] async fn test_muc_joining() -> Result<()> { let server = TestServer::start().await.unwrap(); server.core.create_player(&PlayerId::from("tester")?).await?; let player_id = PlayerId::from("tester").unwrap(); let user = Authenticated { player_id, xmpp_name: Name("tester".into()), xmpp_resource: Resource("tester".into()), xmpp_muc_name: Resource("tester".into()), }; let mut player_conn = server.core.connect_to_player(&user.player_id).await; let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap(); let response = conn.muc_presence(&user.xmpp_name).await.unwrap(); let expected = Presence { to: Some(Jid { name: Some(conn.user.xmpp_name.clone()), server: Server(conn.hostname.clone()), resource: Some(conn.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(user.xmpp_name.clone()), server: Server(conn.hostname_rooms.clone()), resource: Some(conn.user.xmpp_muc_name.clone()), }), custom: vec![XUser], ..Default::default() }; assert_eq!(expected, response); server.shutdown().await.unwrap(); Ok(()) } // Test that joining a room second time after a server restart, // i.e. in-memory cache of memberships is cleaned, does not cause any issues. #[tokio::test] async fn test_muc_joining_twice() -> Result<()> { let server = TestServer::start().await.unwrap(); server.core.create_player(&PlayerId::from("tester")?).await?; let player_id = PlayerId::from("tester").unwrap(); let user = Authenticated { player_id, xmpp_name: Name("tester".into()), xmpp_resource: Resource("tester".into()), xmpp_muc_name: Resource("tester".into()), }; let mut player_conn = server.core.connect_to_player(&user.player_id).await; let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap(); let response = conn.muc_presence(&user.xmpp_name).await.unwrap(); let expected = Presence { to: Some(Jid { name: Some(conn.user.xmpp_name.clone()), server: Server(conn.hostname.clone()), resource: Some(conn.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(user.xmpp_name.clone()), server: Server(conn.hostname_rooms.clone()), resource: Some(conn.user.xmpp_muc_name.clone()), }), custom: vec![XUser], ..Default::default() }; assert_eq!(expected, response); drop(conn); let server = server.reboot().await.unwrap(); let mut player_conn = server.core.connect_to_player(&user.player_id).await; let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap(); let response = conn.muc_presence(&user.xmpp_name).await.unwrap(); assert_eq!(expected, response); server.shutdown().await.unwrap(); Ok(()) } }