Compare commits

..

No commits in common. "6ce8c7a99fb559912a4e89c18c954d25a096d732" and "d02c394e3472059b584dd317de4de1b30b7a5aec" have entirely different histories.

4 changed files with 41 additions and 220 deletions

View File

@ -20,8 +20,7 @@ impl<'a> XmppConnection<'a> {
}) = m.to }) = m.to
{ {
if server.0.as_ref() == &*self.hostname_rooms && m.r#type == MessageType::Groupchat { if server.0.as_ref() == &*self.hostname_rooms && m.r#type == MessageType::Groupchat {
let Some(body) = &m.body else { return Ok(()) }; self.user_handle.send_message(RoomId::try_from(name.0.clone())?, m.body.clone().into()).await?;
self.user_handle.send_message(RoomId::try_from(name.0.clone())?, body.clone()).await?;
Message::<()> { Message::<()> {
to: Some(Jid { to: Some(Jid {
name: Some(self.user.xmpp_name.clone()), name: Some(self.user.xmpp_name.clone()),
@ -43,8 +42,7 @@ impl<'a> XmppConnection<'a> {
.serialize(output); .serialize(output);
Ok(()) Ok(())
} else if server.0.as_ref() == &*self.hostname && m.r#type == MessageType::Chat { } else if server.0.as_ref() == &*self.hostname && m.r#type == MessageType::Chat {
let Some(body) = &m.body else { return Ok(()) }; self.user_handle.send_dialog_message(PlayerId::from(name.0.clone())?, m.body.clone()).await?;
self.user_handle.send_dialog_message(PlayerId::from(name.0.clone())?, body.clone()).await?;
Ok(()) Ok(())
} else { } else {
todo!() todo!()

View File

@ -6,8 +6,8 @@ use serde::Serialize;
use lavina_core::room::RoomId; use lavina_core::room::RoomId;
use proto_xmpp::bind::{Jid, Name, Resource, Server}; use proto_xmpp::bind::{Jid, Name, Resource, Server};
use proto_xmpp::client::{Message, MessageType, Presence, Subject}; use proto_xmpp::client::Presence;
use proto_xmpp::muc::{Affiliation, Delay, Role, XUser, XUserItem, XmppHistoryMessage}; use proto_xmpp::muc::{Delay, XUser, XmppHistoryMessage};
use proto_xmpp::xml::{Ignore, ToXml}; use proto_xmpp::xml::{Ignore, ToXml};
use crate::XmppConnection; use crate::XmppConnection;
@ -24,28 +24,8 @@ impl<'a> XmppConnection<'a> {
// resources in MUCs are members' personas not implemented (yet?) // resources in MUCs are members' personas not implemented (yet?)
resource: Some(_), resource: Some(_),
}) if server.0 == self.hostname_rooms => { }) if server.0 == self.hostname_rooms => {
let mut muc_presence = self.retrieve_muc_presence(&name).await?; let 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); muc_presence.serialize(output);
subject.serialize(output);
let messages = self.retrieve_message_history(&name).await?; let messages = self.retrieve_message_history(&name).await?;
for message in messages { for message in messages {
@ -82,9 +62,7 @@ impl<'a> XmppConnection<'a> {
}; };
response.serialize(output); response.serialize(output);
} }
e => { _ => todo!(),
tracing::error!("TODO: unknown presence type: {e:?}");
}
} }
} }
@ -102,19 +80,7 @@ impl<'a> XmppConnection<'a> {
server: Server(self.hostname_rooms.clone()), server: Server(self.hostname_rooms.clone()),
resource: Some(self.user.xmpp_muc_name.clone()), resource: Some(self.user.xmpp_muc_name.clone()),
}), }),
custom: vec![XUser { custom: vec![XUser],
item: XUserItem {
affiliation: Affiliation::Member,
role: Role::Participant,
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, // TODO we don't know this for sure at this point
}],
..Default::default() ..Default::default()
}; };
Ok(response) Ok(response)
@ -176,7 +142,7 @@ mod tests {
use lavina_core::player::PlayerId; use lavina_core::player::PlayerId;
use proto_xmpp::bind::{Jid, Name, Resource, Server}; use proto_xmpp::bind::{Jid, Name, Resource, Server};
use proto_xmpp::client::Presence; use proto_xmpp::client::Presence;
use proto_xmpp::muc::{Affiliation, Role, XUser, XUserItem}; use proto_xmpp::muc::XUser;
use crate::testkit::{expect_user_authenticated, TestServer}; use crate::testkit::{expect_user_authenticated, TestServer};
use crate::Authenticated; use crate::Authenticated;
@ -198,7 +164,7 @@ mod tests {
let mut player_conn = server.core.connect_to_player(&user.player_id).await; 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 mut conn = expect_user_authenticated(&server, &user, &mut player_conn).await.unwrap();
let muc_presence = conn.retrieve_muc_presence(&user.xmpp_name).await.unwrap(); let response = conn.retrieve_muc_presence(&user.xmpp_name).await.unwrap();
let expected = Presence { let expected = Presence {
to: Some(Jid { to: Some(Jid {
name: Some(conn.user.xmpp_name.clone()), name: Some(conn.user.xmpp_name.clone()),
@ -210,22 +176,10 @@ mod tests {
server: Server(conn.hostname_rooms.clone()), server: Server(conn.hostname_rooms.clone()),
resource: Some(conn.user.xmpp_muc_name.clone()), resource: Some(conn.user.xmpp_muc_name.clone()),
}), }),
custom: vec![XUser { custom: vec![XUser],
item: XUserItem {
affiliation: Affiliation::Member,
role: Role::Participant,
jid: Jid {
name: Some(conn.user.xmpp_name.clone()),
server: Server(conn.hostname.clone()),
resource: Some(conn.user.xmpp_resource.clone()),
},
},
self_presence: true,
just_created: false,
}],
..Default::default() ..Default::default()
}; };
assert_eq!(expected, muc_presence); assert_eq!(expected, response);
server.shutdown().await.unwrap(); server.shutdown().await.unwrap();
Ok(()) Ok(())
@ -262,19 +216,7 @@ mod tests {
server: Server(conn.hostname_rooms.clone()), server: Server(conn.hostname_rooms.clone()),
resource: Some(conn.user.xmpp_muc_name.clone()), resource: Some(conn.user.xmpp_muc_name.clone()),
}), }),
custom: vec![XUser { custom: vec![XUser],
item: XUserItem {
affiliation: Affiliation::Member,
role: Role::Participant,
jid: Jid {
name: Some(conn.user.xmpp_name.clone()),
server: Server(conn.hostname.clone()),
resource: Some(conn.user.xmpp_resource.clone()),
},
},
self_presence: true,
just_created: false,
}],
..Default::default() ..Default::default()
}; };
assert_eq!(expected, response); assert_eq!(expected, response);

