xmpp: use the Jid type in IQs' to and from fields, separate presence handling

This commit is contained in:
Nikita Vilunov 2024-04-15 18:18:51 +02:00
parent 757d7c5665
commit 6d493d83a3
3 changed files with 90 additions and 53 deletions

View File

@ -3,7 +3,7 @@
use quick_xml::events::Event; use quick_xml::events::Event;
use lavina_core::room::RoomRegistry; 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::client::{Iq, IqError, IqErrorType, IqType};
use proto_xmpp::disco::{Feature, Identity, InfoQuery, Item, ItemQuery}; use proto_xmpp::disco::{Feature, Identity, InfoQuery, Item, ItemQuery};
use proto_xmpp::roster::RosterQuery; use proto_xmpp::roster::RosterQuery;
@ -17,7 +17,7 @@ use proto_xmpp::xml::ToXml;
impl<'a> XmppConnection<'a> { impl<'a> XmppConnection<'a> {
pub async fn handle_iq(&self, output: &mut Vec<Event<'static>>, iq: Iq<IqClientBody>) { pub async fn handle_iq(&self, output: &mut Vec<Event<'static>>, iq: Iq<IqClientBody>) {
match iq.body { match iq.body {
IqClientBody::Bind(b) => { IqClientBody::Bind(_) => {
let req = Iq { let req = Iq {
from: None, from: None,
id: iq.id, id: iq.id,
@ -52,7 +52,7 @@ impl<'a> XmppConnection<'a> {
req.serialize(output); req.serialize(output);
} }
IqClientBody::DiscoInfo(info) => { 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 { let req = Iq {
from: iq.to, from: iq.to,
id: iq.id, id: iq.id,
@ -63,7 +63,7 @@ impl<'a> XmppConnection<'a> {
req.serialize(output); req.serialize(output);
} }
IqClientBody::DiscoItem(item) => { 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 { let req = Iq {
from: iq.to, from: iq.to,
id: iq.id, 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 identity;
let feature; let feature;
match to { match to {
Some(r) if r == &*self.hostname => { Some(Jid {
name: None,
server,
resource: None,
}) if server.0 == self.hostname => {
identity = vec![Identity { identity = vec![Identity {
category: "server".into(), category: "server".into(),
name: None, name: None,
@ -106,7 +110,11 @@ impl<'a> XmppConnection<'a> {
Feature::new("presence"), 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 { identity = vec![Identity {
category: "conference".into(), category: "conference".into(),
name: Some("Chat rooms".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 { let item = match to {
Some(r) if r == &*self.hostname => { Some(Jid {
name: None,
server,
resource: None,
}) if server.0 == self.hostname => {
vec![Item { vec![Item {
jid: Jid { jid: Jid {
name: None, name: None,
@ -143,7 +155,11 @@ impl<'a> XmppConnection<'a> {
node: None, 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; let room_list = rooms.get_all_rooms().await;
room_list room_list
.into_iter() .into_iter()

View File

@ -4,7 +4,7 @@ use quick_xml::events::Event;
use lavina_core::prelude::*; use lavina_core::prelude::*;
use lavina_core::room::RoomId; 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::client::Presence;
use proto_xmpp::xml::{Ignore, ToXml}; use proto_xmpp::xml::{Ignore, ToXml};
@ -12,42 +12,59 @@ use crate::XmppConnection;
impl<'a> XmppConnection<'a> { impl<'a> XmppConnection<'a> {
pub async fn handle_presence(&mut self, output: &mut Vec<Event<'static>>, p: Presence<Ignore>) -> Result<()> { pub async fn handle_presence(&mut self, output: &mut Vec<Event<'static>>, p: Presence<Ignore>) -> Result<()> {
let response = if p.to.is_none() { match p.to {
Presence::<()> { None => {
to: Some(Jid { self.self_presence(output).await;
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()
} }
} else if let Some(Jid { Some(Jid {
name: Some(name), name: Some(name),
server, server,
resource: Some(resource), // resources in MUCs are members' personas not implemented (yet?)
}) = p.to resource: Some(_),
{ }) if server.0 == self.hostname_rooms => {
let a = self.user_handle.join_room(RoomId::from(name.0.clone())?).await?; self.muc_presence(name, output).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()
} }
} else { _ => {
Presence::<()>::default() // TODO other presence cases
let response = Presence::<()>::default();
response.serialize(output);
}
}
Ok(())
}
async fn self_presence(&mut self, output: &mut Vec<Event<'static>>) {
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<Event<'static>>) -> 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); response.serialize(output);
Ok(()) Ok(())

View File

@ -295,9 +295,9 @@ impl ToXml for IqError {
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct Iq<T> { pub struct Iq<T> {
pub from: Option<String>, pub from: Option<Jid>,
pub id: String, pub id: String,
pub to: Option<String>, pub to: Option<Jid>,
pub r#type: IqType, pub r#type: IqType,
pub body: T, pub body: T,
} }
@ -323,9 +323,9 @@ enum IqParserInner<T: FromXml> {
Final(IqParserState<T>), Final(IqParserState<T>),
} }
struct IqParserState<T> { struct IqParserState<T> {
pub from: Option<String>, pub from: Option<Jid>,
pub id: Option<String>, pub id: Option<String>,
pub to: Option<String>, pub to: Option<Jid>,
pub r#type: Option<IqType>, pub r#type: Option<IqType>,
pub body: Option<T>, pub body: Option<T>,
} }
@ -348,13 +348,15 @@ impl<T: FromXml> Parser for IqParser<T> {
let attr = fail_fast!(attr); let attr = fail_fast!(attr);
if attr.key.0 == b"from" { if attr.key.0 == b"from" {
let value = fail_fast!(std::str::from_utf8(&*attr.value)); 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" { } else if attr.key.0 == b"id" {
let value = fail_fast!(std::str::from_utf8(&*attr.value)); let value = fail_fast!(std::str::from_utf8(&*attr.value));
state.id = Some(value.to_string()) state.id = Some(value.to_string())
} else if attr.key.0 == b"to" { } else if attr.key.0 == b"to" {
let value = fail_fast!(std::str::from_utf8(&*attr.value)); 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" { } else if attr.key.0 == b"type" {
let value = fail_fast!(IqType::from_str(&*attr.value)); let value = fail_fast!(IqType::from_str(&*attr.value));
state.r#type = Some(value); state.r#type = Some(value);
@ -431,15 +433,17 @@ impl<T: ToXml> ToXml for Iq<T> {
let mut start = BytesStart::new(start); let mut start = BytesStart::new(start);
let mut attrs = vec![]; let mut attrs = vec![];
if let Some(ref from) = self.from { if let Some(ref from) = self.from {
let value = from.to_string().into_bytes();
attrs.push(Attribute { attrs.push(Attribute {
key: QName(b"from"), key: QName(b"from"),
value: from.as_bytes().into(), value: value.into(),
}); });
}; };
if let Some(ref to) = self.to { if let Some(ref to) = self.to {
let value = to.to_string().into_bytes();
attrs.push(Attribute { attrs.push(Attribute {
key: QName(b"to"), key: QName(b"to"),
value: to.as_bytes().into(), value: value.into(),
}); });
} }
attrs.push(Attribute { attrs.push(Attribute {