diff --git a/crates/projection-xmpp/src/iq.rs b/crates/projection-xmpp/src/iq.rs index 0031c36..acd9f74 100644 --- a/crates/projection-xmpp/src/iq.rs +++ b/crates/projection-xmpp/src/iq.rs @@ -4,7 +4,7 @@ use quick_xml::events::Event; use lavina_core::room::{RoomId, RoomRegistry}; use proto_xmpp::bind::{BindResponse, Jid, Name, Server}; -use proto_xmpp::client::{Iq, IqError, IqErrorType, IqType}; +use proto_xmpp::client::{Iq, IqError, IqErrorType, IqType, Message, MessageType}; use proto_xmpp::disco::{Feature, Identity, InfoQuery, Item, ItemQuery}; use proto_xmpp::roster::RosterQuery; use proto_xmpp::session::Session; @@ -12,7 +12,7 @@ use proto_xmpp::session::Session; use crate::proto::IqClientBody; use crate::XmppConnection; -use proto_xmpp::xml::ToXml; +use proto_xmpp::xml::{Ignore, ToXml}; impl<'a> XmppConnection<'a> { pub async fn handle_iq(&self, output: &mut Vec>, iq: Iq) { @@ -87,6 +87,9 @@ impl<'a> XmppConnection<'a> { }; req.serialize(output); } + IqClientBody::MessageArchiveRequest(request) => { + // let response = self.mam().await?; + } _ => { let req = Iq { from: None, @@ -220,4 +223,22 @@ impl<'a> XmppConnection<'a> { }; ItemQuery { item } } + + async fn mam(&self) -> Result, ()> { + // return Ok(()); + Ok(Message:: { + from: None, + id: Some("aacea".to_string()), + to: Some(Jid { + name: Some(Name("nikita".into())), + server: Server("vlnv.dev".into()), + resource: None, + }), + r#type: MessageType::Chat, + lang: None, + subject: Some("daa".into()), + body: "bbb".into(), + custom: vec![Ignore], + }) + } } diff --git a/crates/projection-xmpp/src/proto.rs b/crates/projection-xmpp/src/proto.rs index e486b65..a345b56 100644 --- a/crates/projection-xmpp/src/proto.rs +++ b/crates/projection-xmpp/src/proto.rs @@ -7,6 +7,7 @@ use lavina_core::prelude::*; use proto_xmpp::bind::BindRequest; use proto_xmpp::client::{Iq, Message, Presence}; use proto_xmpp::disco::{InfoQuery, ItemQuery}; +use proto_xmpp::mam::MessageArchiveRequest; use proto_xmpp::roster::RosterQuery; use proto_xmpp::session::Session; use proto_xmpp::xml::*; @@ -18,6 +19,7 @@ pub enum IqClientBody { Roster(RosterQuery), DiscoInfo(InfoQuery), DiscoItem(ItemQuery), + MessageArchiveRequest(MessageArchiveRequest), Unknown(Ignore), } @@ -38,6 +40,7 @@ impl FromXml for IqClientBody { RosterQuery, InfoQuery, ItemQuery, + MessageArchiveRequest, { delegate_parsing!(Ignore, namespace, event).into() } diff --git a/crates/projection-xmpp/tests/lib.rs b/crates/projection-xmpp/tests/lib.rs index 29d0368..137242a 100644 --- a/crates/projection-xmpp/tests/lib.rs +++ b/crates/projection-xmpp/tests/lib.rs @@ -1,4 +1,5 @@ use std::io::ErrorKind; +use std::str::from_utf8; use std::sync::Arc; use std::time::Duration; @@ -6,6 +7,7 @@ use anyhow::Result; use assert_matches::*; use prometheus::Registry as MetricsRegistry; use quick_xml::events::Event; +use quick_xml::name::LocalName; use quick_xml::NsReader; use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::io::{ReadHalf as GenericReadHalf, WriteHalf as GenericWriteHalf}; @@ -22,6 +24,10 @@ use lavina_core::room::RoomRegistry; use projection_xmpp::{launch, RunningServer, ServerConfig}; use proto_xmpp::xml::{Continuation, FromXml, Parser}; +fn element_name<'a>(local_name: &LocalName<'a>) -> &'a str { + from_utf8(local_name.into_inner()).unwrap() +} + pub async fn read_irc_message(reader: &mut BufReader>, buf: &mut Vec) -> Result { let mut size = 0; let res = reader.read_until(b'\n', buf).await?; @@ -107,6 +113,7 @@ impl<'a> TestScopeTls<'a> { } struct IgnoreCertVerification; + impl ServerCertVerifier for IgnoreCertVerification { fn verify_server_cert( &self, @@ -128,6 +135,7 @@ struct TestServer { players: PlayerRegistry, server: RunningServer, } + impl TestServer { async fn start() -> Result { let _ = tracing_subscriber::fmt::try_init(); @@ -300,3 +308,88 @@ async fn terminate_socket() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn test_message_archive_request() -> Result<()> { + let mut server = TestServer::start().await?; + + // test scenario + + server.storage.create_user("tester").await?; + server.storage.set_password("tester", "password").await?; + + let mut stream = TcpStream::connect(server.server.addr).await?; + let mut s = TestScope::new(&mut stream); + tracing::info!("TCP connection established"); + + s.send(r#""#).await?; + s.send(r#""#).await?; + assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {}); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream")); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"features")); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"starttls")); + assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"required")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"starttls")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features")); + s.send(r#""#).await?; + assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed")); + let buffer = s.buffer; + tracing::info!("TLS feature negotiation complete"); + + let connector = TlsConnector::from(Arc::new( + ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(IgnoreCertVerification)) + .with_no_client_auth(), + )); + tracing::info!("Initiating TLS connection..."); + let mut stream = connector.connect(ServerName::IpAddress(server.server.addr.ip()), stream).await?; + tracing::info!("TLS connection established"); + + let mut s = TestScopeTls::new(&mut stream, buffer); + + s.send(r#""#).await?; + s.send(r#""#).await?; + assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {}); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream")); + + assert_matches!(s.next_xml_event().await?, Event::Start(b) => { + let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + assert_eq!(local_name, "features") + }); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => { + let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + assert_eq!(local_name, "mechanisms") + }); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => { + let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + assert_eq!(local_name, "mechanism") + }); + assert_matches!(s.next_xml_event().await?, Event::Text(b) => { + // b. + // let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + // assert_eq!(local_name, "mechanism") + }); + assert_matches!(s.next_xml_event().await?, Event::End(b) => { + // let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + // assert_eq!(local_name, "mechanism") + }); + assert_matches!(s.next_xml_event().await?, Event::End(b) => { + let local_name: &str = from_utf8(b.local_name().into_inner()).unwrap(); + assert_eq!(local_name, "mechanisms") + }); + assert_matches!(s.next_xml_event().await?, Event::End(b) => { + assert_eq!(element_name(&b.local_name()), "features") + }); + // s.send(r#""#).await?; + assert_matches!(s.next_xml_event().await?, Event::Start(b) => { + assert_eq!(element_name(&b.local_name()), "message") + }); + + stream.shutdown().await?; + + // wrap up + + server.server.terminate().await?; + Ok(()) +} diff --git a/crates/proto-xmpp/src/mam.rs b/crates/proto-xmpp/src/mam.rs index 464844c..c134096 100644 --- a/crates/proto-xmpp/src/mam.rs +++ b/crates/proto-xmpp/src/mam.rs @@ -10,17 +10,17 @@ pub const DATA_XMLNS: &'static str = "jabber:x:data"; #[derive(PartialEq, Eq, Debug, Clone)] pub struct MessageArchiveRequest { - x: Option, + pub x: Option, } #[derive(PartialEq, Eq, Debug, Clone)] pub struct X { - fields: Vec, + pub fields: Vec, } #[derive(PartialEq, Eq, Debug, Clone)] pub struct Field { - values: Vec, + pub values: Vec, } impl FromXmlTag for X {