summaryrefslogblamecommitdiffstats
path: root/src/stanza/mod.rs
blob: 1bb3fc2f31af86e8b01deab116d48cf5010b3807 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                    

             
             
               
 
                               
             
 
                                                                        
                                     
                                                                
                                       


                                          
                                 
 



                                                
                    

                                    
                           

                                    
                      


























                                                                                             
                      












                                                                                               


                                                                                                      
                          
                                              
                                                                     













                                                           

 
                       


                     
            

 













                                         


                                                                  
                              



         
              





                                                             
                
                           
         
     
 


                                     
                       
     
 




                                                           
     
 
                                                                            




                                                   
                       
     

 

                                            









                                                                                              
             





























                                                                                               
                 
             
         


     
              
                                                                                   
                                          

                                     

                                                      





                                                
                                                  







                                                    
                                                     
                                  

                                    
                                              


                       
                            
                                                     

                

     





















                                                                                       
         

            

     


                                                                                            

                               
                                                  
                     










                                                                                                                                                                                                 
 















                                                                                                
                 
               
 


                                                                                                   
 
                                                                 
 



                                                           




                                                         



                                                           


                      
                                                                     
                               
                                                  

                                                                 
                   



                                                              


                                                                 

                                                                           
                               


                                                  


                                                                  







                                                                                        
 
                                                                    








                                                                                            



                                                                                             



                                                                                      



                                                                                                  


                             





                                                                                     


                     


                                                            


                                              
                         


                              



                                                                             
             
                                                        
         
     
 
 


                                                                           
                               

                                                  


                                                                  
                                







                                                                                        
 
                                                                    








                                                                                            



                                                                                             



                                                                                      



                                                                                                  


                             





                                                                                     


                     


                                                            
 
                                              




                                               


                                                                             

                    
                                







                                                                                        
 
                                                                    








                                                                                            



                                                                                             



                                                                                      



                                                                                                  


                             





                                                                                     

                     
 


                                                            

                                              

                                                                                                   
                                             
                 
 

                                              

                                               

                              


                                                                             
                    

                                      
                                                                                       
                                         

         









                                                                        

 

                                                                                                            
 


                           
            
               
                          
                     
 

                
                            


                       
// use quick_xml::events::BytesDecl;

pub mod bind;
pub mod iq;
pub mod sasl;
pub mod stream;

use std::collections::BTreeMap;
use std::str;

// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None);
use async_recursion::async_recursion;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::name::PrefixDeclaration;
use quick_xml::{Reader, Writer};
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 {
    /// element prefix
    /// e.g. `foo` in `<foo:bar />`.
    prefix: Option<String>,
    /// element name
    /// e.g. `bar` in `<foo:bar />`.
    localname: String,
    /// qualifying namespace
    /// an element must be qualified by a namespace
    /// e.g. for `<stream:features>` in
    /// ```
    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <stream:features>
    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///         <compression xmlns='http://jabber.org/features/compress'>
    ///             <method>zlib</method>
    ///             <method>lzw</method>
    ///         </compression>
    ///     </stream:features>
    /// </stream:stream>
    /// ```
    /// would be `"http://etherx.jabber.org/streams"` but for
    /// ```
    /// <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <features>
    ///         <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///         <compression xmlns='http://jabber.org/features/compress'>
    ///             <method>zlib</method>
    ///             <method>lzw</method>
    ///         </compression>
    ///     </features>
    /// </stream:stream>
    /// ```
    /// would be `"jabber:client"`
    namespace: String,
    /// all namespaces applied to element
    /// e.g. for `<bind>` in
    /// ```
    /// <stream:features xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
    ///     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
    ///     <compression xmlns='http://jabber.org/features/compress'>
    ///         <method>zlib</method>
    ///         <method>lzw</method>
    ///     </compression>
    /// </stream:features>
    /// ```
    /// would be `[(None, "urn:ietf:params:xml:ns:xmpp-bind")]` despite
    /// `(Some("stream"), "http://etherx.jabber.org/streams")` also being available
    // TODO: maybe not even needed, as can calculate when writing which namespaces need to be declared
    // but then can't have unused namespace on element, confusing.
    namespace_declarations: Box<BTreeMap<Option<String>, String>>,
    /// element attributes
    attributes: Box<BTreeMap<String, String>>,
    // children elements namespaces contain their parents' namespaces
    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),
    Text(String),
    Unknown,
}

