forked from lavina/lavina
feat(xmpp): presence parsing
This commit is contained in:
parent
6add6db371
commit
1cc4761aeb
|
@ -23,8 +23,8 @@ use crate::core::player::PlayerRegistry;
|
||||||
use crate::core::room::RoomRegistry;
|
use crate::core::room::RoomRegistry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::protos::xmpp;
|
use crate::protos::xmpp;
|
||||||
use crate::protos::xmpp::bind::{BindResponse, Jid, Name, Server};
|
use crate::protos::xmpp::bind::{BindResponse, Jid, Name, Resource, Server};
|
||||||
use crate::protos::xmpp::client::Iq;
|
use crate::protos::xmpp::client::{Iq, Presence};
|
||||||
use crate::protos::xmpp::roster::RosterQuery;
|
use crate::protos::xmpp::roster::RosterQuery;
|
||||||
use crate::protos::xmpp::session::Session;
|
use crate::protos::xmpp::session::Session;
|
||||||
use crate::protos::xmpp::stream::*;
|
use crate::protos::xmpp::stream::*;
|
||||||
|
@ -294,7 +294,7 @@ async fn socket_final(
|
||||||
body: BindResponse(Jid {
|
body: BindResponse(Jid {
|
||||||
name: Name("darova".to_string()),
|
name: Name("darova".to_string()),
|
||||||
server: Server("localhost".to_string()),
|
server: Server("localhost".to_string()),
|
||||||
resource: b.0,
|
resource: Resource("kek".to_string()),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
req.serialize(&mut events);
|
req.serialize(&mut events);
|
||||||
|
@ -335,6 +335,19 @@ async fn socket_final(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
proto::ClientPacket::Message(_) => todo!(),
|
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();
|
parser = proto::ClientPacket::parse();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use quick_xml::events::Event;
|
||||||
use quick_xml::name::{Namespace, ResolveResult};
|
use quick_xml::name::{Namespace, ResolveResult};
|
||||||
|
|
||||||
use crate::protos::xmpp::bind::BindRequest;
|
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::roster::RosterQuery;
|
||||||
use crate::protos::xmpp::session::Session;
|
use crate::protos::xmpp::session::Session;
|
||||||
use crate::util::xml::*;
|
use crate::util::xml::*;
|
||||||
|
@ -72,6 +72,7 @@ impl Parser for IqClientBodyParser {
|
||||||
pub enum ClientPacket {
|
pub enum ClientPacket {
|
||||||
Iq(Iq<IqClientBody>),
|
Iq(Iq<IqClientBody>),
|
||||||
Message(Message),
|
Message(Message),
|
||||||
|
Presence(Presence),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(From)]
|
#[derive(From)]
|
||||||
|
@ -88,8 +89,9 @@ impl FromXml for ClientPacket {
|
||||||
#[derive(From)]
|
#[derive(From)]
|
||||||
enum ClientPacketParserInner {
|
enum ClientPacketParserInner {
|
||||||
Initial,
|
Initial,
|
||||||
Iq(<Iq<IqClientBody> as FromXml>::P),
|
IqV(<Iq<IqClientBody> as FromXml>::P),
|
||||||
Message(<Message as FromXml>::P),
|
MessageV(<Message as FromXml>::P),
|
||||||
|
PresenceV(<Presence as FromXml>::P),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser for ClientPacketParser {
|
impl Parser for ClientPacketParser {
|
||||||
|
@ -100,7 +102,7 @@ impl Parser for ClientPacketParser {
|
||||||
namespace: ResolveResult,
|
namespace: ResolveResult,
|
||||||
event: &Event<'a>,
|
event: &Event<'a>,
|
||||||
) -> Continuation<Self, Self::Output> {
|
) -> Continuation<Self, Self::Output> {
|
||||||
use ClientPacketParserInner::{Initial, Iq as IqV, Message as MessageV};
|
use ClientPacketParserInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Initial => {
|
Initial => {
|
||||||
let Event::Start(bytes) = event else {
|
let Event::Start(bytes) = event else {
|
||||||
|
@ -109,11 +111,13 @@ impl Parser for ClientPacketParser {
|
||||||
let name = bytes.name();
|
let name = bytes.name();
|
||||||
match_parser!(ClientPacketParser, name, namespace, event;
|
match_parser!(ClientPacketParser, name, namespace, event;
|
||||||
Iq::<IqClientBody>,
|
Iq::<IqClientBody>,
|
||||||
|
Presence,
|
||||||
Message
|
Message
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
||||||
MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
||||||
|
PresenceV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct Name(pub String);
|
||||||
pub struct Server(pub String);
|
pub struct Server(pub String);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Resource(pub(super) String);
|
pub struct Resource(pub String);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Jid {
|
pub struct Jid {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use derive_more::From;
|
use derive_more::From;
|
||||||
use quick_xml::events::attributes::Attribute;
|
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 quick_xml::name::{QName, ResolveResult};
|
||||||
|
|
||||||
use crate::prelude::*;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::protos::xmpp::bind::{BindRequest, Resource};
|
use crate::protos::xmpp::bind::{BindRequest, Resource};
|
||||||
|
|
Loading…
Reference in New Issue