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<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,
}
impl Stream {
pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self {
Self {
from: Some(from.clone()),
id,
to: Some(to.clone()),
version: Some(VERSION.to_owned()),
lang,
_ns: XMLNS::Client,
}
}
fn build(&self) -> BytesStart {
let mut start = BytesStart::new("stream:stream");
if let Some(from) = &self.from {
start.push_attribute(("from", from.to_string().as_str()));
}
if let Some(id) = &self.id {
start.push_attribute(("id", id.as_str()));
}
if let Some(to) = &self.to {
start.push_attribute(("to", to.to_string().as_str()));
}
if let Some(version) = &self.version {
start.push_attribute(("version", version.to_string().as_str()));
}
if let Some(lang) = &self.lang {
start.push_attribute(("xml:lang", lang.as_str()));
}
start.push_attribute(("xmlns", XMLNS::Client.into()));
start.push_attribute(("xmlns:stream", XMLNS_STREAM));
start
}
}
impl<'e> Into<Element<'e>> for Stream {
fn into(self) -> Element<'e> {
Element {
event: Event::Start(self.build().to_owned()),
content: None,
}
}
}
impl<'e> TryFrom<Element<'e>> for Stream {
type Error = JabberError;
fn try_from(value: Element<'e>) -> Result<Stream> {
let (mut from, mut id, mut to, mut version, mut lang, mut ns) =
(None, None, None, None, None, XMLNS::Client);
if let Event::Start(e) = value.event.as_ref() {
for attribute in e.attributes() {
let attribute = attribute?;
match attribute.key {
QName(b"from") => {
from = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
}
QName(b"id") => {
id = Some(str::from_utf8(&attribute.value)?.to_owned());
}
QName(b"to") => {
to = Some(str::from_utf8(&attribute.value)?.to_string().try_into()?);
}
QName(b"version") => {
version = Some(str::from_utf8(&attribute.value)?.to_owned());
}
QName(b"lang") => {
lang = Some(str::from_utf8(&attribute.value)?.to_owned());
}
QName(b"xmlns") => {
ns = str::from_utf8(&attribute.value)?.try_into()?;
}
_ => {
println!("unknown attribute: {:?}", attribute)
}
}
}
Ok(Stream {
from,
id,
to,
version,
lang,
_ns: ns,
})
} else {
Err(JabberError::ParseError)
}
}
}
#[derive(PartialEq, Debug)]
pub enum StreamFeature {
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(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)
}
}