Port crates to use new XSO-based xmlstream

This commit is contained in:
Jonas Schäfer 2024-08-10 15:05:42 +02:00
commit ab10e30ac0
26 changed files with 623 additions and 973 deletions

View file

@ -1,12 +1,12 @@
use futures::stream::StreamExt;
use tokio::io::{AsyncRead, AsyncWrite};
use std::io;
use futures::{SinkExt, StreamExt};
use tokio::io::{AsyncBufRead, AsyncWrite};
use xmpp_parsers::{component::Handshake, jid::Jid, ns};
use crate::{
connect::ServerConnector,
error::{AuthError, Error},
proto::{Packet, XmppStream},
};
use crate::component::ServerConnector;
use crate::error::{AuthError, Error};
use crate::xmlstream::{ReadError, XmppStream, XmppStreamElement};
/// Log into an XMPP server as a client with a jid+pass
pub async fn component_login<C: ServerConnector>(
@ -15,32 +15,47 @@ pub async fn component_login<C: ServerConnector>(
password: String,
) -> Result<XmppStream<C::Stream>, Error> {
let password = password;
let mut xmpp_stream = connector.connect(&jid, ns::COMPONENT).await?;
auth(&mut xmpp_stream, password).await?;
Ok(xmpp_stream)
let mut stream = connector.connect(&jid, ns::COMPONENT).await?;
let header = stream.take_header();
let mut stream = stream.skip_features();
let stream_id = match header.id {
Some(ref v) => &**v,
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"stream id missing on component stream",
)
.into())
}
};
auth(&mut stream, stream_id, &password).await?;
Ok(stream)
}
pub async fn auth<S: AsyncRead + AsyncWrite + Unpin>(
pub async fn auth<S: AsyncBufRead + AsyncWrite + Unpin>(
stream: &mut XmppStream<S>,
password: String,
stream_id: &str,
password: &str,
) -> Result<(), Error> {
let nonza = Handshake::from_password_and_stream_id(&password, &stream.id);
stream.send_stanza(nonza).await?;
let nonza = Handshake::from_password_and_stream_id(password, stream_id);
stream
.send(&XmppStreamElement::ComponentHandshake(nonza))
.await?;
loop {
match stream.next().await {
Some(Ok(Packet::Stanza(ref stanza)))
if stanza.is("handshake", ns::COMPONENT_ACCEPT) =>
{
Some(Ok(XmppStreamElement::ComponentHandshake(_))) => {
return Ok(());
}
Some(Ok(Packet::Stanza(ref stanza)))
if stanza.is("error", "http://etherx.jabber.org/streams") =>
{
Some(Ok(_)) => {
return Err(AuthError::ComponentFail.into());
}
Some(_) => {}
None => return Err(Error::Disconnected),
Some(Err(ReadError::SoftTimeout)) => (),
Some(Err(ReadError::HardError(e))) => return Err(e.into()),
Some(Err(ReadError::ParseError(e))) => {
return Err(io::Error::new(io::ErrorKind::InvalidData, e).into())
}
Some(Err(ReadError::StreamFooterReceived)) | None => return Err(Error::Disconnected),
}
}
}

View file

@ -2,15 +2,12 @@
//! XMPP server under a JID consisting of just a domain name. They are
//! allowed to use any user and resource identifiers in their stanzas.
use futures::sink::SinkExt;
use minidom::Element;
use std::str::FromStr;
use xmpp_parsers::{jid::Jid, ns};
use xmpp_parsers::jid::Jid;
use crate::{
component::login::component_login,
connect::ServerConnector,
proto::{add_stanza_id, XmppStream},
Error,
component::login::component_login, connect::ServerConnector, xmlstream::XmppStream, Error,
Stanza,
};
#[cfg(any(feature = "starttls", feature = "insecure-tcp"))]
@ -33,8 +30,9 @@ pub struct Component<C: ServerConnector> {
impl<C: ServerConnector> Component<C> {
/// Send stanza
pub async fn send_stanza(&mut self, stanza: Element) -> Result<(), Error> {
self.send(add_stanza_id(stanza, ns::COMPONENT_ACCEPT)).await
pub async fn send_stanza(&mut self, mut stanza: Stanza) -> Result<(), Error> {
stanza.ensure_id();
self.send(stanza).await
}
/// End connection

View file

@ -2,21 +2,27 @@
//! XMPP server under a JID consisting of just a domain name. They are
//! allowed to use any user and resource identifiers in their stanzas.
use futures::{task::Poll, Sink, Stream};
use minidom::Element;
use std::pin::Pin;
use std::task::Context;
use crate::{component::Component, connect::ServerConnector, proto::Packet, Error};
use crate::{
component::Component, connect::ServerConnector, xmlstream::XmppStreamElement, Error, Stanza,
};
impl<C: ServerConnector> Stream for Component<C> {
type Item = Element;
type Item = Stanza;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
loop {
match Pin::new(&mut self.stream).poll_next(cx) {
Poll::Ready(Some(Ok(Packet::Stanza(stanza)))) => return Poll::Ready(Some(stanza)),
Poll::Ready(Some(Ok(Packet::Text(_)))) => {
// retry
Poll::Ready(Some(Ok(XmppStreamElement::Iq(stanza)))) => {
return Poll::Ready(Some(Stanza::Iq(stanza)))
}
Poll::Ready(Some(Ok(XmppStreamElement::Message(stanza)))) => {
return Poll::Ready(Some(Stanza::Message(stanza)))
}
Poll::Ready(Some(Ok(XmppStreamElement::Presence(stanza)))) => {
return Poll::Ready(Some(Stanza::Presence(stanza)))
}
Poll::Ready(Some(Ok(_))) =>
// unexpected
@ -31,12 +37,12 @@ impl<C: ServerConnector> Stream for Component<C> {
}
}
impl<C: ServerConnector> Sink<Element> for Component<C> {
impl<C: ServerConnector> Sink<Stanza> for Component<C> {
type Error = Error;
fn start_send(mut self: Pin<&mut Self>, item: Element) -> Result<(), Self::Error> {
fn start_send(mut self: Pin<&mut Self>, item: Stanza) -> Result<(), Self::Error> {
Pin::new(&mut self.stream)
.start_send(Packet::Stanza(item))
.start_send(&item.into())
.map_err(|e| e.into())
}