From 6d493d83a3033ff86cbb7defa3bcc4d7940cd811 Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Mon, 15 Apr 2024 18:18:51 +0200 Subject: [PATCH] xmpp: use the Jid type in IQs' to and from fields, separate presence handling --- crates/projection-xmpp/src/iq.rs | 36 ++++++++--- crates/projection-xmpp/src/presence.rs | 87 +++++++++++++++----------- crates/proto-xmpp/src/client.rs | 20 +++--- 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/crates/projection-xmpp/src/iq.rs b/crates/projection-xmpp/src/iq.rs index 6766e19..eebbc4d 100644 --- a/crates/projection-xmpp/src/iq.rs +++ b/crates/projection-xmpp/src/iq.rs @@ -3,7 +3,7 @@ use quick_xml::events::Event; use lavina_core::room::RoomRegistry; -use proto_xmpp::bind::{BindResponse, Jid, Name, Resource, Server}; +use proto_xmpp::bind::{BindResponse, Jid, Name, Server}; use proto_xmpp::client::{Iq, IqError, IqErrorType, IqType}; use proto_xmpp::disco::{Feature, Identity, InfoQuery, Item, ItemQuery}; use proto_xmpp::roster::RosterQuery; @@ -17,7 +17,7 @@ use proto_xmpp::xml::ToXml; impl<'a> XmppConnection<'a> { pub async fn handle_iq(&self, output: &mut Vec>, iq: Iq) { match iq.body { - IqClientBody::Bind(b) => { + IqClientBody::Bind(_) => { let req = Iq { from: None, id: iq.id, @@ -52,7 +52,7 @@ impl<'a> XmppConnection<'a> { req.serialize(output); } IqClientBody::DiscoInfo(info) => { - let response = self.disco_info(iq.to.as_deref(), &info); + let response = self.disco_info(iq.to.as_ref(), &info); let req = Iq { from: iq.to, id: iq.id, @@ -63,7 +63,7 @@ impl<'a> XmppConnection<'a> { req.serialize(output); } IqClientBody::DiscoItem(item) => { - let response = self.disco_items(iq.to.as_deref(), &item, self.rooms).await; + let response = self.disco_items(iq.to.as_ref(), &item, self.rooms).await; let req = Iq { from: iq.to, id: iq.id, @@ -88,12 +88,16 @@ impl<'a> XmppConnection<'a> { } } - fn disco_info(&self, to: Option<&str>, req: &InfoQuery) -> InfoQuery { + fn disco_info(&self, to: Option<&Jid>, req: &InfoQuery) -> InfoQuery { let identity; let feature; match to { - Some(r) if r == &*self.hostname => { + Some(Jid { + name: None, + server, + resource: None, + }) if server.0 == self.hostname => { identity = vec![Identity { category: "server".into(), name: None, @@ -106,7 +110,11 @@ impl<'a> XmppConnection<'a> { Feature::new("presence"), ] } - Some(r) if r == &*self.hostname_rooms => { + Some(Jid { + name: None, + server, + resource: None, + }) if server.0 == self.hostname_rooms => { identity = vec![Identity { category: "conference".into(), name: Some("Chat rooms".into()), @@ -130,9 +138,13 @@ impl<'a> XmppConnection<'a> { } } - async fn disco_items(&self, to: Option<&str>, req: &ItemQuery, rooms: &RoomRegistry) -> ItemQuery { + async fn disco_items(&self, to: Option<&Jid>, req: &ItemQuery, rooms: &RoomRegistry) -> ItemQuery { let item = match to { - Some(r) if r == &*self.hostname => { + Some(Jid { + name: None, + server, + resource: None, + }) if server.0 == self.hostname => { vec![Item { jid: Jid { name: None, @@ -143,7 +155,11 @@ impl<'a> XmppConnection<'a> { node: None, }] } - Some(r) if r == &*self.hostname_rooms => { + Some(Jid { + name: None, + server, + resource: None, + }) if server.0 == self.hostname_rooms => { let room_list = rooms.get_all_rooms().await; room_list .into_iter() diff --git a/crates/projection-xmpp/src/presence.rs b/crates/projection-xmpp/src/presence.rs index 6f9540e..82ccb61 100644 --- a/crates/projection-xmpp/src/presence.rs +++ b/crates/projection-xmpp/src/presence.rs @@ -4,7 +4,7 @@ use quick_xml::events::Event; use lavina_core::prelude::*; use lavina_core::room::RoomId; -use proto_xmpp::bind::{Jid, Server}; +use proto_xmpp::bind::{Jid, Name, Server}; use proto_xmpp::client::Presence; use proto_xmpp::xml::{Ignore, ToXml}; @@ -12,42 +12,59 @@ use crate::XmppConnection; impl<'a> XmppConnection<'a> { pub async fn handle_presence(&mut self, output: &mut Vec>, p: Presence) -> Result<()> { - let response = if p.to.is_none() { - 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() + match p.to { + None => { + self.self_presence(output).await; } - } else if let Some(Jid { - name: Some(name), - server, - resource: Some(resource), - }) = p.to - { - let a = self.user_handle.join_room(RoomId::from(name.0.clone())?).await?; - 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()), - }), - ..Default::default() + Some(Jid { + name: Some(name), + server, + // resources in MUCs are members' personas – not implemented (yet?) + resource: Some(_), + }) if server.0 == self.hostname_rooms => { + self.muc_presence(name, output).await?; } - } else { - Presence::<()>::default() + _ => { + // TODO other presence cases + let response = Presence::<()>::default(); + response.serialize(output); + } + } + Ok(()) + } + + async fn self_presence(&mut self, output: &mut Vec>) { + 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); + } + + async fn muc_presence(&mut self, name: Name, output: &mut Vec>) -> Result<()> { + 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()), + }), + ..Default::default() }; response.serialize(output); Ok(()) diff --git a/crates/proto-xmpp/src/client.rs b/crates/proto-xmpp/src/client.rs index 4943283..85b3979 100644 --- a/crates/proto-xmpp/src/client.rs +++ b/crates/proto-xmpp/src/client.rs @@ -295,9 +295,9 @@ impl ToXml for IqError { #[derive(PartialEq, Eq, Debug)] pub struct Iq { - pub from: Option, + pub from: Option, pub id: String, - pub to: Option, + pub to: Option, pub r#type: IqType, pub body: T, } @@ -323,9 +323,9 @@ enum IqParserInner { Final(IqParserState), } struct IqParserState { - pub from: Option, + pub from: Option, pub id: Option, - pub to: Option, + pub to: Option, pub r#type: Option, pub body: Option, } @@ -348,13 +348,15 @@ impl Parser for IqParser { let attr = fail_fast!(attr); if attr.key.0 == b"from" { let value = fail_fast!(std::str::from_utf8(&*attr.value)); - state.from = Some(value.to_string()) + let value = fail_fast!(Jid::from_string(value)); + state.from = Some(value) } else if attr.key.0 == b"id" { let value = fail_fast!(std::str::from_utf8(&*attr.value)); state.id = Some(value.to_string()) } else if attr.key.0 == b"to" { let value = fail_fast!(std::str::from_utf8(&*attr.value)); - state.to = Some(value.to_string()) + let value = fail_fast!(Jid::from_string(value)); + state.to = Some(value) } else if attr.key.0 == b"type" { let value = fail_fast!(IqType::from_str(&*attr.value)); state.r#type = Some(value); @@ -431,15 +433,17 @@ impl ToXml for Iq { let mut start = BytesStart::new(start); let mut attrs = vec![]; if let Some(ref from) = self.from { + let value = from.to_string().into_bytes(); attrs.push(Attribute { key: QName(b"from"), - value: from.as_bytes().into(), + value: value.into(), }); }; if let Some(ref to) = self.to { + let value = to.to_string().into_bytes(); attrs.push(Attribute { key: QName(b"to"), - value: to.as_bytes().into(), + value: value.into(), }); } attrs.push(Attribute {