View File

@ -20,8 +20,8 @@ pub struct Message<T> {
// default is Normal // default is Normal
pub r#type: MessageType, pub r#type: MessageType,
pub lang: Option<Str>, pub lang: Option<Str>,
pub subject: Option<Subject>, pub subject: Option<Str>,
pub body: Option<Str>, pub body: Str,
pub custom: Vec<T>, pub custom: Vec<T>,
} }
@ -38,20 +38,6 @@ impl<T: FromXml> FromXml for Message<T> {
} }
} }
#[derive(PartialEq, Eq, Debug)]
pub struct Subject(pub Option<Str>);
impl ToXml for Subject {
fn serialize(&self, events: &mut Vec<Event<'static>>) {
if let Some(ref s) = self.0 {
events.push(Event::Start(BytesStart::new("subject")));
events.push(Event::Text(BytesText::new(s).into_owned()));
events.push(Event::End(BytesEnd::new("subject")));
} else {
events.push(Event::Empty(BytesStart::new("subject")));
}
}
}
#[derive(From)] #[derive(From)]
struct MessageParser<T: FromXml>(MessageParserInner<T>); struct MessageParser<T: FromXml>(MessageParserInner<T>);
@ -71,7 +57,7 @@ struct MessageParserState<T> {
to: Option<Jid>, to: Option<Jid>,
r#type: MessageType, r#type: MessageType,
lang: Option<Str>, lang: Option<Str>,
subject: Option<Subject>, subject: Option<Str>,
body: Option<Str>, body: Option<Str>,
custom: Vec<T>, custom: Vec<T>,
} }
@ -135,16 +121,22 @@ impl<T: FromXml> Parser for MessageParser<T> {
} }
} }
} }
Event::End(_) => Continuation::Final(Ok(Message { Event::End(_) => {
if let Some(body) = state.body {
Continuation::Final(Ok(Message {
from: state.from, from: state.from,
id: state.id, id: state.id,
to: state.to, to: state.to,
r#type: state.r#type, r#type: state.r#type,
lang: state.lang, lang: state.lang,
subject: state.subject, subject: state.subject,
body: state.body, body,
custom: state.custom, custom: state.custom,
})), }))
} else {
Continuation::Final(Err(ffail!("Body not found")))
}
}
Event::Empty(_) => { Event::Empty(_) => {
let parser = T::parse(); let parser = T::parse();
match parser.consume(namespace, event) { match parser.consume(namespace, event) {
@ -161,7 +153,7 @@ impl<T: FromXml> Parser for MessageParser<T> {
InSubject(mut state) => match event { InSubject(mut state) => match event {
Event::Text(ref bytes) => { Event::Text(ref bytes) => {
let subject = fail_fast!(std::str::from_utf8(&*bytes)); let subject = fail_fast!(std::str::from_utf8(&*bytes));
state.subject = Some(Subject(Some(subject.into()))); state.subject = Some(subject.into());
Continuation::Continue(InSubject(state).into()) Continuation::Continue(InSubject(state).into())
} }
Event::End(_) => Continuation::Continue(Outer(state).into()), Event::End(_) => Continuation::Continue(Outer(state).into()),
@ -216,14 +208,9 @@ impl<T: ToXml> ToXml for Message<T> {
value: self.r#type.as_str().as_bytes().into(), value: self.r#type.as_str().as_bytes().into(),
}); });
events.push(Event::Start(bytes)); events.push(Event::Start(bytes));
if let Some(subject) = &self.subject {
subject.serialize(events);
}
if let Some(body) = &self.body {
events.push(Event::Start(BytesStart::new("body"))); events.push(Event::Start(BytesStart::new("body")));
events.push(Event::Text(BytesText::new(body).into_owned())); events.push(Event::Text(BytesText::new(&self.body).into_owned()));
events.push(Event::End(BytesEnd::new("body"))); events.push(Event::End(BytesEnd::new("body")));
}
events.push(Event::End(BytesEnd::new("message"))); events.push(Event::End(BytesEnd::new("message")));
} }
} }
@ -500,7 +487,6 @@ impl<T: ToXml> ToXml for Iq<T> {
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct Presence<T> { pub struct Presence<T> {
pub id: Option<String>,
pub to: Option<Jid>, pub to: Option<Jid>,
pub from: Option<Jid>, pub from: Option<Jid>,
pub priority: Option<PresencePriority>, pub priority: Option<PresencePriority>,
@ -513,7 +499,6 @@ pub struct Presence<T> {
impl<T> Default for Presence<T> { impl<T> Default for Presence<T> {
fn default() -> Self { fn default() -> Self {
Self { Self {
id: Default::default(),
to: Default::default(), to: Default::default(),
from: Default::default(), from: Default::default(),
priority: Default::default(), priority: Default::default(),
@ -588,10 +573,6 @@ impl<T: FromXml> FromXml for Presence<T> {
let s = std::str::from_utf8(&attr.value)?; let s = std::str::from_utf8(&attr.value)?;
p.r#type = Some(s.into()); p.r#type = Some(s.into());
} }
b"id" => {
let s = std::str::from_utf8(&attr.value)?;
p.id = Option::from(s.to_string());
}
_ => {} _ => {}
} }
} }
@ -679,12 +660,6 @@ impl<T: ToXml> ToXml for Presence<T> {
value: from.to_string().as_bytes().into(), value: from.to_string().as_bytes().into(),
}]); }]);
} }
if let Some(ref id) = self.id {
start.extend_attributes([Attribute {
key: QName(b"id"),
value: id.to_string().as_bytes().into(),
}]);
}
events.push(Event::Start(start)); events.push(Event::Start(start));
if let Some(ref priority) = self.priority { if let Some(ref priority) = self.priority {
let s = priority.0.to_string(); let s = priority.0.to_string();
@ -723,8 +698,8 @@ mod tests {
}), }),
r#type: MessageType::Chat, r#type: MessageType::Chat,
lang: None, lang: None,
subject: Some(Subject(Some("daa".into()))), subject: Some("daa".into()),
body: Some("bbb".into()), body: "bbb".into(),
custom: vec![Ignore], custom: vec![Ignore],
} }
) )
@ -746,8 +721,8 @@ mod tests {
}), }),
r#type: MessageType::Chat, r#type: MessageType::Chat,
lang: None, lang: None,
subject: Some(Subject(Some("daa".into()))), subject: Some("daa".into()),
body: Some("bbb".into()), body: "bbb".into(),
custom: vec![Ignore], custom: vec![Ignore],
} }
) )

