WIP: refactor to parse incoming stream as state machine
This commit is contained in:
parent
2536fa4937
commit
ba94ee66fa
|
@ -10,9 +10,12 @@ edition = "2021"
|
||||||
async-recursion = "1.0.4"
|
async-recursion = "1.0.4"
|
||||||
async-trait = "0.1.68"
|
async-trait = "0.1.68"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
quick-xml = { git = "https://github.com/tafia/quick-xml.git", features = ["async-tokio"] }
|
quick-xml = { git = "https://github.com/tafia/quick-xml.git", features = ["async-tokio", "serialize"] }
|
||||||
# TODO: remove unneeded features
|
# TODO: remove unneeded features
|
||||||
rsasl = { version = "2", default_features = true, features = ["provider_base64", "plain", "config_builder"] }
|
rsasl = { version = "2", default_features = true, features = ["provider_base64", "plain", "config_builder"] }
|
||||||
|
serde = "1.0.180"
|
||||||
tokio = { version = "1.28", features = ["full"] }
|
tokio = { version = "1.28", features = ["full"] }
|
||||||
tokio-native-tls = "0.3.1"
|
tokio-native-tls = "0.3.1"
|
||||||
|
tracing = "0.1.40"
|
||||||
trust-dns-resolver = "0.22.0"
|
trust-dns-resolver = "0.22.0"
|
||||||
|
try_map = "0.3.1"
|
||||||
|
|
|
@ -2,36 +2,26 @@ use std::{collections::BTreeMap, str};
|
||||||
|
|
||||||
use quick_xml::{
|
use quick_xml::{
|
||||||
events::{BytesDecl, Event},
|
events::{BytesDecl, Event},
|
||||||
Reader, Writer,
|
NsReader, Writer,
|
||||||
};
|
};
|
||||||
use rsasl::prelude::{Mechname, SASLClient};
|
use rsasl::prelude::{Mechname, SASLClient};
|
||||||
use tokio::io::{BufReader, ReadHalf, WriteHalf};
|
use tokio::io::{BufReader, ReadHalf, WriteHalf};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_native_tls::TlsStream;
|
use tokio_native_tls::TlsStream;
|
||||||
|
|
||||||
use crate::stanza::{
|
|
||||||
bind::Bind,
|
|
||||||
iq::IQ,
|
|
||||||
sasl::{Challenge, Success},
|
|
||||||
Element,
|
|
||||||
};
|
|
||||||
use crate::stanza::{
|
|
||||||
sasl::{Auth, Response},
|
|
||||||
stream::{Stream, StreamFeature},
|
|
||||||
};
|
|
||||||
use crate::Jabber;
|
use crate::Jabber;
|
||||||
use crate::JabberError;
|
use crate::JabberError;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
pub struct JabberClient<'j> {
|
pub struct JabberClient<'j> {
|
||||||
pub reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
|
pub reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
|
||||||
pub writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
|
pub writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
|
||||||
jabber: &'j mut Jabber<'j>,
|
jabber: &'j mut Jabber<'j>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'j> JabberClient<'j> {
|
impl<'j> JabberClient<'j> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
|
reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
|
||||||
writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
|
writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
|
||||||
jabber: &'j mut Jabber<'j>,
|
jabber: &'j mut Jabber<'j>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
pub mod encrypted;
|
// pub mod encrypted;
|
||||||
pub mod unencrypted;
|
pub mod unencrypted;
|
||||||
|
|
||||||
// use async_trait::async_trait;
|
// use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::stanza::stream::StreamFeature;
|
// use crate::stanza::stream::StreamFeature;
|
||||||
use crate::JabberError;
|
use crate::JabberError;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
pub enum JabberClientType<'j> {
|
pub enum JabberClientType<'j> {
|
||||||
Encrypted(encrypted::JabberClient<'j>),
|
// Encrypted(encrypted::JabberClient<'j>),
|
||||||
Unencrypted(unencrypted::JabberClient<'j>),
|
Unencrypted(unencrypted::JabberClient<'j>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'j> JabberClientType<'j> {
|
impl<'j> JabberClientType<'j> {
|
||||||
|
/// ensures an encrypted jabber client
|
||||||
pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {
|
pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Encrypted(c) => Ok(c),
|
Self::Encrypted(c) => Ok(c),
|
||||||
Self::Unencrypted(mut c) => {
|
Self::Unencrypted(mut c) => {
|
||||||
|
c.start_stream().await?;
|
||||||
let features = c.get_features().await?;
|
let features = c.get_features().await?;
|
||||||
if features.contains(&StreamFeature::StartTls) {
|
if features.contains(&StreamFeature::StartTls) {
|
||||||
Ok(c.starttls().await?)
|
Ok(c.starttls().await?)
|
||||||
|
@ -28,7 +30,7 @@ impl<'j> JabberClientType<'j> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: jabber client trait over both client types
|
// TODO: jabber client trait over both client types using macro
|
||||||
// #[async_trait]
|
// #[async_trait]
|
||||||
// pub trait JabberTrait {
|
// pub trait JabberTrait {
|
||||||
// async fn start_stream(&mut self) -> Result<()>;
|
// async fn start_stream(&mut self) -> Result<()>;
|
||||||
|
|
|
@ -1,27 +1,30 @@
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use quick_xml::{
|
use quick_xml::{
|
||||||
events::{BytesDecl, BytesStart, Event},
|
events::{BytesStart, Event},
|
||||||
name::QName,
|
name::QName,
|
||||||
Reader, Writer,
|
se, NsReader, Writer,
|
||||||
};
|
};
|
||||||
use tokio::io::{BufReader, ReadHalf, WriteHalf};
|
use tokio::io::{BufReader, ReadHalf, WriteHalf};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_native_tls::native_tls::TlsConnector;
|
use tokio_native_tls::native_tls::TlsConnector;
|
||||||
|
use try_map::FallibleMapExt;
|
||||||
|
|
||||||
use crate::stanza::stream::StreamFeature;
|
use crate::error::JabberError;
|
||||||
use crate::stanza::Element;
|
use crate::stanza::stream::Stream;
|
||||||
|
use crate::stanza::DECLARATION;
|
||||||
use crate::Jabber;
|
use crate::Jabber;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use crate::{error::JabberError, stanza::stream::Stream};
|
|
||||||
|
|
||||||
pub struct JabberClient<'j> {
|
pub struct JabberClient<'j> {
|
||||||
reader: Reader<BufReader<ReadHalf<TcpStream>>>,
|
reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
|
||||||
writer: Writer<WriteHalf<TcpStream>>,
|
writer: Writer<WriteHalf<TcpStream>>,
|
||||||
jabber: &'j mut Jabber<'j>,
|
jabber: &'j mut Jabber<'j>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'j> JabberClient<'j> {
|
impl<'j> JabberClient<'j> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
reader: Reader<BufReader<ReadHalf<TcpStream>>>,
|
reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
|
||||||
writer: Writer<WriteHalf<TcpStream>>,
|
writer: Writer<WriteHalf<TcpStream>>,
|
||||||
jabber: &'j mut Jabber<'j>,
|
jabber: &'j mut Jabber<'j>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -34,60 +37,144 @@ impl<'j> JabberClient<'j> {
|
||||||
|
|
||||||
pub async fn start_stream(&mut self) -> Result<()> {
|
pub async fn start_stream(&mut self) -> Result<()> {
|
||||||
// client to server
|
// client to server
|
||||||
let declaration = BytesDecl::new("1.0", None, None);
|
|
||||||
|
// declaration
|
||||||
|
self.writer.write_event_async(DECLARATION).await?;
|
||||||
|
|
||||||
|
// opening stream element
|
||||||
let server = &self.jabber.server.to_owned().try_into()?;
|
let server = &self.jabber.server.to_owned().try_into()?;
|
||||||
let stream_element =
|
let stream_element = Stream::new_client(None, server, None, "en");
|
||||||
Stream::new_client(&self.jabber.jid, server, None, Some("en".to_string()));
|
se::to_writer_with_root(&mut self.writer, "stream:stream", &stream_element);
|
||||||
self.writer
|
|
||||||
.write_event_async(Event::Decl(declaration))
|
|
||||||
.await?;
|
|
||||||
let stream_element: Element<'_> = stream_element.into();
|
|
||||||
stream_element.write_start(&mut self.writer).await?;
|
|
||||||
// server to client
|
// server to client
|
||||||
let mut buf = Vec::new();
|
|
||||||
self.reader.read_event_into_async(&mut buf).await?;
|
|
||||||
let _stream_response = Element::read_start(&mut self.reader).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
|
// may or may not send a declaration
|
||||||
Element::read(&mut self.reader).await?.try_into()
|
let buf = Vec::new();
|
||||||
}
|
let mut first_event = self.reader.read_resolved_event_into_async(&mut buf).await?;
|
||||||
|
match first_event {
|
||||||
pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> {
|
(quick_xml::name::ResolveResult::Unbound, Event::Decl(e)) => {
|
||||||
let mut starttls_element = BytesStart::new("starttls");
|
if let Ok(version) = e.version() {
|
||||||
starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"));
|
if version.as_ref() == b"1.0" {
|
||||||
self.writer
|
first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
|
||||||
.write_event_async(Event::Empty(starttls_element))
|
} else {
|
||||||
.await
|
// todo: error
|
||||||
.unwrap();
|
todo!()
|
||||||
let mut buf = Vec::new();
|
|
||||||
match self.reader.read_event_into_async(&mut buf).await.unwrap() {
|
|
||||||
Event::Empty(e) => match e.name() {
|
|
||||||
QName(b"proceed") => {
|
|
||||||
let connector = TlsConnector::new().unwrap();
|
|
||||||
let stream = self
|
|
||||||
.reader
|
|
||||||
.into_inner()
|
|
||||||
.into_inner()
|
|
||||||
.unsplit(self.writer.into_inner());
|
|
||||||
if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector)
|
|
||||||
.connect(&self.jabber.server, stream)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
let (read, write) = tokio::io::split(tlsstream);
|
|
||||||
let reader = Reader::from_reader(BufReader::new(read));
|
|
||||||
let writer = Writer::new(write);
|
|
||||||
let mut client =
|
|
||||||
super::encrypted::JabberClient::new(reader, writer, self.jabber);
|
|
||||||
client.start_stream().await?;
|
|
||||||
return Ok(client);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
|
||||||
}
|
}
|
||||||
QName(_) => return Err(JabberError::TlsNegotiation),
|
}
|
||||||
},
|
_ => (),
|
||||||
_ => return Err(JabberError::TlsNegotiation),
|
}
|
||||||
|
|
||||||
|
// receive stream element and validate
|
||||||
|
let stream_response: Stream;
|
||||||
|
match first_event {
|
||||||
|
(quick_xml::name::ResolveResult::Bound(ns), Event::Start(e)) => {
|
||||||
|
if ns.0 == crate::stanza::stream::XMLNS.as_bytes() {
|
||||||
|
// stream_response = Stream::new(
|
||||||
|
// e.try_get_attribute("from")?.try_map(|attribute| {
|
||||||
|
// str::from_utf8(attribute.value.as_ref())?
|
||||||
|
// .try_into()?
|
||||||
|
// .as_ref()
|
||||||
|
// })?,
|
||||||
|
// e.try_get_attribute("to")?.try_map(|attribute| {
|
||||||
|
// str::from_utf8(attribute.value.as_ref())?
|
||||||
|
// .try_into()?
|
||||||
|
// .as_ref()
|
||||||
|
// })?,
|
||||||
|
// e.try_get_attribute("id")?.try_map(|attribute| {
|
||||||
|
// str::from_utf8(attribute.value.as_ref())?
|
||||||
|
// .try_into()?
|
||||||
|
// .as_ref()
|
||||||
|
// })?,
|
||||||
|
// e.try_get_attribute("version")?.try_map(|attribute| {
|
||||||
|
// str::from_utf8(attribute.value.as_ref())?
|
||||||
|
// .try_into()?
|
||||||
|
// .as_ref()
|
||||||
|
// })?,
|
||||||
|
// e.try_get_attribute("lang")?.try_map(|attribute| {
|
||||||
|
// str::from_utf8(attribute.value.as_ref())?
|
||||||
|
// .try_into()?
|
||||||
|
// .as_ref()
|
||||||
|
// })?,
|
||||||
|
// );
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
return Err(JabberError::BadStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: errors for incorrect namespace
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Decl(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Start(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::End(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Empty(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Text(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::CData(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Comment(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Decl(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::PI(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::DocType(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unknown(_), Event::Eof) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Start(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::End(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Empty(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Text(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::CData(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Comment(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::PI(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::DocType(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Unbound, Event::Eof) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::End(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::Empty(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::Text(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::CData(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::Comment(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::Decl(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::PI(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::DocType(_)) => todo!(),
|
||||||
|
(quick_xml::name::ResolveResult::Bound(_), Event::Eof) => todo!(),
|
||||||
}
|
}
|
||||||
Err(JabberError::TlsNegotiation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
|
||||||
|
// Element::read(&mut self.reader).await?.try_into()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> {
|
||||||
|
// let mut starttls_element = BytesStart::new("starttls");
|
||||||
|
// starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"));
|
||||||
|
// self.writer
|
||||||
|
// .write_event_async(Event::Empty(starttls_element))
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let mut buf = Vec::new();
|
||||||
|
// match self.reader.read_event_into_async(&mut buf).await.unwrap() {
|
||||||
|
// Event::Empty(e) => match e.name() {
|
||||||
|
// QName(b"proceed") => {
|
||||||
|
// let connector = TlsConnector::new().unwrap();
|
||||||
|
// let stream = self
|
||||||
|
// .reader
|
||||||
|
// .into_inner()
|
||||||
|
// .into_inner()
|
||||||
|
// .unsplit(self.writer.into_inner());
|
||||||
|
// if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector)
|
||||||
|
// .connect(&self.jabber.server, stream)
|
||||||
|
// .await
|
||||||
|
// {
|
||||||
|
// let (read, write) = tokio::io::split(tlsstream);
|
||||||
|
// let reader = Reader::from_reader(BufReader::new(read));
|
||||||
|
// let writer = Writer::new(write);
|
||||||
|
// let mut client =
|
||||||
|
// super::encrypted::JabberClient::new(reader, writer, self.jabber);
|
||||||
|
// client.start_stream().await?;
|
||||||
|
// return Ok(client);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// QName(_) => return Err(JabberError::TlsNegotiation),
|
||||||
|
// },
|
||||||
|
// _ => return Err(JabberError::TlsNegotiation),
|
||||||
|
// }
|
||||||
|
// Err(JabberError::TlsNegotiation)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::net::{IpAddr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use quick_xml::{Reader, Writer};
|
use quick_xml::{NsReader, Writer};
|
||||||
use rsasl::prelude::SASLConfig;
|
use rsasl::prelude::SASLConfig;
|
||||||
use tokio::io::BufReader;
|
use tokio::io::BufReader;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
@ -22,7 +22,7 @@ pub struct Jabber<'j> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'j> Jabber<'j> {
|
impl<'j> Jabber<'j> {
|
||||||
pub fn new(jid: JID, password: String) -> Result<Self> {
|
pub fn user(jid: JID, password: String) -> Result<Self> {
|
||||||
let server = jid.domainpart.clone();
|
let server = jid.domainpart.clone();
|
||||||
let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?;
|
let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?;
|
||||||
println!("auth: {:?}", auth);
|
println!("auth: {:?}", auth);
|
||||||
|
@ -36,7 +36,7 @@ impl<'j> Jabber<'j> {
|
||||||
|
|
||||||
pub async fn login(&'j mut self) -> Result<JabberClient<'j>> {
|
pub async fn login(&'j mut self) -> Result<JabberClient<'j>> {
|
||||||
let mut client = self.connect().await?.ensure_tls().await?;
|
let mut client = self.connect().await?.ensure_tls().await?;
|
||||||
println!("negotiation");
|
client.start_stream().await?;
|
||||||
client.negotiate().await?;
|
client.negotiate().await?;
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,7 @@ impl<'j> Jabber<'j> {
|
||||||
socket_addrs
|
socket_addrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// establishes a connection to the server
|
||||||
pub async fn connect(&'j mut self) -> Result<JabberClientType> {
|
pub async fn connect(&'j mut self) -> Result<JabberClientType> {
|
||||||
for (socket_addr, is_tls) in self.get_sockets().await {
|
for (socket_addr, is_tls) in self.get_sockets().await {
|
||||||
println!("trying {}", socket_addr);
|
println!("trying {}", socket_addr);
|
||||||
|
@ -118,21 +119,18 @@ impl<'j> Jabber<'j> {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let (read, write) = tokio::io::split(stream);
|
let (read, write) = tokio::io::split(stream);
|
||||||
let reader = Reader::from_reader(BufReader::new(read));
|
let reader = NsReader::from_reader(BufReader::new(read));
|
||||||
let writer = Writer::new(write);
|
let writer = Writer::new(write);
|
||||||
let mut client = client::encrypted::JabberClient::new(reader, writer, self);
|
let client = client::encrypted::JabberClient::new(reader, writer, self);
|
||||||
client.start_stream().await?;
|
|
||||||
return Ok(JabberClientType::Encrypted(client));
|
return Ok(JabberClientType::Encrypted(client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
if let Ok(stream) = TcpStream::connect(socket_addr).await {
|
if let Ok(stream) = TcpStream::connect(socket_addr).await {
|
||||||
let (read, write) = tokio::io::split(stream);
|
let (read, write) = tokio::io::split(stream);
|
||||||
let reader = Reader::from_reader(BufReader::new(read));
|
let reader = NsReader::from_reader(BufReader::new(read));
|
||||||
let writer = Writer::new(write);
|
let writer = Writer::new(write);
|
||||||
let mut client =
|
let client = client::unencrypted::JabberClient::new(reader, writer, self);
|
||||||
client::unencrypted::JabberClient::new(reader, writer, self);
|
|
||||||
client.start_stream().await?;
|
|
||||||
return Ok(JabberClientType::Unencrypted(client));
|
return Ok(JabberClientType::Unencrypted(client));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub struct JID {
|
pub struct JID {
|
||||||
// TODO: validate localpart (length, char]
|
// TODO: validate localpart (length, char]
|
||||||
|
@ -8,6 +10,15 @@ pub struct JID {
|
||||||
pub resourcepart: Option<String>,
|
pub resourcepart: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for JID {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum JIDError {
|
pub enum JIDError {
|
||||||
NoResourcePart,
|
NoResourcePart,
|
||||||
ParseError(ParseError),
|
ParseError(ParseError),
|
||||||
|
@ -97,6 +108,14 @@ impl TryFrom<String> for JID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for JID {
|
||||||
|
type Error = ParseError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
value.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for JID {
|
impl std::fmt::Display for JID {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
25
src/lib.rs
25
src/lib.rs
|
@ -8,7 +8,7 @@ pub mod jabber;
|
||||||
pub mod jid;
|
pub mod jid;
|
||||||
pub mod stanza;
|
pub mod stanza;
|
||||||
|
|
||||||
pub use client::encrypted::JabberClient;
|
// pub use client::encrypted::JabberClient;
|
||||||
pub use error::JabberError;
|
pub use error::JabberError;
|
||||||
pub use jabber::Jabber;
|
pub use jabber::Jabber;
|
||||||
pub use jid::JID;
|
pub use jid::JID;
|
||||||
|
@ -22,30 +22,9 @@ mod tests {
|
||||||
use crate::Jabber;
|
use crate::Jabber;
|
||||||
use crate::JID;
|
use crate::JID;
|
||||||
|
|
||||||
// #[tokio::test]
|
|
||||||
// async fn get_sockets() {
|
|
||||||
// let jabber = Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned());
|
|
||||||
// println!("{:?}", jabber.get_sockets().await)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[tokio::test]
|
|
||||||
// async fn connect() {
|
|
||||||
// Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned())
|
|
||||||
// .unwrap()
|
|
||||||
// .connect()
|
|
||||||
// .await
|
|
||||||
// .unwrap()
|
|
||||||
// .ensure_tls()
|
|
||||||
// .await
|
|
||||||
// .unwrap()
|
|
||||||
// .start_stream()
|
|
||||||
// .await
|
|
||||||
// .unwrap();
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn login() {
|
async fn login() {
|
||||||
Jabber::new(
|
Jabber::user(
|
||||||
JID::from_str("test@blos.sm/clown").unwrap(),
|
JID::from_str("test@blos.sm/clown").unwrap(),
|
||||||
"slayed".to_owned(),
|
"slayed".to_owned(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,48 +1 @@
|
||||||
use super::{Element, ElementParseError};
|
|
||||||
use crate::{JabberError, JID};
|
|
||||||
|
|
||||||
const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind";
|
|
||||||
|
|
||||||
pub struct Bind {
|
|
||||||
pub resource: Option<String>,
|
|
||||||
pub jid: Option<JID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Bind> for Element {
|
|
||||||
fn from(bind: Bind) -> Self {
|
|
||||||
let bind_element = Element::new("bind", None, XMLNS);
|
|
||||||
bind_element.push_namespace_declaration((None, XMLNS));
|
|
||||||
if let Some(resource) = bind.resource {
|
|
||||||
let resource_element = Element::new("resource", None, XMLNS);
|
|
||||||
resource_element.push_child(resource);
|
|
||||||
bind_element.push_child(resource_element)
|
|
||||||
}
|
|
||||||
if let Some(jid) = bind.jid {
|
|
||||||
let jid_element = Element::new("jid", None, XMLNS);
|
|
||||||
jid_element.push_child(jid);
|
|
||||||
bind_element.push_child(jid_element)
|
|
||||||
}
|
|
||||||
bind_element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Element> for Bind {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(element: Element) -> Result<Self, Self::Error> {
|
|
||||||
if element.namespace() == XMLNS && element.localname() == "bind" {
|
|
||||||
let (resource, jid);
|
|
||||||
let child: &Element = element.child()?;
|
|
||||||
if child.namespace() == XMLNS {
|
|
||||||
match child.localname() {
|
|
||||||
"resource" => Bind::new(Some(
|
|
||||||
child
|
|
||||||
.text_content()?
|
|
||||||
.first()
|
|
||||||
.ok_or(ElementParseError::NoContent)?,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
169
src/stanza/iq.rs
169
src/stanza/iq.rs
|
@ -1,170 +1 @@
|
||||||
use nanoid::nanoid;
|
|
||||||
use quick_xml::{
|
|
||||||
events::{BytesStart, Event},
|
|
||||||
name::QName,
|
|
||||||
Reader, Writer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{JabberClient, JabberError, JID};
|
|
||||||
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct IQ {
|
|
||||||
to: Option<JID>,
|
|
||||||
from: Option<JID>,
|
|
||||||
id: String,
|
|
||||||
r#type: IQType,
|
|
||||||
lang: Option<String>,
|
|
||||||
child: Element<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum IQType {
|
|
||||||
Get,
|
|
||||||
Set,
|
|
||||||
Result,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IQ {
|
|
||||||
pub async fn set<'j, R: IntoElement<'static>>(
|
|
||||||
client: &mut JabberClient<'j>,
|
|
||||||
to: Option<JID>,
|
|
||||||
from: Option<JID>,
|
|
||||||
element: R,
|
|
||||||
) -> Result<Element<'static>> {
|
|
||||||
let id = nanoid!();
|
|
||||||
let iq = IQ {
|
|
||||||
to,
|
|
||||||
from,
|
|
||||||
id: id.clone(),
|
|
||||||
r#type: IQType::Set,
|
|
||||||
lang: None,
|
|
||||||
child: Element::from(element),
|
|
||||||
};
|
|
||||||
println!("{:?}", iq);
|
|
||||||
let iq = Element::from(iq);
|
|
||||||
println!("{:?}", iq);
|
|
||||||
iq.write(&mut client.writer).await?;
|
|
||||||
let result = Element::read(&mut client.reader).await?;
|
|
||||||
let iq = IQ::try_from(result)?;
|
|
||||||
if iq.id == id {
|
|
||||||
return Ok(iq.child);
|
|
||||||
}
|
|
||||||
Err(JabberError::IDMismatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> IntoElement<'e> for IQ {
|
|
||||||
fn event(&self) -> quick_xml::events::Event<'e> {
|
|
||||||
let mut start = BytesStart::new("iq");
|
|
||||||
if let Some(to) = &self.to {
|
|
||||||
start.push_attribute(("to", to.to_string().as_str()));
|
|
||||||
}
|
|
||||||
if let Some(from) = &self.from {
|
|
||||||
start.push_attribute(("from", from.to_string().as_str()));
|
|
||||||
}
|
|
||||||
start.push_attribute(("id", self.id.as_str()));
|
|
||||||
match self.r#type {
|
|
||||||
IQType::Get => start.push_attribute(("type", "get")),
|
|
||||||
IQType::Set => start.push_attribute(("type", "set")),
|
|
||||||
IQType::Result => start.push_attribute(("type", "result")),
|
|
||||||
IQType::Error => start.push_attribute(("type", "error")),
|
|
||||||
}
|
|
||||||
if let Some(lang) = &self.lang {
|
|
||||||
start.push_attribute(("from", lang.to_string().as_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
quick_xml::events::Event::Start(start)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children(&self) -> Option<Vec<Element<'e>>> {
|
|
||||||
Some(vec![self.child.clone()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Element<'static>> for IQ {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(element: Element<'static>) -> std::result::Result<Self, Self::Error> {
|
|
||||||
if let Event::Start(start) = &element.event {
|
|
||||||
if start.name() == QName(b"iq") {
|
|
||||||
let mut to: Option<JID> = None;
|
|
||||||
let mut from: Option<JID> = None;
|
|
||||||
let mut id = None;
|
|
||||||
let mut r#type = None;
|
|
||||||
let mut lang = None;
|
|
||||||
start
|
|
||||||
.attributes()
|
|
||||||
.into_iter()
|
|
||||||
.try_for_each(|attribute| -> Result<()> {
|
|
||||||
if let Ok(attribute) = attribute {
|
|
||||||
let buf: Vec<u8> = Vec::new();
|
|
||||||
let reader = Reader::from_reader(buf);
|
|
||||||
match attribute.key {
|
|
||||||
QName(b"to") => {
|
|
||||||
to = Some(
|
|
||||||
attribute
|
|
||||||
.decode_and_unescape_value(&reader)
|
|
||||||
.or(Err(JabberError::Utf8Decode))?
|
|
||||||
.into_owned()
|
|
||||||
.try_into()?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
QName(b"from") => {
|
|
||||||
from = Some(
|
|
||||||
attribute
|
|
||||||
.decode_and_unescape_value(&reader)
|
|
||||||
.or(Err(JabberError::Utf8Decode))?
|
|
||||||
.into_owned()
|
|
||||||
.try_into()?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
QName(b"id") => {
|
|
||||||
id = Some(
|
|
||||||
attribute
|
|
||||||
.decode_and_unescape_value(&reader)
|
|
||||||
.or(Err(JabberError::Utf8Decode))?
|
|
||||||
.into_owned(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
QName(b"type") => {
|
|
||||||
let value = attribute
|
|
||||||
.decode_and_unescape_value(&reader)
|
|
||||||
.or(Err(JabberError::Utf8Decode))?;
|
|
||||||
match value.as_ref() {
|
|
||||||
"get" => r#type = Some(IQType::Get),
|
|
||||||
"set" => r#type = Some(IQType::Set),
|
|
||||||
"result" => r#type = Some(IQType::Result),
|
|
||||||
"error" => r#type = Some(IQType::Error),
|
|
||||||
_ => return Err(JabberError::ParseError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QName(b"lang") => {
|
|
||||||
lang = Some(
|
|
||||||
attribute
|
|
||||||
.decode_and_unescape_value(&reader)
|
|
||||||
.or(Err(JabberError::Utf8Decode))?
|
|
||||||
.into_owned(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => return Err(JabberError::UnknownAttribute),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
let iq = IQ {
|
|
||||||
to,
|
|
||||||
from,
|
|
||||||
id: id.ok_or(JabberError::NoID)?,
|
|
||||||
r#type: r#type.ok_or(JabberError::NoType)?,
|
|
||||||
lang,
|
|
||||||
child: element.child()?.to_owned(),
|
|
||||||
};
|
|
||||||
return Ok(iq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(JabberError::ParseError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1,657 +1,11 @@
|
||||||
// use quick_xml::events::BytesDecl;
|
|
||||||
|
|
||||||
pub mod bind;
|
pub mod bind;
|
||||||
pub mod iq;
|
pub mod iq;
|
||||||
|
pub mod message;
|
||||||
|
pub mod presence;
|
||||||
pub mod sasl;
|
pub mod sasl;
|
||||||
|
pub mod starttls;
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use quick_xml::events::{BytesDecl, Event};
|
||||||
use std::str;
|
|
||||||
|
|
||||||
// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None);
|
pub static DECLARATION: Event = Event::Decl(BytesDecl::new("1.0", None, None));
|
||||||
use async_recursion::async_recursion;
|
|
||||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
|
||||||
use quick_xml::name::PrefixDeclaration;
|
|
||||||
use quick_xml::{Reader, Writer};
|
|
||||||
use tokio::io::{AsyncBufRead, AsyncWrite};
|
|
||||||
|
|
||||||
use crate::{JabberError, Result};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
/// represents an xml element as a tree of nodes
|
|
||||||
pub struct Element {
|
|
||||||
/// element prefix
|
|
||||||
/// e.g. `foo` in `<foo:bar />`.
|
|
||||||
prefix: Option<String>,
|
|
||||||
/// element name
|
|
||||||
/// e.g. `bar` in `<foo:bar />`.
|
|
||||||
localname: String,
|
|
||||||
/// qualifying namespace
|
|
||||||
/// an element must be qualified by a namespace
|
|
||||||
/// e.g. for `<stream:features>` in
|
|
||||||
/// ```
|
|
||||||
/// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
|
|
||||||
/// <stream:features>
|
|
||||||
/// <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
|
||||||
/// <compression xmlns='http://jabber.org/features/compress'>
|
|
||||||
/// <method>zlib</method>
|
|
||||||
/// <method>lzw</method>
|
|
||||||
/// </compression>
|
|
||||||
/// </stream:features>
|
|
||||||
/// </stream:stream>
|
|
||||||
/// ```
|
|
||||||
/// would be `"http://etherx.jabber.org/streams"` but for
|
|
||||||
/// ```
|
|
||||||
/// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
|
|
||||||
/// <features>
|
|
||||||
/// <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
|
||||||
/// <compression xmlns='http://jabber.org/features/compress'>
|
|
||||||
/// <method>zlib</method>
|
|
||||||
/// <method>lzw</method>
|
|
||||||
/// </compression>
|
|
||||||
/// </features>
|
|
||||||
/// </stream:stream>
|
|
||||||
/// ```
|
|
||||||
/// would be `"jabber:client"`
|
|
||||||
namespace: String,
|
|
||||||
/// all namespaces applied to element
|
|
||||||
/// e.g. for `<bind>` in
|
|
||||||
/// ```
|
|
||||||
/// <stream:features xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
|
|
||||||
/// <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
|
||||||
/// <compression xmlns='http://jabber.org/features/compress'>
|
|
||||||
/// <method>zlib</method>
|
|
||||||
/// <method>lzw</method>
|
|
||||||
/// </compression>
|
|
||||||
/// </stream:features>
|
|
||||||
/// ```
|
|
||||||
/// would be `[(None, "urn:ietf:params:xml:ns:xmpp-bind")]` despite
|
|
||||||
/// `(Some("stream"), "http://etherx.jabber.org/streams")` also being available
|
|
||||||
// TODO: maybe not even needed, as can calculate when writing which namespaces need to be declared
|
|
||||||
// but then can't have unused namespace on element, confusing.
|
|
||||||
namespace_declarations: Box<BTreeMap<Option<String>, String>>,
|
|
||||||
/// element attributes
|
|
||||||
attributes: Box<BTreeMap<String, String>>,
|
|
||||||
// children elements namespaces contain their parents' namespaces
|
|
||||||
children: Box<Vec<Node>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Node {
|
|
||||||
Element(Element),
|
|
||||||
Text(String),
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Element> for Node {
|
|
||||||
fn from(element: Element) -> Self {
|
|
||||||
Self::Element(element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: ToString> From<S> for Node {
|
|
||||||
fn from(text: S) -> Self {
|
|
||||||
Self::Text(text.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> From<&Node> for Vec<Event<'s>> {
|
|
||||||
fn from(node: &Node) -> Self {
|
|
||||||
match node {
|
|
||||||
Node::Element(e) => e.into(),
|
|
||||||
Node::Text(t) => vec![Event::Text(BytesText::new(t))],
|
|
||||||
Unknown => vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element {
|
|
||||||
/// returns the fully qualified name
|
|
||||||
/// e.g. `foo:bar` in
|
|
||||||
/// `<foo:bar>`.
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
if let Some(prefix) = self.prefix {
|
|
||||||
format!("{}:{}", prefix, self.localname).as_str()
|
|
||||||
} else {
|
|
||||||
&self.localname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the localname.
|
|
||||||
/// e.g. `bar` in `<foo:bar>`
|
|
||||||
pub fn localname(&self) -> &str {
|
|
||||||
&self.localname
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the prefix.
|
|
||||||
/// e.g. `foo` in `<foo:bar>`. returns None if there is
|
|
||||||
/// no prefix.
|
|
||||||
pub fn prefix(&self) -> Option<&str> {
|
|
||||||
self.prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the namespace which applies to the current element, e.g. for
|
|
||||||
/// `<element xmlns='foo' xmlns:bar='bar'>`
|
|
||||||
/// it will be `foo` but for
|
|
||||||
/// `<bar:element xmlns='foo' xmlns:bar='bar'>`
|
|
||||||
/// it will be `bar`.
|
|
||||||
pub fn namespace(&self) -> &str {
|
|
||||||
&self.namespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> From<&Element> for Vec<Event<'s>> {
|
|
||||||
fn from(element: &Element) -> Self {
|
|
||||||
let name = element.name();
|
|
||||||
|
|
||||||
let event = BytesStart::new(name);
|
|
||||||
|
|
||||||
// namespace declarations
|
|
||||||
let namespace_declarations = element.namespace_declarations.iter().map(|declaration| {
|
|
||||||
let (prefix, namespace) = declaration;
|
|
||||||
match prefix {
|
|
||||||
Some(prefix) => return (format!("xmlns:{}", prefix).as_str(), *namespace),
|
|
||||||
None => return ("xmlns", *namespace),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let event = event.with_attributes(namespace_declarations);
|
|
||||||
|
|
||||||
// attributes
|
|
||||||
let event = event.with_attributes(element.attributes.into_iter());
|
|
||||||
|
|
||||||
match element.children.is_empty() {
|
|
||||||
true => return vec![Event::Empty(event)],
|
|
||||||
false => {
|
|
||||||
return {
|
|
||||||
let start: Vec<Event<'s>> = vec![Event::Start(event)];
|
|
||||||
let start_and_content: Vec<Event<'s>> = start
|
|
||||||
.into_iter()
|
|
||||||
.chain({
|
|
||||||
let u = element.children.iter().fold(
|
|
||||||
Vec::new(),
|
|
||||||
|acc: Vec<Event<'s>>, child: &Node<'s>| {
|
|
||||||
acc.into_iter()
|
|
||||||
.chain(Into::<Vec<Event<'s>>>::into(child).into_iter())
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
u
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let full: Vec<Event<'s>> = start_and_content
|
|
||||||
.into_iter()
|
|
||||||
.chain(vec![Event::End(BytesEnd::new(name))])
|
|
||||||
.collect();
|
|
||||||
full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element {
|
|
||||||
/// if there is only one child in the vec of children, will return that element
|
|
||||||
pub fn child(&self) -> Result<&Node> {
|
|
||||||
if self.children.len() == 1 {
|
|
||||||
Ok(&self.children[0])
|
|
||||||
} else if self.children.len() > 1 {
|
|
||||||
Err(ElementError::MultipleChildren.into())
|
|
||||||
} else {
|
|
||||||
Err(ElementError::NoChildren.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns reference to children
|
|
||||||
pub fn children(&self) -> Result<&Vec<Node>> {
|
|
||||||
if !self.children.is_empty() {
|
|
||||||
Ok(&self.children)
|
|
||||||
} else {
|
|
||||||
Err(ElementError::NoChildren.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns text content, error if there is none
|
|
||||||
pub fn text_content(&self) -> Result<Vec<&str>> {
|
|
||||||
let mut text = Vec::new();
|
|
||||||
for node in *self.children {
|
|
||||||
match node {
|
|
||||||
Node::Text(t) => text.push(t),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if text.is_empty() {
|
|
||||||
return Err(ElementError::NotText.into());
|
|
||||||
}
|
|
||||||
Ok(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns whether or not the element is qualified by a namespace, either declared
|
|
||||||
/// by a parent, or itself.
|
|
||||||
fn namespace_qualified<S: AsRef<str>>(
|
|
||||||
&self,
|
|
||||||
namespace_context: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> bool {
|
|
||||||
// create a new local_namespaces combining that in the context and those declared within the element
|
|
||||||
let mut local_namespaces = *namespace_context.clone();
|
|
||||||
self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration));
|
|
||||||
|
|
||||||
if let Some(namespace) = local_namespaces.get(self.prefix) {
|
|
||||||
if namespace != self.namespace {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
for child in *self.children {
|
|
||||||
if child.namespace_qualified(&local_namespaces) == false {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// writes an element to a writer. the element's namespace must be qualified by the
|
|
||||||
/// context given in `local_namespaces` or the element's internal namespace declarations
|
|
||||||
pub async fn write<S: AsRef<str>, W: AsyncWrite + Unpin + Send>(
|
|
||||||
&self,
|
|
||||||
writer: &mut Writer<W>,
|
|
||||||
local_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> Result<()> {
|
|
||||||
// TODO: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s
|
|
||||||
if self.namespace_qualified(local_namespaces) {
|
|
||||||
let events: Vec<Event> = self.into();
|
|
||||||
for event in events {
|
|
||||||
writer.write_event_async(event).await?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ElementError::NamespaceNotQualified.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write_start<S: AsRef<str>, W: AsyncWrite + Unpin + Send>(
|
|
||||||
&self,
|
|
||||||
writer: &mut Writer<W>,
|
|
||||||
local_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> Result<()> {
|
|
||||||
if self.namespace_qualified(local_namespaces) {
|
|
||||||
let mut event = BytesStart::new(self.name());
|
|
||||||
|
|
||||||
// namespace declarations
|
|
||||||
self.namespace_declarations.iter().for_each(|declaration| {
|
|
||||||
let (prefix, namespace) = declaration;
|
|
||||||
match prefix {
|
|
||||||
Some(prefix) => {
|
|
||||||
event.push_attribute((format!("xmlns:{}", prefix).as_str(), *namespace))
|
|
||||||
}
|
|
||||||
None => event.push_attribute(("xmlns", *namespace)),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// attributes
|
|
||||||
let event =
|
|
||||||
event.with_attributes(self.attributes.iter().map(|(attr, value)| (*attr, *value)));
|
|
||||||
|
|
||||||
writer.write_event_async(Event::Start(event)).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ElementError::NamespaceNotQualified.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write_end<W: AsyncWrite + Unpin + Send>(
|
|
||||||
&self,
|
|
||||||
writer: &mut Writer<W>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let event = BytesEnd::new(self.name());
|
|
||||||
writer.write_event_async(Event::End(event)).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_recursion]
|
|
||||||
pub async fn read<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
|
|
||||||
reader: &mut Reader<R>,
|
|
||||||
local_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let node = Node::read_recursive(reader, local_namespaces)
|
|
||||||
.await?
|
|
||||||
.ok_or(JabberError::UnexpectedEnd)?;
|
|
||||||
match node {
|
|
||||||
Node::Element(e) => Ok(e),
|
|
||||||
Node::Text(_) => Err(JabberError::UnexpectedText),
|
|
||||||
Node::Unknown => Err(JabberError::UnexpectedElement),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_start<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
|
|
||||||
reader: &mut Reader<R>,
|
|
||||||
local_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> Result<Element> {
|
|
||||||
let buf = Vec::new();
|
|
||||||
let event = reader.read_event_into_async(&mut buf).await?;
|
|
||||||
match event {
|
|
||||||
Event::Start(e) => {
|
|
||||||
let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
|
|
||||||
let converted_prefix;
|
|
||||||
if let Some(raw_prefix) = prefix {
|
|
||||||
converted_prefix = Some(str::from_utf8(raw_prefix)?)
|
|
||||||
}
|
|
||||||
let prefix = converted_prefix;
|
|
||||||
|
|
||||||
let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();
|
|
||||||
|
|
||||||
let mut local_namespaces = local_namespaces.clone();
|
|
||||||
let mut namespace_declarations = BTreeMap::new();
|
|
||||||
let attributes = BTreeMap::new();
|
|
||||||
|
|
||||||
for attribute in e.attributes() {
|
|
||||||
let attribute = attribute?;
|
|
||||||
if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
|
|
||||||
match prefix_declaration {
|
|
||||||
PrefixDeclaration::Default => {
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(None, value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(None, value);
|
|
||||||
}
|
|
||||||
PrefixDeclaration::Named(prefix) => {
|
|
||||||
let key = str::from_utf8(prefix)?;
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(Some(key), value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(Some(key), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(_) = attributes.insert(
|
|
||||||
str::from_utf8(attribute.key.into_inner())?,
|
|
||||||
str::from_utf8(attribute.value.as_ref())?,
|
|
||||||
) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let namespace = *local_namespaces
|
|
||||||
.get(&prefix)
|
|
||||||
.ok_or(ElementParseError::NoNamespace)?;
|
|
||||||
|
|
||||||
let mut children = Vec::new();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
prefix,
|
|
||||||
localname,
|
|
||||||
namespace,
|
|
||||||
namespace_declarations: Box::new(namespace_declarations),
|
|
||||||
attributes: Box::new(attributes),
|
|
||||||
children: Box::new(children),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
e => Err(ElementError::NotAStart(e).into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
#[async_recursion]
|
|
||||||
async fn read_recursive<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
|
|
||||||
reader: &mut Reader<R>,
|
|
||||||
local_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> Result<Option<Node>> {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let event = reader.read_event_into_async(&mut buf).await?;
|
|
||||||
match event {
|
|
||||||
Event::Empty(e) => {
|
|
||||||
let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
|
|
||||||
let converted_prefix;
|
|
||||||
if let Some(raw_prefix) = prefix {
|
|
||||||
converted_prefix = Some(str::from_utf8(raw_prefix)?)
|
|
||||||
}
|
|
||||||
let prefix = converted_prefix;
|
|
||||||
|
|
||||||
let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();
|
|
||||||
|
|
||||||
let mut local_namespaces = local_namespaces.clone();
|
|
||||||
let mut namespace_declarations = BTreeMap::new();
|
|
||||||
let attributes = BTreeMap::new();
|
|
||||||
|
|
||||||
for attribute in e.attributes() {
|
|
||||||
let attribute = attribute?;
|
|
||||||
if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
|
|
||||||
match prefix_declaration {
|
|
||||||
PrefixDeclaration::Default => {
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(None, value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(None, value);
|
|
||||||
}
|
|
||||||
PrefixDeclaration::Named(prefix) => {
|
|
||||||
let key = str::from_utf8(prefix)?;
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(Some(key), value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(Some(key), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(_) = attributes.insert(
|
|
||||||
str::from_utf8(attribute.key.into_inner())?,
|
|
||||||
str::from_utf8(attribute.value.as_ref())?,
|
|
||||||
) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let namespace = *local_namespaces
|
|
||||||
.get(&prefix)
|
|
||||||
.ok_or(ElementParseError::NoNamespace)?;
|
|
||||||
|
|
||||||
let mut children = Vec::new();
|
|
||||||
|
|
||||||
Ok(Some(Self::Element(Element {
|
|
||||||
prefix,
|
|
||||||
localname,
|
|
||||||
namespace,
|
|
||||||
namespace_declarations: Box::new(namespace_declarations),
|
|
||||||
attributes: Box::new(attributes),
|
|
||||||
children: Box::new(children),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
Event::Start(e) => {
|
|
||||||
let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
|
|
||||||
let converted_prefix;
|
|
||||||
if let Some(raw_prefix) = prefix {
|
|
||||||
converted_prefix = Some(str::from_utf8(raw_prefix)?)
|
|
||||||
}
|
|
||||||
let prefix = converted_prefix;
|
|
||||||
|
|
||||||
let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();
|
|
||||||
|
|
||||||
let mut local_namespaces = local_namespaces.clone();
|
|
||||||
let mut namespace_declarations = BTreeMap::new();
|
|
||||||
let attributes = BTreeMap::new();
|
|
||||||
|
|
||||||
for attribute in e.attributes() {
|
|
||||||
let attribute = attribute?;
|
|
||||||
if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
|
|
||||||
match prefix_declaration {
|
|
||||||
PrefixDeclaration::Default => {
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(None, value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(None, value);
|
|
||||||
}
|
|
||||||
PrefixDeclaration::Named(prefix) => {
|
|
||||||
let key = str::from_utf8(prefix)?;
|
|
||||||
let value = str::from_utf8(attribute.value.as_ref())?;
|
|
||||||
if let Some(_) = namespace_declarations.insert(Some(key), value) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
local_namespaces.insert(Some(key), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let Some(_) = attributes.insert(
|
|
||||||
str::from_utf8(attribute.key.into_inner())?,
|
|
||||||
str::from_utf8(attribute.value.as_ref())?,
|
|
||||||
) {
|
|
||||||
return Err(ElementParseError::DuplicateAttribute.into());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let namespace = *local_namespaces
|
|
||||||
.get(&prefix)
|
|
||||||
.ok_or(ElementParseError::NoNamespace)?;
|
|
||||||
|
|
||||||
let mut children = Vec::new();
|
|
||||||
while let Some(child_node) = Node::read_recursive(reader, &local_namespaces).await?
|
|
||||||
{
|
|
||||||
children.push(child_node)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut children = Vec::new();
|
|
||||||
|
|
||||||
Ok(Some(Self::Element(Element {
|
|
||||||
prefix,
|
|
||||||
localname,
|
|
||||||
namespace,
|
|
||||||
namespace_declarations: Box::new(namespace_declarations),
|
|
||||||
attributes: Box::new(attributes),
|
|
||||||
children: Box::new(children),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
Event::End(_) => Ok(None),
|
|
||||||
Event::Text(e) => Ok(Some(Self::Text(e.unescape()?.as_ref().to_string()))),
|
|
||||||
e => Ok(Some(Self::Unknown)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_qualified<S: AsRef<str>>(
|
|
||||||
&self,
|
|
||||||
namespace_context: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Element(e) => e.namespace_qualified(namespace_context),
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum NodeBuilder {
|
|
||||||
Text(String),
|
|
||||||
Element(ElementBuilder),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ElementBuilder {
|
|
||||||
localname: String,
|
|
||||||
prefix: Option<String>,
|
|
||||||
namespace: String,
|
|
||||||
namespace_declarations: BTreeMap<Option<String>, String>,
|
|
||||||
attributes: BTreeMap<String, String>,
|
|
||||||
children: Vec<NodeBuilder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ElementBuilder {
|
|
||||||
pub fn new<S: ToString>(localname: S, prefix: Option<S>, namespace: S) -> Self {
|
|
||||||
Self {
|
|
||||||
prefix,
|
|
||||||
localname,
|
|
||||||
namespace,
|
|
||||||
namespace_declarations: Box::new(BTreeMap::new()),
|
|
||||||
attributes: Box::new(BTreeMap::new()),
|
|
||||||
children: Box::new(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_namespace_declaration<S: ToString>(
|
|
||||||
&mut self,
|
|
||||||
(prefix, namespace): (Option<S>, S),
|
|
||||||
) -> Option<S> {
|
|
||||||
self.namespace_declarations.insert(prefix, namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_attribute<S: ToString>(&mut self, (key, value): (S, S)) -> Option<S> {
|
|
||||||
self.attributes.insert(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_child(&mut self, child: Node) {
|
|
||||||
self.children.push(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// checks if there is a namespace conflict within the element being built
|
|
||||||
pub fn namespace_conflict<S: AsRef<str>>(
|
|
||||||
&self
|
|
||||||
) -> bool {
|
|
||||||
self.namespace_conflict_recursive(&BTreeMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_conflict_recursive<S: AsRef<str>>(
|
|
||||||
&self,
|
|
||||||
parent_namespaces: &BTreeMap<Option<S>, S>,
|
|
||||||
) -> bool {
|
|
||||||
// create a new local_namespaces combining that in the context and those declared within the element
|
|
||||||
let mut local_namespaces = *parent_namespaces.clone();
|
|
||||||
self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration));
|
|
||||||
|
|
||||||
if let Some(namespace) = local_namespaces.get(self.prefix) {
|
|
||||||
if namespace != self.namespace {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
for child in *self.children {
|
|
||||||
if child.namespace_conflict(&local_namespaces) == false {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for possible conflicts in namespace
|
|
||||||
pub fn build(self) -> Result<Element> {
|
|
||||||
for child in self.children {
|
|
||||||
match child {
|
|
||||||
Node::Element(e) => {
|
|
||||||
if !e.namespace_conflict()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Element {
|
|
||||||
prefix: self.prefix,
|
|
||||||
localname: self.localname,
|
|
||||||
namespace: self.namespace,
|
|
||||||
namespace_declarations: self.namespace_declarations,
|
|
||||||
attributes: self.attributes,
|
|
||||||
children: self.children,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ElementError<'e> {
|
|
||||||
NotAStart(Event<'e>),
|
|
||||||
NotText,
|
|
||||||
NoChildren,
|
|
||||||
NamespaceNotQualified,
|
|
||||||
MultipleChildren,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ElementParseError {
|
|
||||||
DuplicateAttribute,
|
|
||||||
NoNamespace,
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1,145 +1 @@
|
||||||
use quick_xml::{
|
|
||||||
events::{BytesStart, BytesText, Event},
|
|
||||||
name::QName,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::error::SASLError;
|
|
||||||
use crate::JabberError;
|
|
||||||
|
|
||||||
use super::Element;
|
|
||||||
|
|
||||||
const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl";
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Auth<'e> {
|
|
||||||
pub mechanism: &'e str,
|
|
||||||
pub sasl_data: &'e str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> IntoElement<'e> for Auth<'e> {
|
|
||||||
fn event(&self) -> Event<'e> {
|
|
||||||
let mut start = BytesStart::new("auth");
|
|
||||||
start.push_attribute(("xmlns", XMLNS));
|
|
||||||
start.push_attribute(("mechanism", self.mechanism));
|
|
||||||
Event::Start(start)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children(&self) -> Option<Vec<Element<'e>>> {
|
|
||||||
let sasl = BytesText::from_escaped(self.sasl_data);
|
|
||||||
let sasl = Element {
|
|
||||||
event: Event::Text(sasl),
|
|
||||||
children: None,
|
|
||||||
};
|
|
||||||
Some(vec![sasl])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Challenge {
|
|
||||||
pub sasl_data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> TryFrom<&Element<'e>> for Challenge {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(element: &Element<'e>) -> Result<Challenge, Self::Error> {
|
|
||||||
if let Event::Start(start) = &element.event {
|
|
||||||
if start.name() == QName(b"challenge") {
|
|
||||||
let sasl_data: &Element<'_> = element.child()?;
|
|
||||||
if let Event::Text(sasl_data) = &sasl_data.event {
|
|
||||||
let s = sasl_data.clone();
|
|
||||||
let s = s.into_inner();
|
|
||||||
let s = s.to_vec();
|
|
||||||
return Ok(Challenge { sasl_data: s });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(SASLError::NoChallenge.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl<'e> TryFrom<Element<'e>> for Challenge {
|
|
||||||
// type Error = JabberError;
|
|
||||||
|
|
||||||
// fn try_from(element: Element<'e>) -> Result<Challenge, Self::Error> {
|
|
||||||
// if let Event::Start(start) = &element.event {
|
|
||||||
// if start.name() == QName(b"challenge") {
|
|
||||||
// println!("one");
|
|
||||||
// if let Some(children) = element.children.as_deref() {
|
|
||||||
// if children.len() == 1 {
|
|
||||||
// let sasl_data = children.first().unwrap();
|
|
||||||
// if let Event::Text(sasl_data) = &sasl_data.event {
|
|
||||||
// return Ok(Challenge {
|
|
||||||
// sasl_data: sasl_data.clone().into_inner().to_vec(),
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// return Err(SASLError::NoChallenge.into());
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return Err(SASLError::NoChallenge.into());
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return Err(SASLError::NoChallenge.into());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Err(SASLError::NoChallenge.into())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Response<'e> {
|
|
||||||
pub sasl_data: &'e str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> IntoElement<'e> for Response<'e> {
|
|
||||||
fn event(&self) -> Event<'e> {
|
|
||||||
let mut start = BytesStart::new("response");
|
|
||||||
start.push_attribute(("xmlns", XMLNS));
|
|
||||||
Event::Start(start)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children(&self) -> Option<Vec<Element<'e>>> {
|
|
||||||
let sasl = BytesText::from_escaped(self.sasl_data);
|
|
||||||
let sasl = Element {
|
|
||||||
event: Event::Text(sasl),
|
|
||||||
children: None,
|
|
||||||
};
|
|
||||||
Some(vec![sasl])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Success {
|
|
||||||
pub sasl_data: Option<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> TryFrom<&Element<'e>> for Success {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(element: &Element<'e>) -> Result<Success, Self::Error> {
|
|
||||||
match &element.event {
|
|
||||||
Event::Start(start) => {
|
|
||||||
if start.name() == QName(b"success") {
|
|
||||||
match element.child() {
|
|
||||||
Ok(sasl_data) => {
|
|
||||||
if let Event::Text(sasl_data) = &sasl_data.event {
|
|
||||||
return Ok(Success {
|
|
||||||
sasl_data: Some(sasl_data.clone().into_inner().to_vec()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => return Ok(Success { sasl_data: None }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Empty(empty) => {
|
|
||||||
if empty.name() == QName(b"success") {
|
|
||||||
return Ok(Success { sasl_data: None });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Err(SASLError::NoSuccess.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1,190 +1,60 @@
|
||||||
use std::str;
|
use serde::Serialize;
|
||||||
|
|
||||||
use quick_xml::{
|
use crate::JID;
|
||||||
events::{BytesStart, Event},
|
|
||||||
name::QName,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Element;
|
pub static XMLNS: &str = "http://etherx.jabber.org/streams";
|
||||||
use crate::{JabberError, Result, JID};
|
pub static XMLNS_CLIENT: &str = "jabber:client";
|
||||||
|
|
||||||
const XMLNS_STREAM: &str = "http://etherx.jabber.org/streams";
|
// MUST be qualified by stream namespace
|
||||||
const VERSION: &str = "1.0";
|
#[derive(Serialize)]
|
||||||
|
pub struct Stream<'s> {
|
||||||
enum XMLNS {
|
#[serde(rename = "@from")]
|
||||||
Client,
|
from: Option<&'s JID>,
|
||||||
Server,
|
#[serde(rename = "@to")]
|
||||||
}
|
to: Option<&'s JID>,
|
||||||
|
#[serde(rename = "@id")]
|
||||||
impl From<XMLNS> for &str {
|
id: Option<&'s str>,
|
||||||
fn from(xmlns: XMLNS) -> Self {
|
#[serde(rename = "@version")]
|
||||||
match xmlns {
|
version: Option<&'s str>,
|
||||||
XMLNS::Client => return "jabber:client",
|
// TODO: lang enum
|
||||||
XMLNS::Server => return "jabber:server",
|
#[serde(rename = "@lang")]
|
||||||
}
|
lang: Option<&'s str>,
|
||||||
}
|
#[serde(rename = "@xmlns")]
|
||||||
}
|
xmlns: &'s str,
|
||||||
|
#[serde(rename = "@xmlns:stream")]
|
||||||
impl TryInto<XMLNS> for &str {
|
xmlns_stream: &'s str,
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_into(self) -> Result<XMLNS> {
|
|
||||||
match self {
|
|
||||||
"jabber:client" => Ok(XMLNS::Client),
|
|
||||||
"jabber:server" => Ok(XMLNS::Server),
|
|
||||||
_ => Err(JabberError::UnknownNamespace),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Stream {
|
|
||||||
from: Option<JID>,
|
|
||||||
id: Option<String>,
|
|
||||||
to: Option<JID>,
|
|
||||||
version: Option<String>,
|
|
||||||
lang: Option<String>,
|
|
||||||
ns: XMLNS,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self {
|
pub fn new(
|
||||||
|
from: Option<&JID>,
|
||||||
|
to: Option<&JID>,
|
||||||
|
id: Option<&str>,
|
||||||
|
version: Option<&str>,
|
||||||
|
lang: Option<&str>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
from: Some(from.clone()),
|
from,
|
||||||
|
to,
|
||||||
id,
|
id,
|
||||||
to: Some(to.clone()),
|
version,
|
||||||
version: Some(VERSION.to_owned()),
|
|
||||||
lang,
|
lang,
|
||||||
ns: XMLNS::Client,
|
xmlns: XMLNS_CLIENT,
|
||||||
|
xmlns_stream: XMLNS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&self) -> Event<'static> {
|
/// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute.
|
||||||
let mut start = BytesStart::new("stream:stream");
|
/// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection.
|
||||||
if let Some(from) = &self.from {
|
pub fn new_client(from: Option<&JID>, to: &JID, id: Option<&str>, lang: &str) -> Self {
|
||||||
start.push_attribute(("from", from.to_string().as_str()));
|
Self {
|
||||||
}
|
from,
|
||||||
if let Some(id) = &self.id {
|
to: Some(to),
|
||||||
start.push_attribute(("id", id.as_str()));
|
id,
|
||||||
}
|
version: Some("1.0"),
|
||||||
if let Some(to) = &self.to {
|
lang: Some(lang),
|
||||||
start.push_attribute(("to", to.to_string().as_str()));
|
xmlns: XMLNS_CLIENT,
|
||||||
}
|
xmlns_stream: XMLNS,
|
||||||
if let Some(version) = &self.version {
|
|
||||||
start.push_attribute(("version", version.to_string().as_str()));
|
|
||||||
}
|
|
||||||
if let Some(lang) = &self.lang {
|
|
||||||
start.push_attribute(("xml:lang", lang.as_str()));
|
|
||||||
}
|
|
||||||
match &self.ns {
|
|
||||||
XMLNS::Client => start.push_attribute(("xmlns", XMLNS::Client.into())),
|
|
||||||
XMLNS::Server => start.push_attribute(("xmlns", XMLNS::Server.into())),
|
|
||||||
}
|
|
||||||
start.push_attribute(("xmlns:stream", XMLNS_STREAM));
|
|
||||||
Event::Start(start)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> Into<Element<'e>> for Stream {
|
|
||||||
fn into(self) -> Element<'e> {
|
|
||||||
Element {
|
|
||||||
event: self.event(),
|
|
||||||
children: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e> TryFrom<Element<'e>> for Stream {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(value: Element<'e>) -> Result<Stream> {
|
|
||||||
let (mut from, mut id, mut to, mut version, mut lang, mut ns) =
|
|
||||||
(None, None, None, None, None, XMLNS::Client);
|
|
||||||
if let Event::Start(e) = value.event.as_ref() {
|
|
||||||
for attribute in e.attributes() {
|
|
||||||
let attribute = attribute?;
|
|
||||||
match attribute.key {
|
|
||||||
QName(b"from") => {
|
|
||||||
from = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
|
|
||||||
}
|
|
||||||
QName(b"id") => {
|
|
||||||
id = Some(str::from_utf8(&attribute.value)?.to_owned());
|
|
||||||
}
|
|
||||||
QName(b"to") => {
|
|
||||||
to = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
|
|
||||||
}
|
|
||||||
QName(b"version") => {
|
|
||||||
version = Some(str::from_utf8(&attribute.value)?.to_owned());
|
|
||||||
}
|
|
||||||
QName(b"lang") => {
|
|
||||||
lang = Some(str::from_utf8(&attribute.value)?.to_owned());
|
|
||||||
}
|
|
||||||
QName(b"xmlns") => {
|
|
||||||
ns = str::from_utf8(&attribute.value)?.try_into()?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("unknown attribute: {:?}", attribute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Stream {
|
|
||||||
from,
|
|
||||||
id,
|
|
||||||
to,
|
|
||||||
version,
|
|
||||||
lang,
|
|
||||||
ns,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(JabberError::ParseError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
pub enum StreamFeature {
|
|
||||||
StartTls,
|
|
||||||
Sasl(Vec<String>),
|
|
||||||
Bind,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e> TryFrom<Element<'e>> for Vec<StreamFeature> {
|
|
||||||
type Error = JabberError;
|
|
||||||
|
|
||||||
fn try_from(features_element: Element) -> Result<Self> {
|
|
||||||
let mut features = Vec::new();
|
|
||||||
if let Some(children) = features_element.children {
|
|
||||||
for feature_element in children {
|
|
||||||
match feature_element.event {
|
|
||||||
Event::Start(e) => match e.name() {
|
|
||||||
QName(b"starttls") => features.push(StreamFeature::StartTls),
|
|
||||||
QName(b"mechanisms") => {
|
|
||||||
let mut mechanisms = Vec::new();
|
|
||||||
if let Some(children) = feature_element.children {
|
|
||||||
for mechanism_element in children {
|
|
||||||
if let Some(children) = mechanism_element.children {
|
|
||||||
for mechanism_text in children {
|
|
||||||
match mechanism_text.event {
|
|
||||||
Event::Text(e) => mechanisms
|
|
||||||
.push(str::from_utf8(e.as_ref())?.to_owned()),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
features.push(StreamFeature::Sasl(mechanisms))
|
|
||||||
}
|
|
||||||
_ => features.push(StreamFeature::Unknown),
|
|
||||||
},
|
|
||||||
Event::Empty(e) => match e.name() {
|
|
||||||
QName(b"bind") => features.push(StreamFeature::Bind),
|
|
||||||
_ => features.push(StreamFeature::Unknown),
|
|
||||||
},
|
|
||||||
_ => features.push(StreamFeature::Unknown),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(features)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue