From 1a21c05d7dd4909f7ab551a0ea7d551a2c73361f Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Mon, 27 May 2024 14:24:23 +0000 Subject: [PATCH] xmpp: add support leaving MUCs via unavailable presence (#71) Reviewed-on: https://git.vilunov.me/lavina/lavina/pulls/71 --- crates/projection-xmpp/src/presence.rs | 110 ++++++++++++++++++------- 1 file changed, 80 insertions(+), 30 deletions(-) diff --git a/crates/projection-xmpp/src/presence.rs b/crates/projection-xmpp/src/presence.rs index 5b34e5e..9e8e2e0 100644 --- a/crates/projection-xmpp/src/presence.rs +++ b/crates/projection-xmpp/src/presence.rs @@ -23,36 +23,17 @@ impl<'a> XmppConnection<'a> { server, // resources in MUCs are members' personas – not implemented (yet?) resource: Some(_), - }) if server.0 == self.hostname_rooms => { - let mut muc_presence = self.retrieve_muc_presence(&name).await?; - muc_presence.id = p.id; - let subject = Message::<()> { - from: Some(Jid { - name: Some(name.clone()), - server: Server(self.hostname_rooms.clone()), - resource: None, - }), - id: None, - to: Some(Jid { - name: Some(self.user.xmpp_name.clone()), - server: Server(self.hostname.clone()), - resource: Some(self.user.xmpp_resource.clone()), - }), - r#type: MessageType::Groupchat, - lang: None, - subject: Some(Subject(None)), - body: None, - custom: vec![], - }; - muc_presence.serialize(output); - - let messages = self.retrieve_message_history(&name).await?; - for message in messages { - message.serialize(output) + }) if server.0 == self.hostname_rooms => match p.r#type.as_deref() { + None => { + self.join_muc(output, p.id, name).await?; } - // The subject is the last stanza sent during a MUC join process. - subject.serialize(output); - } + Some("unavailable") => { + self.leave_muc(output, p.id, name).await?; + } + _ => { + tracing::error!("Unimplemented case") + } + }, _ => { // TODO other presence cases let response = Presence::<()>::default(); @@ -62,6 +43,75 @@ impl<'a> XmppConnection<'a> { Ok(()) } + async fn join_muc(&mut self, output: &mut Vec>, id: Option, name: Name) -> Result<()> { + // Response presence + let mut muc_presence = self.retrieve_muc_presence(&name).await?; + muc_presence.id = id; + muc_presence.serialize(output); + + // N last messages from the room history before the user joined + let messages = self.retrieve_message_history(&name).await?; + for message in messages { + message.serialize(output) + } + + // The subject is the last stanza sent during a MUC join process. + let subject = Message::<()> { + from: Some(Jid { + name: Some(name.clone()), + server: Server(self.hostname_rooms.clone()), + resource: None, + }), + id: None, + to: Some(Jid { + name: Some(self.user.xmpp_name.clone()), + server: Server(self.hostname.clone()), + resource: Some(self.user.xmpp_resource.clone()), + }), + r#type: MessageType::Groupchat, + lang: None, + subject: Some(Subject(None)), + body: None, + custom: vec![], + }; + subject.serialize(output); + Ok(()) + } + + async fn leave_muc(&mut self, output: &mut Vec>, id: Option, name: Name) -> Result<()> { + self.user_handle.leave_room(RoomId::try_from(name.0.clone())?).await?; + let response = Presence { + id, + 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()), + }), + r#type: Some("unavailable".into()), + custom: vec![XUser { + item: XUserItem { + affiliation: Affiliation::Member, + role: Role::None, + jid: Jid { + name: Some(self.user.xmpp_name.clone()), + server: Server(self.hostname.clone()), + resource: Some(self.user.xmpp_resource.clone()), + }, + }, + self_presence: true, + just_created: false, + }], + ..Default::default() + }; + response.serialize(output); + Ok(()) + } + #[tracing::instrument(skip(self, output, r#type), name = "XmppConnection::self_presence")] async fn self_presence(&mut self, output: &mut Vec>, r#type: Option<&str>) { match r#type { @@ -92,7 +142,7 @@ impl<'a> XmppConnection<'a> { #[tracing::instrument(skip(self), name = "XmppConnection::retrieve_muc_presence")] async fn retrieve_muc_presence(&mut self, name: &Name) -> Result> { - let a = self.user_handle.join_room(RoomId::try_from(name.0.clone())?).await?; + let _ = self.user_handle.join_room(RoomId::try_from(name.0.clone())?).await?; // TODO handle bans let response = Presence { to: Some(Jid {