use std::collections::{HashMap, HashSet}; use peanuts::element::{Content, ElementBuilder, FromElement, IntoElement, NamespaceDeclaration}; use peanuts::XML_NS; use peanuts::{element::Name, Element}; use tracing::debug; use crate::{Error, JID}; use super::sasl::{self, Mechanisms}; use super::starttls::{self, StartTls}; pub const XMLNS: &str = "http://etherx.jabber.org/streams"; pub const XMLNS_CLIENT: &str = "jabber:client"; // MUST be qualified by stream namespace // #[derive(XmlSerialize, XmlDeserialize)] // #[peanuts(xmlns = XMLNS)] #[derive(Debug)] pub struct Stream { pub from: Option, to: Option, id: Option, version: Option, // TODO: lang enum lang: Option, // #[peanuts(content)] // content: Message, } impl FromElement for Stream { fn from_element(mut element: Element) -> std::result::Result { element.check_namespace(XMLNS)?; element.check_name("stream")?; let from = element.attribute_opt("from")?; let to = element.attribute_opt("to")?; let id = element.attribute_opt("id")?; let version = element.attribute_opt("version")?; let lang = element.attribute_opt_namespaced("lang", peanuts::XML_NS)?; Ok(Stream { from, to, id, version, lang, }) } } impl IntoElement for Stream { fn builder(&self) -> ElementBuilder { Element::builder("stream", Some(XMLNS.to_string())) .push_namespace_declaration_override(Some("stream"), XMLNS) .push_namespace_declaration_override(None::<&str>, XMLNS_CLIENT) .push_attribute_opt("to", self.to.clone()) .push_attribute_opt("from", self.from.clone()) .push_attribute_opt("id", self.id.clone()) .push_attribute_opt("version", self.version.clone()) .push_attribute_opt_namespaced(peanuts::XML_NS, "to", self.lang.clone()) } } impl<'s> Stream { pub fn new( from: Option, to: Option, id: Option, version: Option, lang: Option, ) -> Self { Self { from, to, id, version, lang, } } /// 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, to: JID, id: Option, lang: String) -> Self { Self { from, to: Some(to), id, version: Some("1.0".to_string()), lang: Some(lang), } } } #[derive(Debug)] pub struct Features { pub features: Vec, } impl IntoElement for Features { fn builder(&self) -> ElementBuilder { Element::builder("features", Some(XMLNS)).push_children(self.features.clone()) } } impl FromElement for Features { fn from_element( mut element: Element, ) -> std::result::Result { element.check_namespace(XMLNS)?; element.check_name("features")?; debug!("got features stanza"); let features = element.children()?; debug!("got features period"); Ok(Features { features }) } } #[derive(Debug, Clone)] pub enum Feature { StartTls(StartTls), Sasl(Mechanisms), Bind, Unknown, } impl IntoElement for Feature { fn builder(&self) -> ElementBuilder { match self { Feature::StartTls(start_tls) => start_tls.builder(), Feature::Sasl(mechanisms) => mechanisms.builder(), Feature::Bind => todo!(), Feature::Unknown => todo!(), } } } impl FromElement for Feature { fn from_element(element: Element) -> peanuts::element::DeserializeResult { let identity = element.identify(); debug!("identity: {:?}", identity); match element.identify() { (Some(starttls::XMLNS), "starttls") => { debug!("identified starttls"); Ok(Feature::StartTls(StartTls::from_element(element)?)) } (Some(sasl::XMLNS), "mechanisms") => { debug!("identified mechanisms"); Ok(Feature::Sasl(Mechanisms::from_element(element)?)) } _ => { debug!("identified unknown feature"); Ok(Feature::Unknown) } } } }