use std::collections::{HashMap, HashSet}; use peanuts::element::{Content, ElementBuilder, FromElement, IntoElement, NamespaceDeclaration}; use peanuts::XML_NS; use peanuts::{element::Name, Element}; use crate::{Error, JID}; 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 { features: Vec, } impl IntoElement for Features { fn builder(&self) -> ElementBuilder { Element::builder("features", Some(XMLNS)).push_children(self.features.clone()) // let mut content = Vec::new(); // for feature in &self.features { // match feature { // Feature::StartTls(start_tls) => { // content.push(Content::Element(start_tls.into_element())) // } // Feature::Sasl => {} // Feature::Bind => {} // Feature::Unknown => {} // } // } // Element { // name: Name { // namespace: Some(XMLNS.to_string()), // local_name: "features".to_string(), // }, // namespace_declaration_overrides: HashSet::new(), // attributes: HashMap::new(), // content, // } } } impl FromElement for Features { fn from_element( mut element: Element, ) -> std::result::Result { element.check_namespace(XMLNS)?; element.check_name("features")?; let features = element.children()?; Ok(Features { features }) } } #[derive(Debug, Clone)] pub enum Feature { StartTls(StartTls), Sasl, Bind, Unknown, } impl IntoElement for Feature { fn builder(&self) -> ElementBuilder { match self { Feature::StartTls(start_tls) => start_tls.builder(), Feature::Sasl => todo!(), Feature::Bind => todo!(), Feature::Unknown => todo!(), } } } impl FromElement for Feature { fn from_element(element: Element) -> peanuts::element::DeserializeResult { match element.identify() { (Some(starttls::XMLNS), "starttls") => { Ok(Feature::StartTls(StartTls::from_element(element)?)) } _ => Ok(Feature::Unknown), } } }