diff --git a/crates/projection-xmpp/src/lib.rs b/crates/projection-xmpp/src/lib.rs index d79bd50..5468c19 100644 --- a/crates/projection-xmpp/src/lib.rs +++ b/crates/projection-xmpp/src/lib.rs @@ -447,6 +447,7 @@ impl<'a> XmppConnection<'a> { ServerStreamEnd.serialize(output); true } + ClientPacket::Eos => true, }; Ok(res) } diff --git a/crates/projection-xmpp/src/presence.rs b/crates/projection-xmpp/src/presence.rs index 82ccb61..c9fc938 100644 --- a/crates/projection-xmpp/src/presence.rs +++ b/crates/projection-xmpp/src/presence.rs @@ -14,7 +14,7 @@ impl<'a> XmppConnection<'a> { pub async fn handle_presence(&mut self, output: &mut Vec>, p: Presence) -> Result<()> { match p.to { None => { - self.self_presence(output).await; + self.self_presence(output, p.r#type.as_deref()).await; } Some(Jid { name: Some(name), @@ -33,21 +33,29 @@ impl<'a> XmppConnection<'a> { Ok(()) } - async fn self_presence(&mut self, output: &mut Vec>) { - let response = Presence::<()> { - to: Some(Jid { - name: Some(self.user.xmpp_name.clone()), - server: Server(self.hostname.clone()), - resource: Some(self.user.xmpp_resource.clone()), - }), - from: Some(Jid { - name: Some(self.user.xmpp_name.clone()), - server: Server(self.hostname.clone()), - resource: Some(self.user.xmpp_resource.clone()), - }), - ..Default::default() - }; - response.serialize(output); + async fn self_presence(&mut self, output: &mut Vec>, r#type: Option<&str>) { + match r#type { + Some("unavailable") => { + // do not print anything + } + None => { + let response = Presence::<()> { + to: Some(Jid { + name: Some(self.user.xmpp_name.clone()), + server: Server(self.hostname.clone()), + resource: Some(self.user.xmpp_resource.clone()), + }), + from: Some(Jid { + name: Some(self.user.xmpp_name.clone()), + server: Server(self.hostname.clone()), + resource: Some(self.user.xmpp_resource.clone()), + }), + ..Default::default() + }; + response.serialize(output); + } + _ => todo!(), + } } async fn muc_presence(&mut self, name: Name, output: &mut Vec>) -> Result<()> { diff --git a/crates/projection-xmpp/src/proto.rs b/crates/projection-xmpp/src/proto.rs index e486b65..8b16ef0 100644 --- a/crates/projection-xmpp/src/proto.rs +++ b/crates/projection-xmpp/src/proto.rs @@ -52,6 +52,7 @@ pub enum ClientPacket { Message(Message), Presence(Presence), StreamEnd, + Eos, } impl FromXml for ClientPacket { @@ -83,6 +84,7 @@ impl FromXml for ClientPacket { return Err(anyhow!("Unexpected XML event: {event:?}")); } } + Event::Eof => Ok(ClientPacket::Eos), _ => { return Err(anyhow!("Unexpected XML event: {event:?}")); } diff --git a/crates/projection-xmpp/tests/lib.rs b/crates/projection-xmpp/tests/lib.rs index 88af83d..ef8c046 100644 --- a/crates/projection-xmpp/tests/lib.rs +++ b/crates/projection-xmpp/tests/lib.rs @@ -82,7 +82,7 @@ impl<'a> TestScopeTls<'a> { fn new(stream: &'a mut TlsStream, buffer: Vec) -> TestScopeTls<'a> { let (reader, writer) = tokio::io::split(stream); let reader = NsReader::from_reader(BufReader::new(reader)); - let timeout = Duration::from_millis(100); + let timeout = Duration::from_millis(500); TestScopeTls { reader, @@ -203,6 +203,35 @@ async fn scenario_basic() -> Result<()> { 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"mechanisms")); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"mechanism")); + assert_matches!(s.next_xml_event().await?, Event::Text(b) => assert_eq!(&*b, b"PLAIN")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanism")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"mechanisms")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features")); + + // base64-encoded b"\x00tester\x00password" + s.send(r#"AHRlc3RlcgBwYXNzd29yZA=="#) + .await?; + assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"success")); + 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::Empty(b) => assert_eq!(b.local_name().into_inner(), b"bind")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"features")); + s.send(r#"kek"#).await?; + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"iq")); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"bind")); + assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"jid")); + assert_matches!(s.next_xml_event().await?, Event::Text(b) => assert_eq!(&*b, b"tester@localhost/tester")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"jid")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"bind")); + assert_matches!(s.next_xml_event().await?, Event::End(b) => assert_eq!(b.local_name().into_inner(), b"iq")); + s.send(r#"Logged out"#).await?; + stream.shutdown().await?; // wrap up