feat(xmpp): presence parsing

This commit is contained in:
Nikita Vilunov 2023-03-15 15:27:48 +01:00
parent 6add6db371
commit 1cc4761aeb
4 changed files with 218 additions and 9 deletions

View File

@ -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();
}

View File

@ -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<IqClientBody>),
Message(Message),
Presence(Presence),
}
#[derive(From)]
@ -88,8 +89,9 @@ impl FromXml for ClientPacket {
#[derive(From)]
enum ClientPacketParserInner {
Initial,
Iq(<Iq<IqClientBody> as FromXml>::P),
Message(<Message as FromXml>::P),
IqV(<Iq<IqClientBody> as FromXml>::P),
MessageV(<Message as FromXml>::P),
PresenceV(<Presence as FromXml>::P),
}
impl Parser for ClientPacketParser {
@ -100,7 +102,7 @@ impl Parser for ClientPacketParser {
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output> {
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::<IqClientBody>,
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),
}
}
}

View File

@ -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 {

View File

@ -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<T: ToXml> ToXml for Iq<T> {
}
}
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Presence {
pub to: Option<String>,
pub from: Option<String>,
pub priority: Option<PresencePriority>,
pub show: Option<PresenceShow>,
pub status: Vec<String>,
}
#[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<Self> {
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<Presence>;
fn consume<'a>(
self: Self,
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output> {
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<Event<'static>>) {
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};