impl From<Element> for Node {
    fn from(element: Element) -> Self {
        Self::Element(element)
    }
}

impl<S: ToString> From<S> for Node {
    fn from(text: S) -> Self {
        Self::Text(text.to_string())
    }
}

impl<'s> From<&Node> for Vec<Event<'s>> {
    fn from(node: &Node) -> Self {
        match node {
            Node::Element(e) => e.into(),
            Node::Text(t) => vec![Event::Text(BytesText::new(t))],
            Unknown => vec![],
        }
    }
}

impl Element {
    /// returns the fully qualified name
    /// e.g. `foo:bar` in
    /// `<foo:bar>`.
    pub fn name(&self) -> &str {
        if let Some(prefix) = self.prefix {
            format!("{}:{}", prefix, self.localname).as_str()
        } else {
            &self.localname
        }
    }

    /// returns the localname.
    /// e.g. `bar` in `<foo:bar>`
    pub fn localname(&self) -> &str {
        &self.localname
    }

    /// returns the prefix.
    /// e.g. `foo` in `<foo:bar>`. returns None if there is
    /// no prefix.
    pub fn prefix(&self) -> Option<&str> {
        self.prefix
    }

    /// returns the namespace which applies to the current element, e.g. for
    /// `<element xmlns='foo' xmlns:bar='bar'>`
    /// it will be `foo` but for
    /// `<bar:element xmlns='foo' xmlns:bar='bar'>`
    /// it will be `bar`.
    pub fn namespace(&self) -> &str {
        &self.namespace
    }
}

impl<'s> From<&Element> for Vec<Event<'s>> {
    fn from(element: &Element) -> Self {
        let name = element.name();

        let event = BytesStart::new(name);

        // namespace declarations
        let namespace_declarations = element.namespace_declarations.iter().map(|declaration| {
            let (prefix, namespace) = declaration;
            match prefix {
                Some(prefix) => return (format!("xmlns:{}", prefix).as_str(), *namespace),
                None => return ("xmlns", *namespace),
            }
        });
        let event = event.with_attributes(namespace_declarations);

        // attributes
        let event = event.with_attributes(element.attributes.into_iter());

        match element.children.is_empty() {
            true => return vec![Event::Empty(event)],
            false => {
                return {
                    let start: Vec<Event<'s>> = vec![Event::Start(event)];
                    let start_and_content: Vec<Event<'s>> = start
                        .into_iter()
                        .chain({
                            let u = element.children.iter().fold(
                                Vec::new(),
                                |acc: Vec<Event<'s>>, child: &Node<'s>| {
                                    acc.into_iter()
                                        .chain(Into::<Vec<Event<'s>>>::into(child).into_iter())
                                        .collect()
                                },
                            );
                            u
                        })
                        .collect();
                    let full: Vec<Event<'s>> = start_and_content
                        .into_iter()
                        .chain(vec![Event::End(BytesEnd::new(name))])
                        .collect();
                    full
                }
            }
        }
    }
}

impl Element {
    /// if there is only one child in the vec of children, will return that element
    pub fn child(&self) -> Result<&Node> {
        if self.children.len() == 1 {
            Ok(&self.children[0])
        } else if self.children.len() > 1 {
            Err(ElementError::MultipleChildren.into())
        } else {
            Err(ElementError::NoChildren.into())
        }
    }

    /// returns reference to children
    pub fn children(&self) -> Result<&Vec<Node>> {
        if !self.children.is_empty() {
            Ok(&self.children)
        } else {
            Err(ElementError::NoChildren.into())
        }
    }

    /// returns text content, error if there is none
    pub fn text_content(&self) -> Result<Vec<&str>> {
        let mut text = Vec::new();
        for node in *self.children {
            match node {
                Node::Text(t) => text.push(t),
                _ => {}
            }
        }
        if text.is_empty() {
            return Err(ElementError::NotText.into());
        }
        Ok(text)
    }