View File

@ -146,108 +146,14 @@ impl FromXml for X {
} }
} }
/// Information about an MUC member. May contain [MUC status codes](https://xmpp.org/registrar/mucstatus.html).
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct XUser { pub struct XUser;
pub item: XUserItem,
/// Code 110. The receiver is the user referred to in the presence stanza.
pub self_presence: bool,
/// Code 201. The room from which the presence stanza was sent was just created.
pub just_created: bool,
}
impl ToXml for XUser { impl ToXml for XUser {
fn serialize(&self, output: &mut Vec<Event<'static>>) { fn serialize(&self, output: &mut Vec<Event<'static>>) {
let mut tag = BytesStart::new("x"); let mut tag = BytesStart::new("x");
tag.push_attribute(("xmlns", XMLNS_USER)); tag.push_attribute(("xmlns", XMLNS_USER));
output.push(Event::Start(tag)); output.push(Event::Empty(tag));
self.item.serialize(output);
if self.self_presence {
let mut meg = BytesStart::new("status");
meg.push_attribute(("code", "110"));
output.push(Event::Empty(meg));
}
if self.just_created {
let mut meg = BytesStart::new("status");
meg.push_attribute(("code", "201"));
output.push(Event::Empty(meg));
}
output.push(Event::End(BytesEnd::new("x")));
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct XUserItem {
pub affiliation: Affiliation,
pub jid: Jid,
pub role: Role,
}
impl ToXml for XUserItem {
fn serialize(&self, output: &mut Vec<Event<'static>>) {
let mut meg = BytesStart::new("item");
meg.push_attribute(("affiliation", self.affiliation.to_str()));
meg.push_attribute(("role", self.role.to_str()));
meg.push_attribute(("jid", self.jid.to_string().as_str()));
output.push(Event::Empty(meg));
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Affiliation {
Owner,
Admin,
Member,
Outcast,
None,
}
impl Affiliation {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"owner" => Some(Self::Owner),
"admin" => Some(Self::Admin),
"member" => Some(Self::Member),
"outcast" => Some(Self::Outcast),
"none" => Some(Self::None),
_ => None,
}
}
pub fn to_str(&self) -> &str {
match self {
Self::Owner => "owner",
Self::Admin => "admin",
Self::Member => "member",
Self::Outcast => "outcast",
Self::None => "none",
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Role {
Moderator,
Participant,
Visitor,
None,
}
impl Role {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"moderator" => Some(Self::Moderator),
"participant" => Some(Self::Participant),
"visitor" => Some(Self::Visitor),
"none" => Some(Self::None),
_ => None,
}
}
pub fn to_str(&self) -> &str {
match self {
Self::Moderator => "moderator",
Self::Participant => "participant",
Self::Visitor => "visitor",
Self::None => "none",
}
} }
} }