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-trait = "0.1.68"
 | 
			
		||||
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
 | 
			
		||||
rsasl = { version = "2", default_features = true, features = ["provider_base64", "plain", "config_builder"] }
 | 
			
		||||
serde = "1.0.180"
 | 
			
		||||
tokio = { version = "1.28", features = ["full"] }
 | 
			
		||||
tokio-native-tls = "0.3.1"
 | 
			
		||||
tracing = "0.1.40"
 | 
			
		||||
trust-dns-resolver = "0.22.0"
 | 
			
		||||
try_map = "0.3.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,36 +2,26 @@ use std::{collections::BTreeMap, str};
 | 
			
		|||
 | 
			
		||||
use quick_xml::{
 | 
			
		||||
    events::{BytesDecl, Event},
 | 
			
		||||
    Reader, Writer,
 | 
			
		||||
    NsReader, Writer,
 | 
			
		||||
};
 | 
			
		||||
use rsasl::prelude::{Mechname, SASLClient};
 | 
			
		||||
use tokio::io::{BufReader, ReadHalf, WriteHalf};
 | 
			
		||||
use tokio::net::TcpStream;
 | 
			
		||||
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::JabberError;
 | 
			
		||||
use crate::Result;
 | 
			
		||||
 | 
			
		||||
pub struct JabberClient<'j> {
 | 
			
		||||
    pub reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
 | 
			
		||||
    pub reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
 | 
			
		||||
    pub writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
 | 
			
		||||
    jabber: &'j mut Jabber<'j>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'j> JabberClient<'j> {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
 | 
			
		||||
        reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
 | 
			
		||||
        writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
 | 
			
		||||
        jabber: &'j mut Jabber<'j>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,22 +1,24 @@
 | 
			
		|||
pub mod encrypted;
 | 
			
		||||
// pub mod encrypted;
 | 
			
		||||
pub mod unencrypted;
 | 
			
		||||
 | 
			
		||||
// use async_trait::async_trait;
 | 
			
		||||
 | 
			
		||||
use crate::stanza::stream::StreamFeature;
 | 
			
		||||
// use crate::stanza::stream::StreamFeature;
 | 
			
		||||
use crate::JabberError;
 | 
			
		||||
use crate::Result;
 | 
			
		||||
 | 
			
		||||
pub enum JabberClientType<'j> {
 | 
			
		||||
    Encrypted(encrypted::JabberClient<'j>),
 | 
			
		||||
    // Encrypted(encrypted::JabberClient<'j>),
 | 
			
		||||
    Unencrypted(unencrypted::JabberClient<'j>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'j> JabberClientType<'j> {
 | 
			
		||||
    /// ensures an encrypted jabber client
 | 
			
		||||
    pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Encrypted(c) => Ok(c),
 | 
			
		||||
            Self::Unencrypted(mut c) => {
 | 
			
		||||
                c.start_stream().await?;
 | 
			
		||||
                let features = c.get_features().await?;
 | 
			
		||||
                if features.contains(&StreamFeature::StartTls) {
 | 
			
		||||
                    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]
 | 
			
		||||
// pub trait JabberTrait {
 | 
			
		||||
//     async fn start_stream(&mut self) -> Result<()>;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,27 +1,30 @@
 | 
			
		|||
use std::str;
 | 
			
		||||
 | 
			
		||||
use quick_xml::{
 | 
			
		||||
    events::{BytesDecl, BytesStart, Event},
 | 
			
		||||
    events::{BytesStart, Event},
 | 
			
		||||
    name::QName,
 | 
			
		||||
    Reader, Writer,
 | 
			
		||||
    se, NsReader, Writer,
 | 
			
		||||
};
 | 
			
		||||
use tokio::io::{BufReader, ReadHalf, WriteHalf};
 | 
			
		||||
use tokio::net::TcpStream;
 | 
			
		||||
use tokio_native_tls::native_tls::TlsConnector;
 | 
			
		||||
use try_map::FallibleMapExt;
 | 
			
		||||
 | 
			
		||||
use crate::stanza::stream::StreamFeature;
 | 
			
		||||
use crate::stanza::Element;
 | 
			
		||||
use crate::error::JabberError;
 | 
			
		||||
use crate::stanza::stream::Stream;
 | 
			
		||||
use crate::stanza::DECLARATION;
 | 
			
		||||
use crate::Jabber;
 | 
			
		||||
use crate::Result;
 | 
			
		||||
use crate::{error::JabberError, stanza::stream::Stream};
 | 
			
		||||
 | 
			
		||||
pub struct JabberClient<'j> {
 | 
			
		||||
    reader: Reader<BufReader<ReadHalf<TcpStream>>>,
 | 
			
		||||
    reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
 | 
			
		||||
    writer: Writer<WriteHalf<TcpStream>>,
 | 
			
		||||
    jabber: &'j mut Jabber<'j>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'j> JabberClient<'j> {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        reader: Reader<BufReader<ReadHalf<TcpStream>>>,
 | 
			
		||||
        reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
 | 
			
		||||
        writer: Writer<WriteHalf<TcpStream>>,
 | 
			
		||||
        jabber: &'j mut Jabber<'j>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -34,60 +37,144 @@ impl<'j> JabberClient<'j> {
 | 
			
		|||
 | 
			
		||||
    pub async fn start_stream(&mut self) -> Result<()> {
 | 
			
		||||
        // 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 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?;
 | 
			
		||||
        let stream_element = Stream::new_client(None, server, None, "en");
 | 
			
		||||
        se::to_writer_with_root(&mut self.writer, "stream:stream", &stream_element);
 | 
			
		||||
 | 
			
		||||
        // 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(())
 | 
			
		||||
 | 
			
		||||
        // may or may not send a declaration
 | 
			
		||||
        let buf = Vec::new();
 | 
			
		||||
        let mut first_event = self.reader.read_resolved_event_into_async(&mut buf).await?;
 | 
			
		||||
        match first_event {
 | 
			
		||||
            (quick_xml::name::ResolveResult::Unbound, Event::Decl(e)) => {
 | 
			
		||||
                if let Ok(version) = e.version() {
 | 
			
		||||
                    if version.as_ref() == b"1.0" {
 | 
			
		||||
                        first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // todo: error
 | 
			
		||||
                        todo!()
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => (),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
 | 
			
		||||
        Element::read(&mut self.reader).await?.try_into()
 | 
			
		||||
        // 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!(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    }
 | 
			
		||||
    // 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::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use quick_xml::{Reader, Writer};
 | 
			
		||||
use quick_xml::{NsReader, Writer};
 | 
			
		||||
use rsasl::prelude::SASLConfig;
 | 
			
		||||
use tokio::io::BufReader;
 | 
			
		||||
use tokio::net::TcpStream;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ pub struct 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 auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?;
 | 
			
		||||
        println!("auth: {:?}", auth);
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ impl<'j> Jabber<'j> {
 | 
			
		|||
 | 
			
		||||
    pub async fn login(&'j mut self) -> Result<JabberClient<'j>> {
 | 
			
		||||
        let mut client = self.connect().await?.ensure_tls().await?;
 | 
			
		||||
        println!("negotiation");
 | 
			
		||||
        client.start_stream().await?;
 | 
			
		||||
        client.negotiate().await?;
 | 
			
		||||
        Ok(client)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +106,7 @@ impl<'j> Jabber<'j> {
 | 
			
		|||
        socket_addrs
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// establishes a connection to the server
 | 
			
		||||
    pub async fn connect(&'j mut self) -> Result<JabberClientType> {
 | 
			
		||||
        for (socket_addr, is_tls) in self.get_sockets().await {
 | 
			
		||||
            println!("trying {}", socket_addr);
 | 
			
		||||
| 
						 | 
				
			
			@ -118,21 +119,18 @@ impl<'j> Jabber<'j> {
 | 
			
		|||
                        .await
 | 
			
		||||
                    {
 | 
			
		||||
                        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 mut client = client::encrypted::JabberClient::new(reader, writer, self);
 | 
			
		||||
                        client.start_stream().await?;
 | 
			
		||||
                        let client = client::encrypted::JabberClient::new(reader, writer, self);
 | 
			
		||||
                        return Ok(JabberClientType::Encrypted(client));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                false => {
 | 
			
		||||
                    if let Ok(stream) = TcpStream::connect(socket_addr).await {
 | 
			
		||||
                        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 mut client =
 | 
			
		||||
                            client::unencrypted::JabberClient::new(reader, writer, self);
 | 
			
		||||
                        client.start_stream().await?;
 | 
			
		||||
                        let client = client::unencrypted::JabberClient::new(reader, writer, self);
 | 
			
		||||
                        return Ok(JabberClientType::Unencrypted(client));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Debug, Clone)]
 | 
			
		||||
pub struct JID {
 | 
			
		||||
    // TODO: validate localpart (length, char]
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +10,15 @@ pub struct JID {
 | 
			
		|||
    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 {
 | 
			
		||||
    NoResourcePart,
 | 
			
		||||
    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 {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
							
								
								
									
										25
									
								
								src/lib.rs
								
								
								
								
							
							
						
						
									
										25
									
								
								src/lib.rs
								
								
								
								
							| 
						 | 
				
			
			@ -8,7 +8,7 @@ pub mod jabber;
 | 
			
		|||
pub mod jid;
 | 
			
		||||
pub mod stanza;
 | 
			
		||||
 | 
			
		||||
pub use client::encrypted::JabberClient;
 | 
			
		||||
// pub use client::encrypted::JabberClient;
 | 
			
		||||
pub use error::JabberError;
 | 
			
		||||
pub use jabber::Jabber;
 | 
			
		||||
pub use jid::JID;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,30 +22,9 @@ mod tests {
 | 
			
		|||
    use crate::Jabber;
 | 
			
		||||
    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]
 | 
			
		||||
    async fn login() {
 | 
			
		||||
        Jabber::new(
 | 
			
		||||
        Jabber::user(
 | 
			
		||||
            JID::from_str("test@blos.sm/clown").unwrap(),
 | 
			
		||||
            "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 iq;
 | 
			
		||||
pub mod message;
 | 
			
		||||
pub mod presence;
 | 
			
		||||
pub mod sasl;
 | 
			
		||||
pub mod starttls;
 | 
			
		||||
pub mod stream;
 | 
			
		||||
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
use std::str;
 | 
			
		||||
use quick_xml::events::{BytesDecl, Event};
 | 
			
		||||
 | 
			
		||||
// const DECLARATION: BytesDecl<'_> = 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,
 | 
			
		||||
}
 | 
			
		||||
pub static DECLARATION: Event = Event::Decl(BytesDecl::new("1.0", None, None));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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::{
 | 
			
		||||
    events::{BytesStart, Event},
 | 
			
		||||
    name::QName,
 | 
			
		||||
};
 | 
			
		||||
use crate::JID;
 | 
			
		||||
 | 
			
		||||
use super::Element;
 | 
			
		||||
use crate::{JabberError, Result, JID};
 | 
			
		||||
pub static XMLNS: &str = "http://etherx.jabber.org/streams";
 | 
			
		||||
pub static XMLNS_CLIENT: &str = "jabber:client";
 | 
			
		||||
 | 
			
		||||
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>,
 | 
			
		||||
    to: Option<JID>,
 | 
			
		||||
    version: Option<String>,
 | 
			
		||||
    lang: Option<String>,
 | 
			
		||||
    ns: XMLNS,
 | 
			
		||||
// MUST be qualified by stream namespace
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct Stream<'s> {
 | 
			
		||||
    #[serde(rename = "@from")]
 | 
			
		||||
    from: Option<&'s JID>,
 | 
			
		||||
    #[serde(rename = "@to")]
 | 
			
		||||
    to: Option<&'s JID>,
 | 
			
		||||
    #[serde(rename = "@id")]
 | 
			
		||||
    id: Option<&'s str>,
 | 
			
		||||
    #[serde(rename = "@version")]
 | 
			
		||||
    version: Option<&'s str>,
 | 
			
		||||
    // TODO: lang enum
 | 
			
		||||
    #[serde(rename = "@lang")]
 | 
			
		||||
    lang: Option<&'s str>,
 | 
			
		||||
    #[serde(rename = "@xmlns")]
 | 
			
		||||
    xmlns: &'s str,
 | 
			
		||||
    #[serde(rename = "@xmlns:stream")]
 | 
			
		||||
    xmlns_stream: &'s str,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
            from: Some(from.clone()),
 | 
			
		||||
            id,
 | 
			
		||||
            to: Some(to.clone()),
 | 
			
		||||
            version: Some(VERSION.to_owned()),
 | 
			
		||||
            lang,
 | 
			
		||||
            ns: XMLNS::Client,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn event(&self) -> Event<'static> {
 | 
			
		||||
        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()));
 | 
			
		||||
        }
 | 
			
		||||
        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,
 | 
			
		||||
            id,
 | 
			
		||||
            version,
 | 
			
		||||
            lang,
 | 
			
		||||
                ns,
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(JabberError::ParseError)
 | 
			
		||||
            xmlns: XMLNS_CLIENT,
 | 
			
		||||
            xmlns_stream: XMLNS,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute.
 | 
			
		||||
    /// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection.
 | 
			
		||||
    pub fn new_client(from: Option<&JID>, to: &JID, id: Option<&str>, lang: &str) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            from,
 | 
			
		||||
            to: Some(to),
 | 
			
		||||
            id,
 | 
			
		||||
            version: Some("1.0"),
 | 
			
		||||
            lang: Some(lang),
 | 
			
		||||
            xmlns: XMLNS_CLIENT,
 | 
			
		||||
            xmlns_stream: XMLNS,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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