diff --git a/src/projections/xmpp/mod.rs b/src/projections/xmpp/mod.rs index dbf9965..e0f5f14 100644 --- a/src/projections/xmpp/mod.rs +++ b/src/projections/xmpp/mod.rs @@ -25,6 +25,7 @@ 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::roster::RosterQuery; use crate::protos::xmpp::session::Session; use crate::protos::xmpp::stream::*; use crate::util::xml::{Continuation, FromXml, Parser, ToXml}; @@ -317,6 +318,21 @@ async fn socket_final( } xml_writer.get_mut().flush().await?; } + proto::IqClientBody::Roster(_) => { + let mut events = vec![]; + let req = Iq { + from: None, + id: iq.id, + to: None, + r#type: xmpp::client::IqType::Result, + body: RosterQuery, + }; + req.serialize(&mut events); + for i in events { + xml_writer.write_event_async(i).await?; + } + xml_writer.get_mut().flush().await?; + } }, proto::ClientPacket::Message(_) => todo!(), } diff --git a/src/projections/xmpp/proto.rs b/src/projections/xmpp/proto.rs index 909774c..eb69789 100644 --- a/src/projections/xmpp/proto.rs +++ b/src/projections/xmpp/proto.rs @@ -4,8 +4,9 @@ use quick_xml::name::{Namespace, ResolveResult}; use crate::protos::xmpp::bind::BindRequest; use crate::protos::xmpp::client::{Iq, Message}; +use crate::protos::xmpp::roster::RosterQuery; use crate::protos::xmpp::session::Session; -use crate::util::xml::{Continuation, FromXml, FromXmlTag, Parser}; +use crate::util::xml::*; use crate::prelude::*; @@ -13,6 +14,7 @@ use crate::prelude::*; pub enum IqClientBody { Bind(BindRequest), Session(Session), + Roster(RosterQuery), } #[derive(From)] @@ -23,6 +25,7 @@ enum IqClientBodyParserInner { Initial, Bind(::P), SessionV(::P), + RosterV(::P), } impl FromXml for IqClientBody { @@ -51,34 +54,16 @@ impl Parser for IqClientBodyParser { return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))) } }; - if bytes.name().0 == BindRequest::NAME.as_bytes() - && namespace == ResolveResult::Bound(Namespace(BindRequest::NS.as_bytes())) - { - IqClientBodyParser(BindRequest::parse().into()).consume(namespace, event) - } else if bytes.name().0 == Session::NAME.as_bytes() - && namespace == ResolveResult::Bound(Namespace(Session::NS.as_bytes())) - { - IqClientBodyParser(Session::parse().into()).consume(namespace, event) - } else { - Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))) - } + let name = bytes.name(); + match_parser!(IqClientBodyParser, name, namespace, event; + BindRequest, + Session, + RosterQuery + ) } - Bind(p) => match p.consume(namespace, event) { - Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), - Continuation::Final(Err(e)) => Continuation::Final(Err(e)), - Continuation::Continue(s) => { - let inner: IqClientBodyParserInner = s.into(); - Continuation::Continue(inner.into()) - } - }, - SessionV(p) => match p.consume(namespace, event) { - Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), - Continuation::Final(Err(e)) => Continuation::Final(Err(e)), - Continuation::Continue(s) => { - let inner: IqClientBodyParserInner = s.into(); - Continuation::Continue(inner.into()) - } - }, + Bind(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), + SessionV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), + RosterV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), } } } @@ -121,35 +106,14 @@ impl Parser for ClientPacketParser { let Event::Start(bytes) = event else { return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); }; - if bytes.name().0 == Iq::::NAME.as_bytes() - && namespace - == ResolveResult::Bound(Namespace(Iq::::NS.as_bytes())) - { - ClientPacketParser(IqV(Iq::::parse())).consume(namespace, event) - } else if bytes.name().0 == Message::NAME.as_bytes() - && namespace == ResolveResult::Bound(Namespace(Message::NS.as_bytes())) - { - ClientPacketParser(MessageV(Message::parse())).consume(namespace, event) - } else { - Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))) - } + let name = bytes.name(); + match_parser!(ClientPacketParser, name, namespace, event; + Iq::, + Message + ) } - IqV(p) => match p.consume(namespace, event) { - Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), - Continuation::Final(Err(e)) => Continuation::Final(Err(e)), - Continuation::Continue(s) => { - let inner: ClientPacketParserInner = s.into(); - Continuation::Continue(inner.into()) - } - }, - MessageV(p) => match p.consume(namespace, event) { - Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), - Continuation::Final(Err(e)) => Continuation::Final(Err(e)), - Continuation::Continue(s) => { - let inner: ClientPacketParserInner = s.into(); - Continuation::Continue(inner.into()) - } - }, + IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), + MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), } } } diff --git a/src/protos/xmpp/mod.rs b/src/protos/xmpp/mod.rs index 43bd8e1..49c973e 100644 --- a/src/protos/xmpp/mod.rs +++ b/src/protos/xmpp/mod.rs @@ -1,5 +1,6 @@ pub mod bind; pub mod client; +pub mod roster; pub mod sasl; pub mod session; pub mod stanzaerror; diff --git a/src/protos/xmpp/roster.rs b/src/protos/xmpp/roster.rs new file mode 100644 index 0000000..4990bc1 --- /dev/null +++ b/src/protos/xmpp/roster.rs @@ -0,0 +1,60 @@ +use quick_xml::events::{BytesStart, Event}; + +use crate::prelude::*; +use crate::util::xml::*; + +pub const XMLNS: &'static str = "jabber:iq:roster"; + +#[derive(PartialEq, Eq, Debug)] +pub struct RosterQuery; + +pub struct QueryParser(QueryParserInner); + +enum QueryParserInner { + Initial, + InQuery, +} + +impl Parser for QueryParser { + type Output = Result; + + fn consume<'a>( + self: Self, + namespace: quick_xml::name::ResolveResult, + event: &quick_xml::events::Event<'a>, + ) -> Continuation { + match self.0 { + QueryParserInner::Initial => match event { + Event::Start(_) => Continuation::Continue(QueryParser(QueryParserInner::InQuery)), + Event::Empty(_) => Continuation::Final(Ok(RosterQuery)), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + QueryParserInner::InQuery => match event { + Event::End(_) => Continuation::Final(Ok(RosterQuery)), + _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), + }, + } + } +} + +impl FromXml for RosterQuery { + type P = QueryParser; + + fn parse() -> Self::P { + QueryParser(QueryParserInner::Initial) + } +} + +impl FromXmlTag for RosterQuery { + const NAME: &'static str = "query"; + const NS: &'static str = XMLNS; +} + +impl ToXml for RosterQuery { + fn serialize(&self, events: &mut Vec>) { + events.push(Event::Empty(BytesStart::new(format!( + r#"query xmlns="{}""#, + XMLNS + )))); + } +} diff --git a/src/util/xml.rs b/src/util/xml.rs index a132534..91ba0f0 100644 --- a/src/util/xml.rs +++ b/src/util/xml.rs @@ -42,4 +42,36 @@ macro_rules! fail_fast { }; } +macro_rules! delegate_parsing { + ($parser: ident, $intermediate: ident, $namespace: expr, $event: expr) => { + match $parser.consume($namespace, $event) { + Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), + Continuation::Final(Err(e)) => Continuation::Final(Err(e)), + Continuation::Continue(s) => { + let inner: $intermediate = s.into(); + Continuation::Continue(inner.into()) + } + } + }; +} + +macro_rules! match_parser { + ($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty) => { + if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { + $outer(<$subtype as FromXml>::parse().into()).consume($ns, $event) + } else { + Continuation::Final(Err(ffail!("Unexpected XML event of name {:?} in namespace {:?}", $name, $ns))) + } + }; + ($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+) => { + if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { + $outer(<$subtype as FromXml>::parse().into()).consume($ns, $event) + } else { + match_parser!($outer, $name, $ns, $event; $($rest),*) + } + }; +} + +pub(crate) use delegate_parsing; pub(crate) use fail_fast; +pub(crate) use match_parser;