2023-10-13 20:09:08 +00:00
|
|
|
|
//! Handling of all client2server presence stanzas
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
use anyhow::Result;
|
2023-10-13 20:09:08 +00:00
|
|
|
|
use quick_xml::events::Event;
|
2024-05-23 12:35:52 +00:00
|
|
|
|
use serde::Serialize;
|
2023-10-13 20:09:08 +00:00
|
|
|
|
|
|
|
|
|
use lavina_core::room::RoomId;
|
2024-05-18 18:18:42 +00:00
|
|
|
|
use proto_xmpp::bind::{Jid, Name, Resource, Server};
|
2023-10-13 20:09:08 +00:00
|
|
|
|
use proto_xmpp::client::Presence;
|
2024-05-23 12:35:52 +00:00
|
|
|
|
use proto_xmpp::muc::{Delay, XUser, XmppHistoryMessage};
|
2023-10-13 20:09:08 +00:00
|
|
|
|
use proto_xmpp::xml::{Ignore, ToXml};
|
|
|
|
|
|
|
|
|
|
use crate::XmppConnection;
|
|
|
|
|
|
|
|
|
|
impl<'a> XmppConnection<'a> {
|
|
|
|
|
pub async fn handle_presence(&mut self, output: &mut Vec<Event<'static>>, p: Presence<Ignore>) -> Result<()> {
|
2024-04-15 16:18:51 +00:00
|
|
|
|
match p.to {
|
|
|
|
|
None => {
|
2024-04-28 13:43:22 +00:00
|
|
|
|
self.self_presence(output, p.r#type.as_deref()).await;
|
2023-10-13 20:09:08 +00:00
|
|
|
|
}
|
2024-04-15 16:18:51 +00:00
|
|
|
|
Some(Jid {
|
|
|
|
|
name: Some(name),
|
|
|
|
|
server,
|
|
|
|
|
// resources in MUCs are members' personas – not implemented (yet?)
|
|
|
|
|
resource: Some(_),
|
|
|
|
|
}) if server.0 == self.hostname_rooms => {
|
2024-05-23 17:09:51 +00:00
|
|
|
|
let muc_presence = self.retrieve_muc_presence(&name).await?;
|
|
|
|
|
muc_presence.serialize(output);
|
2024-05-18 18:18:42 +00:00
|
|
|
|
|
2024-05-23 17:09:51 +00:00
|
|
|
|
let messages = self.retrieve_message_history(&name).await?;
|
|
|
|
|
for message in messages {
|
2024-05-23 12:35:52 +00:00
|
|
|
|
message.serialize(output)
|
|
|
|
|
}
|
2023-10-13 20:09:08 +00:00
|
|
|
|
}
|
2024-04-15 16:18:51 +00:00
|
|
|
|
_ => {
|
|
|
|
|
// TODO other presence cases
|
|
|
|
|
let response = Presence::<()>::default();
|
|
|
|
|
response.serialize(output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-28 13:43:22 +00:00
|
|
|
|
async fn self_presence(&mut self, output: &mut Vec<Event<'static>>, 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!(),
|
|
|
|
|
}
|
2024-04-15 16:18:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-23 17:09:51 +00:00
|
|
|
|
async fn retrieve_muc_presence(&mut self, name: &Name) -> Result<(Presence<XUser>)> {
|
2024-05-23 12:35:52 +00:00
|
|
|
|
let a = self.user_handle.join_room(RoomId::try_from(name.0.clone())?).await?;
|
2024-04-15 16:18:51 +00:00
|
|
|
|
// TODO handle bans
|
2024-05-14 14:44:49 +00:00
|
|
|
|
let response = Presence {
|
2024-04-15 16:18:51 +00:00
|
|
|
|
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()),
|
|
|
|
|
}),
|
2024-05-14 14:44:49 +00:00
|
|
|
|
custom: vec![XUser],
|
2024-04-15 16:18:51 +00:00
|
|
|
|
..Default::default()
|
2023-10-13 20:09:08 +00:00
|
|
|
|
};
|
2024-05-10 13:35:34 +00:00
|
|
|
|
Ok(response)
|
|
|
|
|
}
|
2024-05-23 17:09:51 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Retrieve a room's message history. The output can be serialized into a stream of XML stanzas.
|
|
|
|
|
|
|
|
|
|
Example of such a stanza:
|
|
|
|
|
|
|
|
|
|
<message from="duqedadi@conference.oflor.me/misha" xml:lang="en" to="misha@oflor.me/tux" type="groupchat" id="7ca7cb14-b2af-49c9-bd90-05dabb1113a5">
|
|
|
|
|
<delay xmlns="urn:xmpp:delay" stamp="2024-05-17T16:05:28.440337Z" from="duqedadi@conference.oflor.me"/>
|
|
|
|
|
<body></body>
|
|
|
|
|
</message>
|
|
|
|
|
*/
|
|
|
|
|
async fn retrieve_message_history(&self, room_name: &Name) -> Result<(Vec<XmppHistoryMessage>)> {
|
2024-05-23 12:35:52 +00:00
|
|
|
|
let room_id = RoomId::try_from(room_name.0.clone())?;
|
|
|
|
|
let history_messages = self.user_handle.get_room_message_history(room_id).await?;
|
|
|
|
|
let mut response = vec![];
|
|
|
|
|
|
|
|
|
|
for history_message in history_messages.into_iter() {
|
|
|
|
|
response.push(XmppHistoryMessage {
|
2024-05-23 18:52:03 +00:00
|
|
|
|
id: history_message.id.to_string(),
|
2024-05-23 12:35:52 +00:00
|
|
|
|
to: Jid {
|
2024-05-23 17:09:51 +00:00
|
|
|
|
name: Option::from(Name(self.user.xmpp_muc_name.0.clone().into())),
|
2024-05-23 12:35:52 +00:00
|
|
|
|
server: Server(self.hostname.clone()),
|
2024-05-23 17:09:51 +00:00
|
|
|
|
resource: None,
|
2024-05-18 18:18:42 +00:00
|
|
|
|
},
|
2024-05-23 12:35:52 +00:00
|
|
|
|
from: Jid {
|
2024-05-23 17:09:51 +00:00
|
|
|
|
name: Option::from(room_name.clone()),
|
2024-05-23 12:35:52 +00:00
|
|
|
|
server: Server(self.hostname_rooms.clone()),
|
2024-05-23 18:52:03 +00:00
|
|
|
|
resource: Option::from(Resource(history_message.author.name.clone().into())),
|
2024-05-23 12:35:52 +00:00
|
|
|
|
},
|
2024-05-23 17:09:51 +00:00
|
|
|
|
delay: Delay::new(
|
|
|
|
|
Jid {
|
2024-05-23 18:52:03 +00:00
|
|
|
|
name: Option::from(Name(history_message.author.name.clone().into())),
|
2024-05-23 17:09:51 +00:00
|
|
|
|
server: Server(self.hostname_rooms.clone()),
|
|
|
|
|
resource: None,
|
|
|
|
|
},
|
|
|
|
|
history_message.created_at.to_rfc3339(),
|
|
|
|
|
),
|
2024-05-23 18:52:03 +00:00
|
|
|
|
body: history_message.content.clone(),
|
2024-05-23 12:35:52 +00:00
|
|
|
|
});
|
2024-05-23 18:52:03 +00:00
|
|
|
|
tracing::info!(
|
|
|
|
|
"Retrieved message: {:?} {:?}",
|
|
|
|
|
history_message.author,
|
|
|
|
|
history_message.content.clone()
|
|
|
|
|
);
|
2024-05-23 12:35:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ok(response);
|
2024-05-18 18:18:42 +00:00
|
|
|
|
}
|
2024-05-10 13:35:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2024-05-13 14:32:45 +00:00
|
|
|
|
use anyhow::Result;
|
2024-05-18 18:18:42 +00:00
|
|
|
|
|
2024-05-10 13:35:34 +00:00
|
|
|
|
use lavina_core::player::PlayerId;
|
2024-05-10 21:50:34 +00:00
|
|
|
|
use proto_xmpp::bind::{Jid, Name, Resource, Server};
|
2024-05-10 13:35:34 +00:00
|
|
|
|
use proto_xmpp::client::Presence;
|
2024-05-14 14:44:49 +00:00
|
|
|
|
use proto_xmpp::muc::XUser;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
|
2024-05-18 18:18:42 +00:00
|
|
|
|
use crate::testkit::{expect_user_authenticated, TestServer};
|
|
|
|
|
use crate::Authenticated;
|
|
|
|
|
|
2024-05-10 13:35:34 +00:00
|
|
|
|
#[tokio::test]
|
2024-05-13 14:32:45 +00:00
|
|
|
|
async fn test_muc_joining() -> Result<()> {
|
2024-05-10 13:35:34 +00:00
|
|
|
|
let server = TestServer::start().await.unwrap();
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
server.core.create_player(&PlayerId::from("tester")?).await?;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
|
|
|
|
|
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()),
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
let mut player_conn = server.core.connect_to_player(&user.player_id).await;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
|
|
|
|
|
|
2024-05-23 17:09:51 +00:00
|
|
|
|
let response = conn.retrieve_muc_presence(&user.xmpp_name).await.unwrap();
|
2024-05-14 14:44:49 +00:00
|
|
|
|
let expected = Presence {
|
2024-05-10 13:35:34 +00:00
|
|
|
|
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()),
|
|
|
|
|
}),
|
2024-05-14 14:44:49 +00:00
|
|
|
|
custom: vec![XUser],
|
2024-05-10 13:35:34 +00:00
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
|
|
|
|
|
|
server.shutdown().await.unwrap();
|
2024-05-13 14:32:45 +00:00
|
|
|
|
Ok(())
|
2024-05-10 13:35:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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]
|
2024-05-13 14:32:45 +00:00
|
|
|
|
async fn test_muc_joining_twice() -> Result<()> {
|
2024-05-10 13:35:34 +00:00
|
|
|
|
let server = TestServer::start().await.unwrap();
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
server.core.create_player(&PlayerId::from("tester")?).await?;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
|
|
|
|
|
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()),
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
let mut player_conn = server.core.connect_to_player(&user.player_id).await;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
|
|
|
|
|
|
2024-05-23 17:09:51 +00:00
|
|
|
|
let response = conn.retrieve_muc_presence(&user.xmpp_name).await.unwrap();
|
2024-05-14 14:44:49 +00:00
|
|
|
|
let expected = Presence {
|
2024-05-10 13:35:34 +00:00
|
|
|
|
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()),
|
|
|
|
|
}),
|
2024-05-14 14:44:49 +00:00
|
|
|
|
custom: vec![XUser],
|
2024-05-10 13:35:34 +00:00
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
|
|
|
|
|
|
drop(conn);
|
|
|
|
|
let server = server.reboot().await.unwrap();
|
|
|
|
|
|
2024-05-13 14:32:45 +00:00
|
|
|
|
let mut player_conn = server.core.connect_to_player(&user.player_id).await;
|
2024-05-10 13:35:34 +00:00
|
|
|
|
let mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
|
|
|
|
|
|
2024-05-23 17:09:51 +00:00
|
|
|
|
let response = conn.retrieve_muc_presence(&user.xmpp_name).await.unwrap();
|
2024-05-10 13:35:34 +00:00
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
|
|
|
|
|
|
server.shutdown().await.unwrap();
|
2024-05-13 14:32:45 +00:00
|
|
|
|
Ok(())
|
2023-10-13 20:09:08 +00:00
|
|
|
|
}
|
|
|
|
|
}
|