Detailed changes
@@ -1,37 +0,0 @@
-use sasl::common::Credentials;
-use xmpp_parsers::{jid::Jid, ns};
-
-use crate::{
- client::{auth::auth, bind::bind},
- connect::ServerConnector,
- proto::XmppStream,
- Error,
-};
-
-/// Log into an XMPP server as a client with a jid+pass
-/// does channel binding if supported
-pub async fn client_login<C: ServerConnector>(
- server: C,
- jid: Jid,
- password: String,
-) -> Result<XmppStream<C::Stream>, Error> {
- let username = jid.node().unwrap().as_str();
- let password = password;
-
- let xmpp_stream = server.connect(&jid, ns::JABBER_CLIENT).await?;
-
- let channel_binding = C::channel_binding(xmpp_stream.stream.get_ref())?;
-
- let creds = Credentials::default()
- .with_username(username)
- .with_password(password)
- .with_channel_binding(channel_binding);
- // Authenticated (unspecified) stream
- let stream = auth(xmpp_stream, creds).await?;
- // Authenticated XmppStream
- let xmpp_stream = XmppStream::start(stream, jid, ns::JABBER_CLIENT.to_owned()).await?;
-
- // XmppStream bound to user session
- let xmpp_stream = bind(xmpp_stream).await?;
- Ok(xmpp_stream)
-}
@@ -7,9 +7,14 @@ use std::collections::HashSet;
use std::str::FromStr;
use tokio::io::{AsyncRead, AsyncWrite};
use xmpp_parsers::sasl::{Auth, Challenge, Failure, Mechanism as XMPPMechanism, Response, Success};
+use xmpp_parsers::{jid::Jid, ns};
-use crate::error::{AuthError, Error, ProtocolError};
-use crate::proto::{Packet, XmppStream};
+use crate::{
+ client::bind::bind,
+ connect::ServerConnector,
+ error::{AuthError, Error, ProtocolError},
+ proto::{Packet, XmppStream},
+};
pub async fn auth<S: AsyncRead + AsyncWrite + Unpin>(
mut stream: XmppStream<S>,
@@ -82,3 +87,31 @@ pub async fn auth<S: AsyncRead + AsyncWrite + Unpin>(
Err(AuthError::NoMechanism.into())
}
+
+/// Log into an XMPP server as a client with a jid+pass
+/// does channel binding if supported
+pub async fn client_login<C: ServerConnector>(
+ server: C,
+ jid: Jid,
+ password: String,
+) -> Result<XmppStream<C::Stream>, Error> {
+ let username = jid.node().unwrap().as_str();
+ let password = password;
+
+ let xmpp_stream = server.connect(&jid, ns::JABBER_CLIENT).await?;
+
+ let channel_binding = C::channel_binding(xmpp_stream.stream.get_ref())?;
+
+ let creds = Credentials::default()
+ .with_username(username)
+ .with_password(password)
+ .with_channel_binding(channel_binding);
+ // Authenticated (unspecified) stream
+ let stream = auth(xmpp_stream, creds).await?;
+ // Authenticated XmppStream
+ let xmpp_stream = XmppStream::start(stream, jid, ns::JABBER_CLIENT.to_owned()).await?;
+
+ // XmppStream bound to user session
+ let xmpp_stream = bind(xmpp_stream).await?;
+ Ok(xmpp_stream)
+}
@@ -1,6 +1,143 @@
-mod auth;
+use futures::sink::SinkExt;
+use minidom::Element;
+use xmpp_parsers::{jid::Jid, ns, stream_features::StreamFeatures};
+
+use crate::{
+ client::{login::client_login, stream::ClientState},
+ connect::ServerConnector,
+ error::Error,
+ proto::{add_stanza_id, Packet},
+};
+
+#[cfg(any(feature = "starttls", feature = "insecure-tcp"))]
+use crate::connect::DnsConfig;
+#[cfg(feature = "starttls")]
+use crate::connect::StartTlsServerConnector;
+#[cfg(feature = "insecure-tcp")]
+use crate::connect::TcpServerConnector;
+
mod bind;
+mod login;
+mod stream;
+
+/// XMPP client connection and state
+///
+/// It is able to reconnect. TODO: implement session management.
+///
+/// This implements the `futures` crate's [`Stream`](#impl-Stream) and
+/// [`Sink`](#impl-Sink<Packet>) traits.
+pub struct Client<C: ServerConnector> {
+ jid: Jid,
+ password: String,
+ connector: C,
+ state: ClientState<C::Stream>,
+ reconnect: bool,
+ // TODO: tls_required=true
+}
+
+impl<C: ServerConnector> Client<C> {
+ /// Set whether to reconnect (`true`) or let the stream end
+ /// (`false`) when a connection to the server has ended.
+ pub fn set_reconnect(&mut self, reconnect: bool) -> &mut Self {
+ self.reconnect = reconnect;
+ self
+ }
+
+ /// Get the client's bound JID (the one reported by the XMPP
+ /// server).
+ pub fn bound_jid(&self) -> Option<&Jid> {
+ match self.state {
+ ClientState::Connected(ref stream) => Some(&stream.jid),
+ _ => None,
+ }
+ }
+
+ /// Send stanza
+ pub async fn send_stanza(&mut self, stanza: Element) -> Result<(), Error> {
+ self.send(Packet::Stanza(add_stanza_id(stanza, ns::JABBER_CLIENT)))
+ .await
+ }
+
+ /// Get the stream features (`<stream:features/>`) of the underlying stream
+ pub fn get_stream_features(&self) -> Option<&StreamFeatures> {
+ match self.state {
+ ClientState::Connected(ref stream) => Some(&stream.stream_features),
+ _ => None,
+ }
+ }
+
+ /// End connection by sending `</stream:stream>`
+ ///
+ /// You may expect the server to respond with the same. This
+ /// client will then drop its connection.
+ ///
+ /// Make sure to disable reconnect.
+ pub async fn send_end(&mut self) -> Result<(), Error> {
+ self.send(Packet::StreamEnd).await
+ }
+}
+
+#[cfg(feature = "starttls")]
+impl Client<StartTlsServerConnector> {
+ /// Start a new XMPP client using StartTLS transport and autoreconnect
+ ///
+ /// Start polling the returned instance so that it will connect
+ /// and yield events.
+ pub fn new<J: Into<Jid>, P: Into<String>>(jid: J, password: P) -> Self {
+ let jid = jid.into();
+ let mut client = Self::new_starttls(
+ jid.clone(),
+ password,
+ DnsConfig::srv(&jid.domain().to_string(), "_xmpp-client._tcp", 5222),
+ );
+ client.set_reconnect(true);
+ client
+ }
+
+ /// Start a new XMPP client with StartTLS transport and specific DNS config
+ pub fn new_starttls<J: Into<Jid>, P: Into<String>>(
+ jid: J,
+ password: P,
+ dns_config: DnsConfig,
+ ) -> Self {
+ Self::new_with_connector(jid, password, StartTlsServerConnector::from(dns_config))
+ }
+}
+
+#[cfg(feature = "insecure-tcp")]
+impl Client<TcpServerConnector> {
+ /// Start a new XMPP client with plaintext insecure connection and specific DNS config
+ pub fn new_plaintext<J: Into<Jid>, P: Into<String>>(
+ jid: J,
+ password: P,
+ dns_config: DnsConfig,
+ ) -> Self {
+ Self::new_with_connector(jid, password, TcpServerConnector::from(dns_config))
+ }
+}
-pub(crate) mod connect;
+impl<C: ServerConnector> Client<C> {
+ /// Start a new client given that the JID is already parsed.
+ pub fn new_with_connector<J: Into<Jid>, P: Into<String>>(
+ jid: J,
+ password: P,
+ connector: C,
+ ) -> Self {
+ let jid = jid.into();
+ let password = password.into();
-pub mod async_client;
+ let connect = tokio::spawn(client_login(
+ connector.clone(),
+ jid.clone(),
+ password.clone(),
+ ));
+ let client = Client {
+ jid,
+ password,
+ connector,
+ state: ClientState::Connecting(connect),
+ reconnect: false,
+ };
+ client
+ }
+}
@@ -1,153 +1,24 @@
-use futures::{sink::SinkExt, task::Poll, Future, Sink, Stream};
-use minidom::Element;
+use futures::{task::Poll, Future, Sink, Stream};
use std::mem::replace;
use std::pin::Pin;
use std::task::Context;
use tokio::task::JoinHandle;
-use xmpp_parsers::{jid::Jid, ns, stream_features::StreamFeatures};
use crate::{
- client::connect::client_login,
+ client::login::client_login,
connect::{AsyncReadAndWrite, ServerConnector},
error::{Error, ProtocolError},
- proto::{add_stanza_id, Packet, XmppStream},
- Event,
+ proto::{Packet, XmppStream},
+ Client, Event,
};
-#[cfg(any(feature = "starttls", feature = "insecure-tcp"))]
-use crate::connect::DnsConfig;
-#[cfg(feature = "starttls")]
-use crate::connect::StartTlsServerConnector;
-#[cfg(feature = "insecure-tcp")]
-use crate::connect::TcpServerConnector;
-
-/// XMPP client connection and state
-///
-/// It is able to reconnect. TODO: implement session management.
-///
-/// This implements the `futures` crate's [`Stream`](#impl-Stream) and
-/// [`Sink`](#impl-Sink<Packet>) traits.
-pub struct Client<C: ServerConnector> {
- jid: Jid,
- password: String,
- connector: C,
- state: ClientState<C::Stream>,
- reconnect: bool,
- // TODO: tls_required=true
-}
-
-enum ClientState<S: AsyncReadAndWrite> {
+pub(crate) enum ClientState<S: AsyncReadAndWrite> {
Invalid,
Disconnected,
Connecting(JoinHandle<Result<XmppStream<S>, Error>>),
Connected(XmppStream<S>),
}
-#[cfg(feature = "starttls")]
-impl Client<StartTlsServerConnector> {
- /// Start a new XMPP client using StartTLS transport and autoreconnect
- ///
- /// Start polling the returned instance so that it will connect
- /// and yield events.
- pub fn new<J: Into<Jid>, P: Into<String>>(jid: J, password: P) -> Self {
- let jid = jid.into();
- let mut client = Self::new_starttls(
- jid.clone(),
- password,
- DnsConfig::srv(&jid.domain().to_string(), "_xmpp-client._tcp", 5222),
- );
- client.set_reconnect(true);
- client
- }
-
- /// Start a new XMPP client with StartTLS transport and specific DNS config
- pub fn new_starttls<J: Into<Jid>, P: Into<String>>(
- jid: J,
- password: P,
- dns_config: DnsConfig,
- ) -> Self {
- Self::new_with_connector(jid, password, StartTlsServerConnector::from(dns_config))
- }
-}
-
-#[cfg(feature = "insecure-tcp")]
-impl Client<TcpServerConnector> {
- /// Start a new XMPP client with plaintext insecure connection and specific DNS config
- pub fn new_plaintext<J: Into<Jid>, P: Into<String>>(
- jid: J,
- password: P,
- dns_config: DnsConfig,
- ) -> Self {
- Self::new_with_connector(jid, password, TcpServerConnector::from(dns_config))
- }
-}
-
-impl<C: ServerConnector> Client<C> {
- /// Start a new client given that the JID is already parsed.
- pub fn new_with_connector<J: Into<Jid>, P: Into<String>>(
- jid: J,
- password: P,
- connector: C,
- ) -> Self {
- let jid = jid.into();
- let password = password.into();
-
- let connect = tokio::spawn(client_login(
- connector.clone(),
- jid.clone(),
- password.clone(),
- ));
- let client = Client {
- jid,
- password,
- connector,
- state: ClientState::Connecting(connect),
- reconnect: false,
- };
- client
- }
-
- /// Set whether to reconnect (`true`) or let the stream end
- /// (`false`) when a connection to the server has ended.
- pub fn set_reconnect(&mut self, reconnect: bool) -> &mut Self {
- self.reconnect = reconnect;
- self
- }
-
- /// Get the client's bound JID (the one reported by the XMPP
- /// server).
- pub fn bound_jid(&self) -> Option<&Jid> {
- match self.state {
- ClientState::Connected(ref stream) => Some(&stream.jid),
- _ => None,
- }
- }
-
- /// Send stanza
- pub async fn send_stanza(&mut self, stanza: Element) -> Result<(), Error> {
- self.send(Packet::Stanza(add_stanza_id(stanza, ns::JABBER_CLIENT)))
- .await
- }
-
- /// Get the stream features (`<stream:features/>`) of the underlying stream
- pub fn get_stream_features(&self) -> Option<&StreamFeatures> {
- match self.state {
- ClientState::Connected(ref stream) => Some(&stream.stream_features),
- _ => None,
- }
- }
-
- /// End connection by sending `</stream:stream>`
- ///
- /// You may expect the server to respond with the same. This
- /// client will then drop its connection.
- ///
- /// Make sure to disable reconnect.
- pub async fn send_end(&mut self) -> Result<(), Error> {
- self.send(Packet::StreamEnd).await
- }
-}
-
/// Incoming XMPP events
///
/// In an `async fn` you may want to use this with `use
@@ -48,16 +48,17 @@ compile_error!(
mod event;
pub use event::Event;
-mod client;
pub mod connect;
pub mod proto;
-pub use client::async_client::Client;
+mod client;
+pub use client::Client;
#[cfg(feature = "insecure-tcp")]
mod component;
#[cfg(feature = "insecure-tcp")]
pub use crate::component::Component;
+
/// Detailed error types
pub mod error;