    /// returns whether or not the element is qualified by a namespace, either declared
    /// by a parent, or itself.
    fn namespace_qualified<S: AsRef<str>>(
        &self,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> bool {
        if let Some(namespace) = local_namespaces.get(self.prefix) {
            if namespace != self.namespace {
                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 {
                return false;
            }
        }

        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>(
        &self,
        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
        if self.namespace_qualified(local_namespaces) {
            let events: Vec<Event> = self.into();
            for event in events {
                writer.write_event_async(event).await?
            }
            Ok(())
        } else {
            Err(ElementError::NamespaceNotQualified.into())
        }
    }

    pub async fn write_start<S: AsRef<str>, W: AsyncWrite + Unpin + Send>(
        &self,
        writer: &mut Writer<W>,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> Result<()> {
        if self.namespace_qualified(local_namespaces) {
            let mut event = BytesStart::new(self.name());

            // namespace declarations
            self.namespace_declarations.iter().for_each(|declaration| {
                let (prefix, namespace) = declaration;
                match prefix {
                    Some(prefix) => {
                        event.push_attribute((format!("xmlns:{}", prefix).as_str(), *namespace))
                    }
                    None => event.push_attribute(("xmlns", *namespace)),
                }
            });

            // attributes
            let event =
                event.with_attributes(self.attributes.iter().map(|(attr, value)| (*attr, *value)));

            writer.write_event_async(Event::Start(event)).await?;

            Ok(())
        } else {
            Err(ElementError::NamespaceNotQualified.into())
        }
    }

    pub async fn write_end<W: AsyncWrite + Unpin + Send>(
        &self,
        writer: &mut Writer<W>,
    ) -> Result<()> {
        let event = BytesEnd::new(self.name());
        writer.write_event_async(Event::End(event)).await?;
        Ok(())
    }

    #[async_recursion]
    pub async fn read<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> Result<Self> {
        let node = Node::read_recursive(reader, local_namespaces)
            .await?
            .ok_or(JabberError::UnexpectedEnd)?;
        match node {
            Node::Element(e) => Ok(e),
            Node::Text(_) => Err(JabberError::UnexpectedText),
            Node::Unknown => Err(JabberError::UnexpectedElement),
        }
    }

    pub async fn read_start<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> Result<Element> {
        let buf = Vec::new();
        let event = reader.read_event_into_async(&mut buf).await?;
        match event {
            Event::Start(e) => {
                let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
                let converted_prefix;
                if let Some(raw_prefix) = prefix {
                    converted_prefix = Some(str::from_utf8(raw_prefix)?)
                }
                let prefix = converted_prefix;

                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();

                let mut local_namespaces = local_namespaces.clone();
                let mut namespace_declarations = BTreeMap::new();
                let attributes = BTreeMap::new();

                for attribute in e.attributes() {
                    let attribute = attribute?;
                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
                        match prefix_declaration {
                            PrefixDeclaration::Default => {
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(None, value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(None, value);
                            }
                            PrefixDeclaration::Named(prefix) => {
                                let key = str::from_utf8(prefix)?;
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(Some(key), value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(Some(key), value);
                            }
                        }
                    } else {
                        if let Some(_) = attributes.insert(
                            str::from_utf8(attribute.key.into_inner())?,
                            str::from_utf8(attribute.value.as_ref())?,
                        ) {
                            return Err(ElementParseError::DuplicateAttribute.into());
                        };
                    }
                }

                let namespace = *local_namespaces
                    .get(&prefix)
                    .ok_or(ElementParseError::NoNamespace)?;

                let mut children = Vec::new();

                Ok(Self {
                    prefix,
                    localname,
                    namespace,
                    namespace_declarations: Box::new(namespace_declarations),
                    attributes: Box::new(attributes),
                    children: Box::new(children),
                })
            }
            e => Err(ElementError::NotAStart(e).into()),
        }
    }
}

impl Node {
    #[async_recursion]
    async fn read_recursive<S: AsRef<str>, R: AsyncBufRead + Unpin + Send>(
        reader: &mut Reader<R>,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> Result<Option<Node>> {
        let mut buf = Vec::new();
        let event = reader.read_event_into_async(&mut buf).await?;
        match event {
            Event::Empty(e) => {
                let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
                let converted_prefix;
                if let Some(raw_prefix) = prefix {
                    converted_prefix = Some(str::from_utf8(raw_prefix)?)
                }
                let prefix = converted_prefix;

                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();

                let mut local_namespaces = local_namespaces.clone();
                let mut namespace_declarations = BTreeMap::new();
                let attributes = BTreeMap::new();

                for attribute in e.attributes() {
                    let attribute = attribute?;
                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
                        match prefix_declaration {
                            PrefixDeclaration::Default => {
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(None, value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(None, value);
                            }
                            PrefixDeclaration::Named(prefix) => {
                                let key = str::from_utf8(prefix)?;
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(Some(key), value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(Some(key), value);
                            }
                        }
                    } else {
                        if let Some(_) = attributes.insert(
                            str::from_utf8(attribute.key.into_inner())?,
                            str::from_utf8(attribute.value.as_ref())?,
                        ) {
                            return Err(ElementParseError::DuplicateAttribute.into());
                        };
                    }
                }

                let namespace = *local_namespaces
                    .get(&prefix)
                    .ok_or(ElementParseError::NoNamespace)?;

                let mut children = Vec::new();

                Ok(Some(Self::Element(Element {
                    prefix,
                    localname,
                    namespace,
                    namespace_declarations: Box::new(namespace_declarations),
                    attributes: Box::new(attributes),
                    children: Box::new(children),
                })))
            }
            Event::Start(e) => {
                let prefix = e.name().prefix().map(|prefix| prefix.into_inner());
                let converted_prefix;
                if let Some(raw_prefix) = prefix {
                    converted_prefix = Some(str::from_utf8(raw_prefix)?)
                }
                let prefix = converted_prefix;

                let localname = str::from_utf8(e.local_name().into_inner())?.to_owned();

                let mut local_namespaces = local_namespaces.clone();
                let mut namespace_declarations = BTreeMap::new();
                let attributes = BTreeMap::new();

                for attribute in e.attributes() {
                    let attribute = attribute?;
                    if let Some(prefix_declaration) = attribute.key.as_namespace_binding() {
                        match prefix_declaration {
                            PrefixDeclaration::Default => {
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(None, value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(None, value);
                            }
                            PrefixDeclaration::Named(prefix) => {
                                let key = str::from_utf8(prefix)?;
                                let value = str::from_utf8(attribute.value.as_ref())?;
                                if let Some(_) = namespace_declarations.insert(Some(key), value) {
                                    return Err(ElementParseError::DuplicateAttribute.into());
                                };
                                local_namespaces.insert(Some(key), value);
                            }
                        }
                    } else {
                        if let Some(_) = attributes.insert(
                            str::from_utf8(attribute.key.into_inner())?,
                            str::from_utf8(attribute.value.as_ref())?,
                        ) {
                            return Err(ElementParseError::DuplicateAttribute.into());
                        };
                    }
                }

                let namespace = *local_namespaces
                    .get(&prefix)
                    .ok_or(ElementParseError::NoNamespace)?;

                let mut children = Vec::new();
                while let Some(child_node) = Node::read_recursive(reader, &local_namespaces).await?
                {
                    children.push(child_node)
                }

                let mut children = Vec::new();

                Ok(Some(Self::Element(Element {
                    prefix,
                    localname,
                    namespace,
                    namespace_declarations: Box::new(namespace_declarations),
                    attributes: Box::new(attributes),
                    children: Box::new(children),
                })))
            }
            Event::End(_) => Ok(None),
            Event::Text(e) => Ok(Some(Self::Text(e.unescape()?.as_ref().to_string()))),
            e => Ok(Some(Self::Unknown)),
        }
    }

    fn namespace_qualified<S: AsRef<str>>(
        &self,
        local_namespaces: &BTreeMap<Option<S>, S>,
    ) -> bool {
        match self {
            Self::Element(e) => e.namespace_qualified(local_namespaces),
            _ => 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

#[derive(Debug)]
pub enum ElementError<'e> {
    NotAStart(Event<'e>),
    NotText,
    NoChildren,
    NamespaceNotQualified,
    MultipleChildren,
}

#[derive(Debug)]
pub enum ElementParseError {
    DuplicateAttribute,
    NoNamespace,
}