remove serde functions

This commit is contained in:
cel 🌸 2023-07-11 21:28:42 +01:00
parent 143a0365d0
commit f43911ccba
Signed by: cel
GPG Key ID: 48E29AF13B5F1349
13 changed files with 389 additions and 309 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-recursion = "1.0.4"
async-trait = "0.1.68"
quick-xml = { git = "https://github.com/tafia/quick-xml.git", features = ["async-tokio", "serialize"] }
# TODO: remove unneeded features

5
notes.md Normal file
View File

@ -0,0 +1,5 @@
tag = <tag> or </tag>
element = <sajdlfkajsdf/> or <slakdfj></slakdfj>
stanza = specific element <message/> <presence/> <iq/>

View File

@ -1,35 +1,26 @@
use std::str;
use quick_xml::{
de::Deserializer,
events::{BytesDecl, BytesStart, Event},
name::QName,
se::Serializer,
events::{BytesDecl, Event},
Reader, Writer,
};
use rsasl::prelude::{Mechname, SASLClient};
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncWriteExt, BufReader, ReadHalf, WriteHalf};
use tokio::io::{BufReader, ReadHalf, WriteHalf};
use tokio::net::TcpStream;
use tokio_native_tls::TlsStream;
use crate::stanza::{
sasl::{Auth, Challenge, Mechanisms},
stream::{StreamFeature, StreamFeatures},
};
use crate::element::Element;
use crate::stanza::stream::{Stream, StreamFeature};
use crate::Jabber;
use crate::Result;
pub struct JabberClient<'j> {
reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
writer: WriteHalf<TlsStream<TcpStream>>,
writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
jabber: &'j mut Jabber<'j>,
}
impl<'j> JabberClient<'j> {
pub fn new(
reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
writer: WriteHalf<TlsStream<TcpStream>>,
writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
jabber: &'j mut Jabber<'j>,
) -> Self {
Self {
@ -40,90 +31,29 @@ impl<'j> JabberClient<'j> {
}
pub async fn start_stream(&mut self) -> Result<()> {
// client to server
let declaration = BytesDecl::new("1.0", None, None);
let mut stream_element = BytesStart::new("stream:stream");
stream_element.push_attribute(("from".as_bytes(), self.jabber.jid.to_string().as_bytes()));
stream_element.push_attribute(("to".as_bytes(), self.jabber.server.as_bytes()));
stream_element.push_attribute(("version", "1.0"));
stream_element.push_attribute(("xml:lang", "en"));
stream_element.push_attribute(("xmlns", "jabber:client"));
stream_element.push_attribute(("xmlns:stream", "http://etherx.jabber.org/streams"));
let mut writer = Writer::new(&mut self.writer);
writer.write_event_async(Event::Decl(declaration)).await;
writer.write_event_async(Event::Start(stream_element)).await;
let server = &self.jabber.server.to_owned().try_into()?;
let stream_element =
Stream::new_client(&self.jabber.jid, server, None, Some("en".to_string()));
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
let mut buf = Vec::new();
loop {
match self.reader.read_event_into_async(&mut buf).await.unwrap() {
Event::Start(e) => {
println!("{:?}", e);
break;
}
e => println!("decl: {:?}", e),
};
}
self.reader.read_event_into_async(&mut buf).await?;
let _stream_response = Element::read_start(&mut self.reader).await?;
Ok(())
}
pub async fn get_node<'a>(&mut self) -> Result<String> {
let mut buf = Vec::new();
let mut txt = Vec::new();
let mut qname_set = false;
let mut qname: Option<Vec<u8>> = None;
loop {
match self.reader.read_event_into_async(&mut buf).await? {
Event::Start(e) => {
if !qname_set {
qname = Some(e.name().into_inner().to_owned());
qname_set = true;
pub async fn get_features(&mut self) -> Result<Option<Vec<StreamFeature>>> {
if let Some(features) = Element::read(&mut self.reader).await? {
Ok(Some(features.try_into()?))
} else {
Ok(None)
}
txt.push(b'<');
txt = txt
.into_iter()
.chain(buf.to_owned())
.chain(vec![b'>'])
.collect();
}
Event::End(e) => {
let mut end = false;
if e.name() == QName(qname.as_deref().unwrap()) {
end = true;
}
txt.push(b'<');
txt = txt
.into_iter()
.chain(buf.to_owned())
.chain(vec![b'>'])
.collect();
if end {
break;
}
}
Event::Text(_e) => {
txt = txt.into_iter().chain(buf.to_owned()).collect();
}
_ => {
txt.push(b'<');
txt = txt
.into_iter()
.chain(buf.to_owned())
.chain(vec![b'>'])
.collect();
}
}
buf.clear();
}
println!("{:?}", txt);
let decoded = str::from_utf8(&txt)?.to_owned();
println!("{:?}", decoded);
Ok(decoded)
}
pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
let node = self.get_node().await?;
let mut deserializer = Deserializer::from_str(&node);
let features = StreamFeatures::deserialize(&mut deserializer).unwrap();
println!("{:?}", features);
Ok(features.features)
}
pub async fn negotiate(&mut self) -> Result<()> {
@ -131,98 +61,14 @@ impl<'j> JabberClient<'j> {
println!("loop");
let features = &self.get_features().await?;
println!("{:?}", features);
match &features[0] {
StreamFeature::Sasl(sasl) => {
println!("{:?}", sasl);
self.sasl(&sasl).await?;
// match &features[0] {
// StreamFeature::Sasl(sasl) => {
// println!("{:?}", sasl);
// todo!()
// }
// StreamFeature::Bind => todo!(),
// x => println!("{:?}", x),
// }
}
StreamFeature::Bind => todo!(),
x => println!("{:?}", x),
}
}
}
pub async fn sasl(&mut self, mechanisms: &Mechanisms) -> Result<()> {
println!("{:?}", mechanisms);
let sasl = SASLClient::new(self.jabber.auth.clone());
let mut offered_mechs: Vec<&Mechname> = Vec::new();
for mechanism in &mechanisms.mechanisms {
offered_mechs.push(Mechname::parse(&mechanism.mechanism.as_bytes())?)
}
println!("{:?}", offered_mechs);
let mut session = sasl.start_suggested(&offered_mechs)?;
let selected_mechanism = session.get_mechname().as_str().to_owned();
println!("selected mech: {:?}", selected_mechanism);
let mut data: Option<Vec<u8>> = None;
if !session.are_we_first() {
// if not first mention the mechanism then get challenge data
// mention mechanism
let auth = Auth {
ns: "urn:ietf:params:xml:ns:xmpp-sasl".to_owned(),
mechanism: selected_mechanism.clone(),
sasl_data: Some("=".to_owned()),
};
let mut buffer = String::new();
let ser = Serializer::new(&mut buffer);
auth.serialize(ser).unwrap();
self.writer.write_all(buffer.as_bytes());
// get challenge data
let node = self.get_node().await?;
let mut deserializer = Deserializer::from_str(&node);
let challenge = Challenge::deserialize(&mut deserializer).unwrap();
println!("challenge: {:?}", challenge);
data = Some(challenge.sasl_data.as_bytes().to_owned());
println!("we didn't go first");
} else {
// if first, mention mechanism and send data
let mut sasl_data = Vec::new();
session.step64(None, &mut sasl_data).unwrap();
let auth = Auth {
ns: "urn:ietf:params:xml:ns:xmpp-sasl".to_owned(),
mechanism: selected_mechanism.clone(),
sasl_data: Some(str::from_utf8(&sasl_data).unwrap().to_owned()),
};
let mut buffer = String::new();
let ser = Serializer::new(&mut buffer);
auth.serialize(ser).unwrap();
println!("node: {:?}", buffer);
self.writer.write_all(buffer.as_bytes()).await;
println!("we went first");
// get challenge data
// TODO: check if needed
// let node = self.get_node().await?;
// println!("node: {:?}", node);
// let mut deserializer = Deserializer::from_str(&node);
// let challenge = Challenge::deserialize(&mut deserializer).unwrap();
// println!("challenge: {:?}", challenge);
// data = Some(challenge.sasl_data.as_bytes().to_owned());
}
// stepping the authentication exchange to completion
let mut sasl_data = Vec::new();
while {
// decide if need to send more data over
let state = session
.step64(data.as_deref(), &mut sasl_data)
.expect("step errored!");
state.is_running()
} {
// While we aren't finished, receive more data from the other party
let auth = Auth {
ns: "urn:ietf:params:xml:ns:xmpp-sasl".to_owned(),
mechanism: selected_mechanism.clone(),
sasl_data: Some(str::from_utf8(&sasl_data).unwrap().to_owned()),
};
let mut buffer = String::new();
let ser = Serializer::new(&mut buffer);
auth.serialize(ser).unwrap();
self.writer.write_all(buffer.as_bytes());
let node = self.get_node().await?;
let mut deserializer = Deserializer::from_str(&node);
let challenge = Challenge::deserialize(&mut deserializer).unwrap();
data = Some(challenge.sasl_data.as_bytes().to_owned());
}
self.start_stream().await?;
Ok(())
}
}

View File

@ -15,18 +15,17 @@ pub enum JabberClientType<'j> {
impl<'j> JabberClientType<'j> {
pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {
match self {
Self::Encrypted(mut c) => {
c.start_stream();
Ok(c)
}
Self::Encrypted(c) => Ok(c),
Self::Unencrypted(mut c) => {
c.start_stream().await?;
let features = c.get_features().await?;
if let Some(features) = c.get_features().await? {
if features.contains(&StreamFeature::StartTls) {
Ok(c.starttls().await?)
} else {
Err(JabberError::StartTlsUnavailable)
}
} else {
Err(JabberError::NoFeatures)
}
}
}
}

View File

@ -1,19 +1,19 @@
use std::str;
use quick_xml::{
de::Deserializer,
events::{BytesDecl, BytesStart, Event},
name::QName,
Reader, Writer,
};
use serde::Deserialize;
use tokio::io::{BufReader, ReadHalf, WriteHalf};
use tokio::net::TcpStream;
use tokio_native_tls::native_tls::TlsConnector;
use crate::element::Element;
use crate::stanza::stream::StreamFeature;
use crate::Jabber;
use crate::Result;
use crate::{error::JabberError, stanza::stream::StreamFeature};
use crate::{stanza::stream::StreamFeatures, Jabber};
use crate::{error::JabberError, stanza::stream::Stream};
pub struct JabberClient<'j> {
reader: Reader<BufReader<ReadHalf<TcpStream>>>,
@ -35,63 +35,30 @@ impl<'j> JabberClient<'j> {
}
pub async fn start_stream(&mut self) -> Result<()> {
// client to server
let declaration = BytesDecl::new("1.0", None, None);
let mut stream_element = BytesStart::new("stream:stream");
stream_element.push_attribute(("from".as_bytes(), self.jabber.jid.to_string().as_bytes()));
stream_element.push_attribute(("to".as_bytes(), self.jabber.server.as_bytes()));
stream_element.push_attribute(("version", "1.0"));
stream_element.push_attribute(("xml:lang", "en"));
stream_element.push_attribute(("xmlns", "jabber:client"));
stream_element.push_attribute(("xmlns:stream", "http://etherx.jabber.org/streams"));
let server = &self.jabber.server.to_owned().try_into()?;
let stream_element =
Stream::new_client(&self.jabber.jid, server, None, Some("en".to_string()));
self.writer
.write_event_async(Event::Decl(declaration))
.await;
self.writer
.write_event_async(Event::Start(stream_element))
.await
.unwrap();
.await?;
let stream_element: Element<'_> = stream_element.into();
stream_element.write_start(&mut self.writer).await?;
// server to client
let mut buf = Vec::new();
loop {
match self.reader.read_event_into_async(&mut buf).await.unwrap() {
Event::Start(e) => {
println!("{:?}", e);
break;
}
Event::Decl(e) => println!("decl: {:?}", e),
_ => return Err(JabberError::BadStream),
}
}
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>> {
let mut buf = Vec::new();
let mut txt = Vec::new();
let mut loop_end = false;
while !loop_end {
match self.reader.read_event_into_async(&mut buf).await.unwrap() {
Event::End(e) => {
if e.name() == QName(b"stream:features") {
loop_end = true;
}
}
_ => (),
}
txt.push(b'<');
txt = txt
.into_iter()
.chain(buf.to_owned())
.chain(vec![b'>'])
.collect();
buf.clear();
}
println!("{:?}", txt);
let decoded = str::from_utf8(&txt).unwrap();
println!("decoded: {:?}", decoded);
let mut deserializer = Deserializer::from_str(decoded);
let features = StreamFeatures::deserialize(&mut deserializer).unwrap();
pub async fn get_features(&mut self) -> Result<Option<Vec<StreamFeature>>> {
if let Some(features) = Element::read(&mut self.reader).await? {
println!("{:?}", features);
Ok(features.features)
Ok(Some(features.try_into()?))
} else {
Ok(None)
}
}
pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> {
@ -115,8 +82,9 @@ impl<'j> JabberClient<'j> {
.connect(&self.jabber.server, stream)
.await
{
let (read, writer) = tokio::io::split(tlsstream);
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?;

108
src/element.rs Normal file
View File

@ -0,0 +1,108 @@
use async_recursion::async_recursion;
use quick_xml::events::Event;
use quick_xml::{Reader, Writer};
use tokio::io::{AsyncBufRead, AsyncWrite};
use crate::Result;
#[derive(Debug)]
pub struct Element<'e> {
pub event: Event<'e>,
pub content: Option<Vec<Element<'e>>>,
}
// TODO: make method
#[async_recursion]
pub async fn write<'e: 'async_recursion, W: AsyncWrite + Unpin + Send>(
element: Element<'e>,
writer: &mut Writer<W>,
) -> Result<()> {
match element.event {
Event::Start(e) => {
writer.write_event_async(Event::Start(e.clone())).await?;
if let Some(content) = element.content {
for e in content {
write(e, writer).await?;
}
}
writer.write_event_async(Event::End(e.to_end())).await?;
return Ok(());
}
e => Ok(writer.write_event_async(e).await?),
}
}
impl<'e> Element<'e> {
pub async fn write_start<W: AsyncWrite + Unpin + Send>(
&self,
writer: &mut Writer<W>,
) -> Result<()> {
match self.event.as_ref() {
Event::Start(e) => Ok(writer.write_event_async(Event::Start(e.clone())).await?),
e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
}
}
pub async fn write_end<W: AsyncWrite + Unpin + Send>(
&self,
writer: &mut Writer<W>,
) -> Result<()> {
match self.event.as_ref() {
Event::Start(e) => Ok(writer
.write_event_async(Event::End(e.clone().to_end()))
.await?),
e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
}
}
#[async_recursion]
pub async fn read<R: AsyncBufRead + Unpin + Send>(
reader: &mut Reader<R>,
) -> Result<Option<Self>> {
let mut buf = Vec::new();
let event = reader.read_event_into_async(&mut buf).await?;
match event {
Event::Start(e) => {
let mut content_vec = Vec::new();
while let Some(sub_element) = Element::read(reader).await? {
content_vec.push(sub_element)
}
let mut content = None;
if !content_vec.is_empty() {
content = Some(content_vec)
}
Ok(Some(Self {
event: Event::Start(e.into_owned()),
content,
}))
}
Event::End(_) => Ok(None),
e => Ok(Some(Self {
event: e.into_owned(),
content: None,
})),
}
}
#[async_recursion]
pub async fn read_start<R: AsyncBufRead + Unpin + Send>(
reader: &mut Reader<R>,
) -> Result<Self> {
let mut buf = Vec::new();
let event = reader.read_event_into_async(&mut buf).await?;
match event {
Event::Start(e) => {
return Ok(Self {
event: Event::Start(e.into_owned()),
content: None,
})
}
e => Err(ElementError::NotAStart(e.into_owned()).into()),
}
}
}
#[derive(Debug)]
pub enum ElementError<'e> {
NotAStart(Event<'e>),
}

View File

@ -1,7 +1,13 @@
use std::str::Utf8Error;
use quick_xml::events::attributes::AttrError;
use rsasl::mechname::MechanismNameError;
use crate::{
element::{self, ElementError},
jid::ParseError,
};
#[derive(Debug)]
pub enum JabberError {
Connection,
@ -9,8 +15,13 @@ pub enum JabberError {
StartTlsUnavailable,
TlsNegotiation,
Utf8Decode,
NoFeatures,
UnknownNamespace,
ParseError,
XML(quick_xml::Error),
SASL(SASLError),
Element(ElementError<'static>),
JID(ParseError),
}
#[derive(Debug)]
@ -32,7 +43,7 @@ impl From<MechanismNameError> for JabberError {
}
impl From<Utf8Error> for JabberError {
fn from(e: Utf8Error) -> Self {
fn from(_e: Utf8Error) -> Self {
Self::Utf8Decode
}
}
@ -42,3 +53,21 @@ impl From<quick_xml::Error> for JabberError {
Self::XML(e)
}
}
impl From<element::ElementError<'static>> for JabberError {
fn from(e: element::ElementError<'static>) -> Self {
Self::Element(e)
}
}
impl From<AttrError> for JabberError {
fn from(e: AttrError) -> Self {
Self::XML(e.into())
}
}
impl From<ParseError> for JabberError {
fn from(e: ParseError) -> Self {
Self::JID(e)
}
}

View File

@ -117,11 +117,12 @@ impl<'j> Jabber<'j> {
.connect(&self.server, socket)
.await
{
let (read, writer) = tokio::io::split(stream);
let (read, write) = tokio::io::split(stream);
let reader = Reader::from_reader(BufReader::new(read));
return Ok(JabberClientType::Encrypted(
client::encrypted::JabberClient::new(reader, writer, self),
));
let writer = Writer::new(write);
let mut client = client::encrypted::JabberClient::new(reader, writer, self);
client.start_stream().await?;
return Ok(JabberClientType::Encrypted(client));
}
}
false => {
@ -129,9 +130,10 @@ impl<'j> Jabber<'j> {
let (read, write) = tokio::io::split(stream);
let reader = Reader::from_reader(BufReader::new(read));
let writer = Writer::new(write);
return Ok(JabberClientType::Unencrypted(
client::unencrypted::JabberClient::new(reader, writer, self),
));
let mut client =
client::unencrypted::JabberClient::new(reader, writer, self);
client.start_stream().await?;
return Ok(JabberClientType::Unencrypted(client));
}
}
}

View File

@ -1,6 +1,6 @@
use std::str::FromStr;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Debug, Clone)]
pub struct JID {
// TODO: validate localpart (length, char]
pub localpart: Option<String>,

View File

@ -2,6 +2,7 @@
// TODO: logging (dropped errors)
pub mod client;
pub mod element;
pub mod error;
pub mod jabber;
pub mod jid;

View File

@ -1,2 +1,6 @@
// use quick_xml::events::BytesDecl;
pub mod sasl;
pub mod stream;
// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None);

View File

@ -1,32 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, PartialEq, Debug)]
pub struct Mechanisms {
#[serde(rename = "$value")]
pub mechanisms: Vec<Mechanism>,
}
#[derive(Deserialize, PartialEq, Debug)]
pub struct Mechanism {
#[serde(rename = "$text")]
pub mechanism: String,
}
#[derive(Serialize, Debug)]
#[serde(rename = "auth")]
pub struct Auth {
#[serde(rename = "@xmlns")]
pub ns: String,
#[serde(rename = "@mechanism")]
pub mechanism: String,
#[serde(rename = "$text")]
pub sasl_data: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct Challenge {
#[serde(rename = "@xmlns")]
pub ns: String,
#[serde(rename = "$text")]
pub sasl_data: String,
}

View File

@ -1,41 +1,182 @@
use serde::{Deserialize, Serialize};
use std::str;
use super::sasl::Mechanisms;
use quick_xml::{
events::{BytesStart, Event},
name::QName,
};
#[derive(Serialize, Deserialize)]
#[serde(rename = "stream:stream")]
struct Stream {
#[serde(rename = "@from")]
from: Option<String>,
#[serde(rename = "@id")]
use crate::{element::Element, JabberError, Result, JID};
const XMLNS_STREAM: &str = "http://etherx.jabber.org/streams";
const VERSION: &str = "1.0";
enum XMLNS {
Client,
Server,
}
impl From<XMLNS> for &str {
fn from(xmlns: XMLNS) -> Self {
match xmlns {
XMLNS::Client => return "jabber:client",
XMLNS::Server => return "jabber:server",
}
}
}
impl TryInto<XMLNS> for &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>,
#[serde(rename = "@to")]
to: Option<String>,
#[serde(rename = "@version")]
version: Option<f32>,
#[serde(rename = "@xml:lang")]
to: Option<JID>,
version: Option<String>,
lang: Option<String>,
#[serde(rename = "@xmlns")]
namespace: Option<String>,
#[serde(rename = "@xmlns:stream")]
stream_namespace: Option<String>,
_ns: XMLNS,
}
#[derive(Deserialize, Debug)]
#[serde(rename = "stream:features")]
pub struct StreamFeatures {
#[serde(rename = "$value")]
pub features: Vec<StreamFeature>,
impl Stream {
pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self {
Self {
from: Some(from.clone()),
id,
to: Some(to.clone()),
version: Some(VERSION.to_owned()),
lang,
_ns: XMLNS::Client,
}
}
fn build(&self) -> BytesStart {
let mut start = BytesStart::new("stream:stream");
if let Some(from) = &self.from {
start.push_attribute(("from", from.to_string().as_str()));
}
if let Some(id) = &self.id {
start.push_attribute(("id", id.as_str()));
}
if let Some(to) = &self.to {
start.push_attribute(("to", to.to_string().as_str()));
}
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()));
}
start.push_attribute(("xmlns", XMLNS::Client.into()));
start.push_attribute(("xmlns:stream", XMLNS_STREAM));
start
}
}
#[derive(Deserialize, PartialEq, Debug)]
impl<'e> Into<Element<'e>> for Stream {
fn into(self) -> Element<'e> {
Element {
event: Event::Start(self.build().to_owned()),
content: 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: ns,
})
} else {
Err(JabberError::ParseError)
}
}
}
#[derive(PartialEq, Debug)]
pub enum StreamFeature {
#[serde(rename = "starttls")]
StartTls,
// TODO: other stream features
#[serde(rename = "mechanisms")]
Sasl(Mechanisms),
Sasl(Vec<String>),
Bind,
#[serde(other)]
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(content) = features_element.content {
for feature_element in content {
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(content) = feature_element.content {
for mechanism_element in content {
if let Some(content) = mechanism_element.content {
for mechanism_text in content {
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),
}
}
}
Ok(features)
}
}