use std::str; use quick_xml::{ events::{BytesStart, Event}, name::QName, }; 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 for &str { fn from(xmlns: XMLNS) -> Self { match xmlns { XMLNS::Client => return "jabber:client", XMLNS::Server => return "jabber:server", } } } impl TryInto for &str { type Error = JabberError; fn try_into(self) -> Result { match self { "jabber:client" => Ok(XMLNS::Client), "jabber:server" => Ok(XMLNS::Server), _ => Err(JabberError::UnknownNamespace), } } } pub struct Stream { from: Option, id: Option, to: Option, version: Option, lang: Option, _ns: XMLNS, } impl Stream { pub fn new_client(from: &JID, to: &JID, id: Option, lang: Option) -> 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 } } impl<'e> Into> for Stream { fn into(self) -> Element<'e> { Element { event: Event::Start(self.build().to_owned()), content: None, } } } impl<'e> TryFrom> for Stream { type Error = JabberError; fn try_from(value: Element<'e>) -> Result { 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 { StartTls, Sasl(Vec), Bind, Unknown, } impl<'e> TryFrom> for Vec { type Error = JabberError; fn try_from(features_element: Element) -> Result { 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) } }