diff options
Diffstat (limited to 'src/stanza')
| -rw-r--r-- | src/stanza/bind.rs | 140 | ||||
| -rw-r--r-- | src/stanza/iq.rs | 1 | ||||
| -rw-r--r-- | src/stanza/mod.rs | 132 | ||||
| -rw-r--r-- | src/stanza/sasl.rs | 1 | 
4 files changed, 130 insertions, 144 deletions
| diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs index 5e9704f..939a716 100644 --- a/src/stanza/bind.rs +++ b/src/stanza/bind.rs @@ -1,12 +1,4 @@ -use std::collections::BTreeMap; - -use quick_xml::{ -    events::{BytesStart, BytesText, Event}, -    name::QName, -    Reader, -}; - -use super::{Element, IntoElement, Node}; +use super::{Element, ElementParseError};  use crate::{JabberError, JID};  const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind"; @@ -17,120 +9,40 @@ pub struct Bind {  }  impl From<Bind> for Element { -    fn from(value: Bind) -> Self { -        let mut namespace_declarations = Box::new(BTreeMap::new()); -        namespace_declarations.insert(None, XMLNS.to_owned()); -        let mut children = Vec::new(); -        if let Some(resource) = value.resource { -            children.push(Node::Element( -                Element { prefix: None, localname: "", namespace: , namespace_declarations: , attributes: , children:  } -            )  -                 -            ) -        } -        Self { -            prefix: None, -            localname: "bind".to_string(), -            namespace: XMLNS.to_owned(), -            namespace_declarations, -            attributes: todo!(), -            children: todo!(), -        } -    } -} - -impl IntoElement for Bind { -    fn event(&self) -> quick_xml::events::Event<'static> { -        let mut bind_event = BytesStart::new("bind"); -        bind_event.push_attribute(("xmlns", XMLNS)); -        if self.resource.is_none() && self.jid.is_none() { -            return Event::Empty(bind_event); -        } else { -            return Event::Start(bind_event); +    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)          } -    } - -    fn children(&self) -> Option<Vec<Element<'static>>> { -        if let Some(resource) = &self.resource { -            let resource_event: BytesStart<'static> = BytesStart::new("resource"); -            let resource_child: BytesText<'static> = BytesText::new(resource).into_owned(); -            let resource_child: Element<'static> = Element { -                event: Event::Text(resource_child), -                children: None, -            }; -            let resource_element: Element<'static> = Element { -                event: Event::Start(resource_event), -                children: Some(vec![resource_child]), -            }; -            return Some(vec![resource_element]); -        } else if let Some(jid) = &self.jid { -            let jid_event = BytesStart::new("jid"); -            let jid_child = BytesText::new(&jid.to_string()).into_owned(); -            let jid_child = Element { -                event: Event::Text(jid_child), -                children: None, -            }; -            let jid_element = Element { -                event: Event::Start(jid_event), -                children: Some(vec![jid_child]), -            }; -            return Some(vec![jid_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)          } -        None +        bind_element      }  } -impl TryFrom<Element<'static>> for Bind { +impl TryFrom<Element> for Bind {      type Error = JabberError; -    fn try_from(element: Element<'static>) -> Result<Self, Self::Error> { -        if let Event::Start(start) = &element.event { -            let buf: Vec<u8> = Vec::new(); -            let reader = Reader::from_reader(buf); -            if start.name() == QName(b"bind") -                && start.try_get_attribute("xmlns")?.is_some_and(|attribute| { -                    attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS -                }) -            { -                let child: Element<'static> = element.child()?.clone(); -                if let Event::Start(start) = &child.event { -                    match start.name() { -                        QName(b"resource") => { -                            let resource_text = child.child()?; -                            if let Event::Text(text) = &resource_text.event { -                                return Ok(Self { -                                    resource: Some(text.unescape()?.into_owned()), -                                    jid: None, -                                }); -                            } -                        } -                        QName(b"jid") => { -                            let jid_text = child.child()?; -                            if let Event::Text(text) = &jid_text.event { -                                return Ok(Self { -                                    jid: Some(text.unescape()?.into_owned().try_into()?), -                                    resource: None, -                                }); -                            } -                        } -                        _ => return Err(JabberError::UnexpectedElement), -                    } +    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)?, +                    )),                  }              } -        } else if let Event::Empty(start) = &element.event { -            let buf: Vec<u8> = Vec::new(); -            let reader = Reader::from_reader(buf); -            if start.name() == QName(b"bind") -                && start.try_get_attribute("xmlns")?.is_some_and(|attribute| { -                    attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS -                }) -            { -                return Ok(Bind { -                    resource: None, -                    jid: None, -                }); -            }          } -        Err(JabberError::UnexpectedElement)      }  } diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs index 8a373b2..6c7dee3 100644 --- a/src/stanza/iq.rs +++ b/src/stanza/iq.rs @@ -7,7 +7,6 @@ use quick_xml::{  use crate::{JabberClient, JabberError, JID}; -use super::{Element, IntoElement};  use crate::Result;  #[derive(Debug)] diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 1bb3fc2..13fc31e 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -17,8 +17,6 @@ use tokio::io::{AsyncBufRead, AsyncWrite};  use crate::{JabberError, Result}; -pub type Prefix<'s> = Option<&'s str>; -  #[derive(Clone, Debug)]  /// represents an xml element as a tree of nodes  pub struct Element { @@ -78,19 +76,6 @@ pub struct Element {      children: Box<Vec<Node>>,  } -impl Element { -    pub fn new_empty<S: ToString, C: Into<Node>>( -        prefix: Option<S>, -        localname: S, -        namespace: S, -        namespace_declarations: BTreeMap<Option<S>, S>, -        attributes: BTreeMap<S, S>, -        children: Vec<C>, -    ) { -    } -    pub fn push_child<C: Into<Node>>(&mut self, node: C) {} -} -  #[derive(Clone, Debug)]  pub enum Node {      Element(Element), @@ -244,22 +229,22 @@ impl Element {      /// by a parent, or itself.      fn namespace_qualified<S: AsRef<str>>(          &self, -        local_namespaces: &BTreeMap<Option<S>, S>, +        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;          }; -        if let Some(namespace) = self.namespace_declarations.get(&self.prefix) { -            if namespace != self.namespace { -                return false; -            } -        } -          for child in *self.children { -            if child.namespace_qualified(local_namespaces) == false { +            if child.namespace_qualified(&local_namespaces) == false {                  return false;              }          } @@ -267,6 +252,7 @@ impl Element {          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>( @@ -274,7 +260,7 @@ impl Element {          writer: &mut Writer<W>,          local_namespaces: &BTreeMap<Option<S>, S>,      ) -> Result<()> { -        // TODO: NEXT: 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 +        // 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 { @@ -553,17 +539,107 @@ impl Node {      fn namespace_qualified<S: AsRef<str>>(          &self, -        local_namespaces: &BTreeMap<Option<S>, S>, +        namespace_context: &BTreeMap<Option<S>, S>,      ) -> bool {          match self { -            Self::Element(e) => e.namespace_qualified(local_namespaces), +            Self::Element(e) => e.namespace_qualified(namespace_context),              _ => true,          }      }  } -// the issue is i don't know how to validate that an element always has a namespace when it is being written -// TODO: ElementBuilder that makes it easier to build an element under a namespace +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> { diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs index 50ffd83..20cd063 100644 --- a/src/stanza/sasl.rs +++ b/src/stanza/sasl.rs @@ -7,7 +7,6 @@ use crate::error::SASLError;  use crate::JabberError;  use super::Element; -use super::IntoElement;  const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; | 
