forked from lavina/lavina
xmpp: fix parsing of unknown elements in messages (#20)
Reviewed-on: lavina/lavina#20
This commit is contained in:
parent
8047a97baa
commit
9fca913430
|
@ -328,7 +328,7 @@ async fn socket_final(
|
||||||
if let Some(update) = update {
|
if let Some(update) = update {
|
||||||
match update {
|
match update {
|
||||||
lavina_core::player::Updates::NewMessage { room_id, author_id, body } => {
|
lavina_core::player::Updates::NewMessage { room_id, author_id, body } => {
|
||||||
Message {
|
Message::<()> {
|
||||||
to: Some(Jid {
|
to: Some(Jid {
|
||||||
name: Some(authenticated.xmpp_name.clone()),
|
name: Some(authenticated.xmpp_name.clone()),
|
||||||
server: Server("localhost".into()),
|
server: Server("localhost".into()),
|
||||||
|
@ -344,6 +344,7 @@ async fn socket_final(
|
||||||
lang: None,
|
lang: None,
|
||||||
subject: None,
|
subject: None,
|
||||||
body: body.into(),
|
body: body.into(),
|
||||||
|
custom: vec![],
|
||||||
}
|
}
|
||||||
.serialize(&mut events);
|
.serialize(&mut events);
|
||||||
}
|
}
|
||||||
|
@ -393,7 +394,7 @@ async fn handle_packet(
|
||||||
user_handle
|
user_handle
|
||||||
.send_message(RoomId::from(name.0.clone())?, m.body.clone().into())
|
.send_message(RoomId::from(name.0.clone())?, m.body.clone().into())
|
||||||
.await?;
|
.await?;
|
||||||
Message {
|
Message::<()> {
|
||||||
to: Some(Jid {
|
to: Some(Jid {
|
||||||
name: Some(user.xmpp_name.clone()),
|
name: Some(user.xmpp_name.clone()),
|
||||||
server: Server("localhost".into()),
|
server: Server("localhost".into()),
|
||||||
|
@ -409,6 +410,7 @@ async fn handle_packet(
|
||||||
lang: None,
|
lang: None,
|
||||||
subject: None,
|
subject: None,
|
||||||
body: m.body.clone(),
|
body: m.body.clone(),
|
||||||
|
custom: vec![],
|
||||||
}
|
}
|
||||||
.serialize(output);
|
.serialize(output);
|
||||||
false
|
false
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl FromXml for IqClientBody {
|
||||||
#[derive(PartialEq, Eq, Debug, From)]
|
#[derive(PartialEq, Eq, Debug, From)]
|
||||||
pub enum ClientPacket {
|
pub enum ClientPacket {
|
||||||
Iq(Iq<IqClientBody>),
|
Iq(Iq<IqClientBody>),
|
||||||
Message(Message),
|
Message(Message<Ignore>),
|
||||||
Presence(Presence<Ignore>),
|
Presence(Presence<Ignore>),
|
||||||
StreamEnd,
|
StreamEnd,
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ impl FromXml for ClientPacket {
|
||||||
match_parser!(name, namespace, event;
|
match_parser!(name, namespace, event;
|
||||||
Iq::<IqClientBody>,
|
Iq::<IqClientBody>,
|
||||||
Presence::<Ignore>,
|
Presence::<Ignore>,
|
||||||
Message,
|
Message::<Ignore>,
|
||||||
{
|
{
|
||||||
Err(anyhow!(
|
Err(anyhow!(
|
||||||
"Unexpected XML event of name {:?} in namespace {:?}",
|
"Unexpected XML event of name {:?} in namespace {:?}",
|
||||||
|
|
|
@ -3,8 +3,7 @@ use quick_xml::events::attributes::Attribute;
|
||||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
||||||
use quick_xml::name::{QName, ResolveResult};
|
use quick_xml::name::{QName, ResolveResult};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow as ffail};
|
use anyhow::{anyhow as ffail, Result};
|
||||||
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::xml::*;
|
use crate::xml::*;
|
||||||
|
@ -14,44 +13,45 @@ use super::bind::Jid;
|
||||||
pub const XMLNS: &'static str = "jabber:client";
|
pub const XMLNS: &'static str = "jabber:client";
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Message {
|
pub struct Message<T> {
|
||||||
pub from: Option<Jid>,
|
pub from: Option<Jid>,
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
pub to: Option<Jid>,
|
pub to: Option<Jid>,
|
||||||
// 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<Str>,
|
pub subject: Option<Str>,
|
||||||
pub body: Str,
|
pub body: Str,
|
||||||
|
pub custom: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromXmlTag for Message {
|
impl<T: FromXml> FromXmlTag for Message<T> {
|
||||||
const NS: &'static str = XMLNS;
|
const NS: &'static str = XMLNS;
|
||||||
const NAME: &'static str = "message";
|
const NAME: &'static str = "message";
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromXml for Message {
|
impl<T: FromXml> FromXml for Message<T> {
|
||||||
type P = MessageParser;
|
type P = impl Parser<Output = Result<Self>>;
|
||||||
|
|
||||||
fn parse() -> Self::P {
|
fn parse() -> Self::P {
|
||||||
MessageParserInner::Init.into()
|
MessageParser(MessageParserInner::Init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(From)]
|
#[derive(From)]
|
||||||
pub struct MessageParser(MessageParserInner);
|
struct MessageParser<T: FromXml>(MessageParserInner<T>);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
enum MessageParserInner {
|
enum MessageParserInner<T: FromXml> {
|
||||||
#[default]
|
#[default]
|
||||||
Init,
|
Init,
|
||||||
Outer(MessageParserState),
|
Outer(MessageParserState<T>),
|
||||||
InSubject(MessageParserState),
|
InSubject(MessageParserState<T>),
|
||||||
InBody(MessageParserState),
|
InBody(MessageParserState<T>),
|
||||||
|
InCustom(MessageParserState<T>, T::P),
|
||||||
}
|
}
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MessageParserState {
|
struct MessageParserState<T> {
|
||||||
from: Option<Jid>,
|
from: Option<Jid>,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
to: Option<Jid>,
|
to: Option<Jid>,
|
||||||
|
@ -59,21 +59,27 @@ struct MessageParserState {
|
||||||
lang: Option<Str>,
|
lang: Option<Str>,
|
||||||
subject: Option<Str>,
|
subject: Option<Str>,
|
||||||
body: Option<Str>,
|
body: Option<Str>,
|
||||||
|
custom: Vec<T>,
|
||||||
}
|
}
|
||||||
impl Parser for MessageParser {
|
impl<T: FromXml> Parser for MessageParser<T> {
|
||||||
type Output = Result<Message>;
|
type Output = Result<Message<T>>;
|
||||||
|
|
||||||
fn consume<'a>(
|
fn consume<'a>(self: Self, namespace: ResolveResult, event: &Event<'a>) -> Continuation<Self, Self::Output> {
|
||||||
self: Self,
|
|
||||||
namespace: ResolveResult,
|
|
||||||
event: &Event<'a>,
|
|
||||||
) -> Continuation<Self, Self::Output> {
|
|
||||||
// TODO validate tag name and namespace at each stage
|
// TODO validate tag name and namespace at each stage
|
||||||
use MessageParserInner::*;
|
use MessageParserInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Init => {
|
Init => {
|
||||||
if let Event::Start(ref bytes) = event {
|
if let Event::Start(ref bytes) = event {
|
||||||
let mut state: MessageParserState = Default::default();
|
let mut state: MessageParserState<T> = MessageParserState {
|
||||||
|
from: None,
|
||||||
|
id: None,
|
||||||
|
to: None,
|
||||||
|
r#type: MessageType::Normal,
|
||||||
|
lang: None,
|
||||||
|
subject: None,
|
||||||
|
body: None,
|
||||||
|
custom: vec![],
|
||||||
|
};
|
||||||
for attr in bytes.attributes() {
|
for attr in bytes.attributes() {
|
||||||
let attr = fail_fast!(attr);
|
let attr = fail_fast!(attr);
|
||||||
if attr.key.0 == b"from" {
|
if attr.key.0 == b"from" {
|
||||||
|
@ -104,7 +110,7 @@ impl Parser for MessageParser {
|
||||||
} else if bytes.name().0 == b"body" {
|
} else if bytes.name().0 == b"body" {
|
||||||
Continuation::Continue(InBody(state).into())
|
Continuation::Continue(InBody(state).into())
|
||||||
} else {
|
} else {
|
||||||
Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}")))
|
Continuation::Continue(InCustom(state, T::parse()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::End(_) => {
|
Event::End(_) => {
|
||||||
|
@ -117,6 +123,7 @@ impl Parser for MessageParser {
|
||||||
lang: state.lang,
|
lang: state.lang,
|
||||||
subject: state.subject,
|
subject: state.subject,
|
||||||
body,
|
body,
|
||||||
|
custom: state.custom,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Continuation::Final(Err(ffail!("Body not found")))
|
Continuation::Final(Err(ffail!("Body not found")))
|
||||||
|
@ -144,11 +151,19 @@ impl Parser for MessageParser {
|
||||||
Event::End(_) => Continuation::Continue(Outer(state).into()),
|
Event::End(_) => Continuation::Continue(Outer(state).into()),
|
||||||
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
|
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
|
||||||
},
|
},
|
||||||
|
InCustom(mut state, mut custom) => match custom.consume(namespace, event) {
|
||||||
|
Continuation::Final(Ok(e)) => {
|
||||||
|
state.custom.push(e);
|
||||||
|
Continuation::Continue(Outer(state).into())
|
||||||
|
}
|
||||||
|
Continuation::Final(Err(e)) => Continuation::Final(Err(e)),
|
||||||
|
Continuation::Continue(c) => Continuation::Continue(InCustom(state, c).into()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToXml for Message {
|
impl<T: ToXml> ToXml for Message<T> {
|
||||||
fn serialize(&self, events: &mut Vec<Event<'static>>) {
|
fn serialize(&self, events: &mut Vec<Event<'static>>) {
|
||||||
let mut bytes = BytesStart::new(format!(r#"message xmlns="{}""#, XMLNS));
|
let mut bytes = BytesStart::new(format!(r#"message xmlns="{}""#, XMLNS));
|
||||||
if let Some(from) = &self.from {
|
if let Some(from) = &self.from {
|
||||||
|
@ -261,11 +276,7 @@ struct IqParserState<T> {
|
||||||
impl<T: FromXml> Parser for IqParser<T> {
|
impl<T: FromXml> Parser for IqParser<T> {
|
||||||
type Output = Result<Iq<T>>;
|
type Output = Result<Iq<T>>;
|
||||||
|
|
||||||
fn consume<'a>(
|
fn consume<'a>(self: Self, namespace: ResolveResult, event: &Event<'a>) -> Continuation<Self, Self::Output> {
|
||||||
self: Self,
|
|
||||||
namespace: ResolveResult,
|
|
||||||
event: &Event<'a>,
|
|
||||||
) -> Continuation<Self, Self::Output> {
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
IqParserInner::Init => {
|
IqParserInner::Init => {
|
||||||
if let Event::Start(ref bytes) = event {
|
if let Event::Start(ref bytes) = event {
|
||||||
|
@ -297,8 +308,7 @@ impl<T: FromXml> Parser for IqParser<T> {
|
||||||
Continuation::Final(Err(ffail!("Expected start")))
|
Continuation::Final(Err(ffail!("Expected start")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IqParserInner::ParsingBody(mut state, parser) => {
|
IqParserInner::ParsingBody(mut state, parser) => match parser.consume(namespace, event) {
|
||||||
match parser.consume(namespace, event) {
|
|
||||||
Continuation::Final(f) => {
|
Continuation::Final(f) => {
|
||||||
let body = fail_fast!(f);
|
let body = fail_fast!(f);
|
||||||
state.body = Some(body);
|
state.body = Some(body);
|
||||||
|
@ -307,8 +317,7 @@ impl<T: FromXml> Parser for IqParser<T> {
|
||||||
Continuation::Continue(parser) => {
|
Continuation::Continue(parser) => {
|
||||||
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, parser)))
|
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, parser)))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
IqParserInner::Final(state) => {
|
IqParserInner::Final(state) => {
|
||||||
if let Event::End(ref bytes) = event {
|
if let Event::End(ref bytes) = event {
|
||||||
let id = fail_fast!(state.id.ok_or_else(|| ffail!("No id provided")));
|
let id = fail_fast!(state.id.ok_or_else(|| ffail!("No id provided")));
|
||||||
|
@ -589,22 +598,16 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn parse_message() {
|
async fn parse_message() {
|
||||||
let input = r#"<message id="aacea" type="chat" to="nikita@vlnv.dev"><subject>daa</subject><body>bbb</body></message>"#;
|
let input = r#"<message id="aacea" type="chat" to="nikita@vlnv.dev"><subject>daa</subject><body>bbb</body><unknown-stuff></unknown-stuff></message>"#;
|
||||||
let mut reader = NsReader::from_reader(input.as_bytes());
|
let mut reader = NsReader::from_reader(input.as_bytes());
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
let (ns, event) = reader
|
let (ns, event) = reader.read_resolved_event_into_async(&mut buf).await.unwrap();
|
||||||
.read_resolved_event_into_async(&mut buf)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let mut parser = Message::parse().consume(ns, &event);
|
let mut parser = Message::parse().consume(ns, &event);
|
||||||
let result = loop {
|
let result = loop {
|
||||||
match parser {
|
match parser {
|
||||||
Continuation::Final(res) => break res,
|
Continuation::Final(res) => break res,
|
||||||
Continuation::Continue(next) => {
|
Continuation::Continue(next) => {
|
||||||
let (ns, event) = reader
|
let (ns, event) = reader.read_resolved_event_into_async(&mut buf).await.unwrap();
|
||||||
.read_resolved_event_into_async(&mut buf)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
parser = next.consume(ns, &event);
|
parser = next.consume(ns, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,7 +615,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
Message {
|
Message::<Ignore> {
|
||||||
from: None,
|
from: None,
|
||||||
id: Some("aacea".to_string()),
|
id: Some("aacea".to_string()),
|
||||||
to: Some(Jid {
|
to: Some(Jid {
|
||||||
|
@ -624,6 +627,7 @@ mod tests {
|
||||||
lang: None,
|
lang: None,
|
||||||
subject: Some("daa".into()),
|
subject: Some("daa".into()),
|
||||||
body: "bbb".into(),
|
body: "bbb".into(),
|
||||||
|
custom: vec![Ignore],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -633,19 +637,13 @@ mod tests {
|
||||||
let input = r#"<iq id="bind_1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>mobile</resource></bind></iq>"#;
|
let input = r#"<iq id="bind_1" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>mobile</resource></bind></iq>"#;
|
||||||
let mut reader = NsReader::from_reader(input.as_bytes());
|
let mut reader = NsReader::from_reader(input.as_bytes());
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
let (ns, event) = reader
|
let (ns, event) = reader.read_resolved_event_into_async(&mut buf).await.unwrap();
|
||||||
.read_resolved_event_into_async(&mut buf)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let mut parser = Iq::<BindRequest>::parse().consume(ns, &event);
|
let mut parser = Iq::<BindRequest>::parse().consume(ns, &event);
|
||||||
let result = loop {
|
let result = loop {
|
||||||
match parser {
|
match parser {
|
||||||
Continuation::Final(res) => break res,
|
Continuation::Final(res) => break res,
|
||||||
Continuation::Continue(next) => {
|
Continuation::Continue(next) => {
|
||||||
let (ns, event) = reader
|
let (ns, event) = reader.read_resolved_event_into_async(&mut buf).await.unwrap();
|
||||||
.read_resolved_event_into_async(&mut buf)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
parser = next.consume(ns, &event);
|
parser = next.consume(ns, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue