diff --git a/src/projections/xmpp/mod.rs b/src/projections/xmpp/mod.rs index e0f5f14..d9fc50b 100644 --- a/src/projections/xmpp/mod.rs +++ b/src/projections/xmpp/mod.rs @@ -23,8 +23,8 @@ use crate::core::player::PlayerRegistry; use crate::core::room::RoomRegistry; use crate::prelude::*; use crate::protos::xmpp; -use crate::protos::xmpp::bind::{BindResponse, Jid, Name, Server}; -use crate::protos::xmpp::client::Iq; +use crate::protos::xmpp::bind::{BindResponse, Jid, Name, Resource, Server}; +use crate::protos::xmpp::client::{Iq, Presence}; use crate::protos::xmpp::roster::RosterQuery; use crate::protos::xmpp::session::Session; use crate::protos::xmpp::stream::*; @@ -294,7 +294,7 @@ async fn socket_final( body: BindResponse(Jid { name: Name("darova".to_string()), server: Server("localhost".to_string()), - resource: b.0, + resource: Resource("kek".to_string()), }), }; req.serialize(&mut events); @@ -335,6 +335,19 @@ async fn socket_final( } }, proto::ClientPacket::Message(_) => todo!(), + proto::ClientPacket::Presence(p) => { + let mut events = vec![]; + let response = Presence { + to: Some("darova@localhost/kek".to_string()), + from: Some("darova@localhost/kek".to_string()), + ..Default::default() + }; + response.serialize(&mut events); + for i in events { + xml_writer.write_event_async(i).await?; + } + xml_writer.get_mut().flush().await?; + } } parser = proto::ClientPacket::parse(); } diff --git a/src/projections/xmpp/proto.rs b/src/projections/xmpp/proto.rs index eb69789..261d3f4 100644 --- a/src/projections/xmpp/proto.rs +++ b/src/projections/xmpp/proto.rs @@ -3,7 +3,7 @@ use quick_xml::events::Event; use quick_xml::name::{Namespace, ResolveResult}; use crate::protos::xmpp::bind::BindRequest; -use crate::protos::xmpp::client::{Iq, Message}; +use crate::protos::xmpp::client::{Iq, Message, Presence}; use crate::protos::xmpp::roster::RosterQuery; use crate::protos::xmpp::session::Session; use crate::util::xml::*; @@ -72,6 +72,7 @@ impl Parser for IqClientBodyParser { pub enum ClientPacket { Iq(Iq), Message(Message), + Presence(Presence), } #[derive(From)] @@ -88,8 +89,9 @@ impl FromXml for ClientPacket { #[derive(From)] enum ClientPacketParserInner { Initial, - Iq( as FromXml>::P), - Message(::P), + IqV( as FromXml>::P), + MessageV(::P), + PresenceV(::P), } impl Parser for ClientPacketParser { @@ -100,7 +102,7 @@ impl Parser for ClientPacketParser { namespace: ResolveResult, event: &Event<'a>, ) -> Continuation { - use ClientPacketParserInner::{Initial, Iq as IqV, Message as MessageV}; + use ClientPacketParserInner::*; match self.0 { Initial => { let Event::Start(bytes) = event else { @@ -109,11 +111,13 @@ impl Parser for ClientPacketParser { let name = bytes.name(); match_parser!(ClientPacketParser, name, namespace, event; Iq::, + Presence, Message ) } IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), + PresenceV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), } } } diff --git a/src/protos/xmpp/bind.rs b/src/protos/xmpp/bind.rs index e0fc08b..c790b0c 100644 --- a/src/protos/xmpp/bind.rs +++ b/src/protos/xmpp/bind.rs @@ -16,7 +16,7 @@ pub struct Name(pub String); pub struct Server(pub String); #[derive(PartialEq, Eq, Debug)] -pub struct Resource(pub(super) String); +pub struct Resource(pub String); #[derive(PartialEq, Eq, Debug)] pub struct Jid { diff --git a/src/protos/xmpp/client.rs b/src/protos/xmpp/client.rs index 6764dc2..daf384b 100644 --- a/src/protos/xmpp/client.rs +++ b/src/protos/xmpp/client.rs @@ -1,6 +1,6 @@ use derive_more::From; use quick_xml::events::attributes::Attribute; -use quick_xml::events::{BytesEnd, BytesStart, Event}; +use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use quick_xml::name::{QName, ResolveResult}; use crate::prelude::*; @@ -342,6 +342,198 @@ impl ToXml for Iq { } } +#[derive(PartialEq, Eq, Debug, Default)] +pub struct Presence { + pub to: Option, + pub from: Option, + pub priority: Option, + pub show: Option, + pub status: Vec, +} + +#[derive(PartialEq, Eq, Debug)] +pub enum PresenceShow { + Away, + Chat, + Dnd, + Xa, +} + +/// Presence priority is an integer number in range [-128; 127]. +/// +/// Presence priority < 0 means that the bound resource will never be chosen unless it was asked for specifically. +#[derive(PartialEq, Eq, Debug)] +pub struct PresencePriority(pub i8); + +impl PresenceShow { + pub fn from_str(s: &[u8]) -> Result { + use PresenceShow::*; + let s = std::str::from_utf8(s)?; + match s { + "away" => Ok(Away), + "chat" => Ok(Chat), + "dnd" => Ok(Dnd), + "xa" => Ok(Xa), + t => Err(ffail!("Unknown presence show type: {t}")), + } + } + pub fn as_str(&self) -> &'static str { + use PresenceShow::*; + match self { + Away => "away", + Chat => "chat", + Dnd => "dnd", + Xa => "xa", + } + } +} + +#[derive(From)] +pub struct PresenceParser(PresenceParserInner); +enum PresenceParserInner { + Initial, + InPresence(Presence), + InPriority(Presence), + InPriorityEnd(Presence), + InShow(Presence), + InShowEnd(Presence), + InStatus(Presence), + InStatusEnd(Presence), +} + +impl Parser for PresenceParser { + type Output = Result; + + fn consume<'a>( + self: Self, + namespace: ResolveResult, + event: &Event<'a>, + ) -> Continuation { + match self.0 { + PresenceParserInner::Initial => match event { + //TODO validate + Event::Start(bytes) => Continuation::Continue( + PresenceParserInner::InPresence(Presence::default()).into(), + ), + Event::Empty(bytes) => Continuation::Final(Ok(Presence::default())), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InPresence(p) => match event { + Event::Start(bytes) => match bytes.name().0 { + b"show" => Continuation::Continue(PresenceParserInner::InShow(p).into()), + b"status" => Continuation::Continue(PresenceParserInner::InStatus(p).into()), + b"priority" => { + Continuation::Continue(PresenceParserInner::InPriority(p).into()) + } + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + Event::End(_) => Continuation::Final(Ok(p)), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InPriority(p) => match event { + Event::Text(bytes) => { + match (|| { + let s = std::str::from_utf8(bytes)?; + let i = s.parse()?; + Ok(Presence { + priority: Some(PresencePriority(i)), + ..p + }) + })() { + Ok(res) => { + Continuation::Continue(PresenceParserInner::InPriorityEnd(res).into()) + } + Err(e) => Continuation::Final(Err(e)), + } + } + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InPriorityEnd(p) => match event { + Event::End(_) => Continuation::Continue(PresenceParserInner::InPresence(p).into()), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InShow(p) => match event { + Event::Text(bytes) => { + match (|| { + let i = PresenceShow::from_str(bytes)?; + Ok(Presence { show: Some(i), ..p }) + })() { + Ok(res) => { + Continuation::Continue(PresenceParserInner::InShowEnd(res).into()) + } + Err(e) => Continuation::Final(Err(e)), + } + } + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InShowEnd(p) => match event { + Event::End(_) => Continuation::Continue(PresenceParserInner::InPresence(p).into()), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InStatus(mut p) => match event { + Event::Text(bytes) => { + match (|| { + let i = std::str::from_utf8(bytes)?; + p.status.push(i.to_string()); + Ok(p) + })() { + Ok(res) => { + Continuation::Continue(PresenceParserInner::InStatusEnd(res).into()) + } + Err(e) => Continuation::Final(Err(e)), + } + } + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + PresenceParserInner::InStatusEnd(p) => match event { + Event::End(_) => Continuation::Continue(PresenceParserInner::InPresence(p).into()), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + } + } +} + +impl FromXml for Presence { + type P = PresenceParser; + + fn parse() -> Self::P { + PresenceParserInner::Initial.into() + } +} + +impl FromXmlTag for Presence { + const NAME: &'static str = "presence"; + const NS: &'static str = XMLNS; +} + +impl ToXml for Presence { + fn serialize(&self, events: &mut Vec>) { + let mut start = BytesStart::new("presence"); + if let Some(ref to) = self.to { + start.extend_attributes([Attribute { + key: QName(b"to"), + value: to.as_bytes().into(), + }]); + } + if let Some(ref from) = self.from { + start.extend_attributes([Attribute { + key: QName(b"from"), + value: from.as_bytes().into(), + }]); + } + events.push(Event::Start(start)); + if let Some(ref priority) = self.priority { + let s = priority.0.to_string(); + events.extend_from_slice(&[ + Event::Start(BytesStart::new(r#"priority"#)), + Event::Text(BytesText::new(s.as_str()).into_owned()), + Event::End(BytesEnd::new("priority")), + ]); + } + events.push(Event::End(BytesEnd::new("presence"))); + } +} + #[cfg(test)] mod tests { use crate::protos::xmpp::bind::{BindRequest, Resource};