diff options
| author | 2023-10-20 04:51:56 +0100 | |
|---|---|---|
| committer | 2023-10-20 04:51:56 +0100 | |
| commit | ba94ee66fafbabd63d6d1ed5edf435d4c46c6796 (patch) | |
| tree | fe1bebc35914941b5c4fbd6f0286f4c9f8916154 /src/stanza | |
| parent | 2536fa4937f0283b4187142cc6cede8e1dbfafa8 (diff) | |
| download | luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.tar.gz luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.tar.bz2 luz-ba94ee66fafbabd63d6d1ed5edf435d4c46c6796.zip | |
WIP: refactor to parse incoming stream as state machine
Diffstat (limited to 'src/stanza')
| -rw-r--r-- | src/stanza/bind.rs | 47 | ||||
| -rw-r--r-- | src/stanza/iq.rs | 169 | ||||
| -rw-r--r-- | src/stanza/message.rs | 1 | ||||
| -rw-r--r-- | src/stanza/mod.rs | 656 | ||||
| -rw-r--r-- | src/stanza/presence.rs | 1 | ||||
| -rw-r--r-- | src/stanza/sasl.rs | 144 | ||||
| -rw-r--r-- | src/stanza/starttls.rs | 1 | ||||
| -rw-r--r-- | src/stanza/stream.rs | 226 | 
8 files changed, 56 insertions, 1189 deletions
| diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs index 939a716..8b13789 100644 --- a/src/stanza/bind.rs +++ b/src/stanza/bind.rs @@ -1,48 +1 @@ -use super::{Element, ElementParseError}; -use crate::{JabberError, JID}; -const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind"; - -pub struct Bind { -    pub resource: Option<String>, -    pub jid: Option<JID>, -} - -impl From<Bind> for Element { -    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) -        } -        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) -        } -        bind_element -    } -} - -impl TryFrom<Element> for Bind { -    type Error = JabberError; - -    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)?, -                    )), -                } -            } -        } -    } -} diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs index 6c7dee3..8b13789 100644 --- a/src/stanza/iq.rs +++ b/src/stanza/iq.rs @@ -1,170 +1 @@ -use nanoid::nanoid; -use quick_xml::{ -    events::{BytesStart, Event}, -    name::QName, -    Reader, Writer, -}; -use crate::{JabberClient, JabberError, JID}; - -use crate::Result; - -#[derive(Debug)] -pub struct IQ { -    to: Option<JID>, -    from: Option<JID>, -    id: String, -    r#type: IQType, -    lang: Option<String>, -    child: Element<'static>, -} - -#[derive(Debug)] -enum IQType { -    Get, -    Set, -    Result, -    Error, -} - -impl IQ { -    pub async fn set<'j, R: IntoElement<'static>>( -        client: &mut JabberClient<'j>, -        to: Option<JID>, -        from: Option<JID>, -        element: R, -    ) -> Result<Element<'static>> { -        let id = nanoid!(); -        let iq = IQ { -            to, -            from, -            id: id.clone(), -            r#type: IQType::Set, -            lang: None, -            child: Element::from(element), -        }; -        println!("{:?}", iq); -        let iq = Element::from(iq); -        println!("{:?}", iq); -        iq.write(&mut client.writer).await?; -        let result = Element::read(&mut client.reader).await?; -        let iq = IQ::try_from(result)?; -        if iq.id == id { -            return Ok(iq.child); -        } -        Err(JabberError::IDMismatch) -    } -} - -impl<'e> IntoElement<'e> for IQ { -    fn event(&self) -> quick_xml::events::Event<'e> { -        let mut start = BytesStart::new("iq"); -        if let Some(to) = &self.to { -            start.push_attribute(("to", to.to_string().as_str())); -        } -        if let Some(from) = &self.from { -            start.push_attribute(("from", from.to_string().as_str())); -        } -        start.push_attribute(("id", self.id.as_str())); -        match self.r#type { -            IQType::Get => start.push_attribute(("type", "get")), -            IQType::Set => start.push_attribute(("type", "set")), -            IQType::Result => start.push_attribute(("type", "result")), -            IQType::Error => start.push_attribute(("type", "error")), -        } -        if let Some(lang) = &self.lang { -            start.push_attribute(("from", lang.to_string().as_str())); -        } - -        quick_xml::events::Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        Some(vec![self.child.clone()]) -    } -} - -impl TryFrom<Element<'static>> for IQ { -    type Error = JabberError; - -    fn try_from(element: Element<'static>) -> std::result::Result<Self, Self::Error> { -        if let Event::Start(start) = &element.event { -            if start.name() == QName(b"iq") { -                let mut to: Option<JID> = None; -                let mut from: Option<JID> = None; -                let mut id = None; -                let mut r#type = None; -                let mut lang = None; -                start -                    .attributes() -                    .into_iter() -                    .try_for_each(|attribute| -> Result<()> { -                        if let Ok(attribute) = attribute { -                            let buf: Vec<u8> = Vec::new(); -                            let reader = Reader::from_reader(buf); -                            match attribute.key { -                                QName(b"to") => { -                                    to = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned() -                                            .try_into()?, -                                    ) -                                } -                                QName(b"from") => { -                                    from = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned() -                                            .try_into()?, -                                    ) -                                } -                                QName(b"id") => { -                                    id = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned(), -                                    ) -                                } -                                QName(b"type") => { -                                    let value = attribute -                                        .decode_and_unescape_value(&reader) -                                        .or(Err(JabberError::Utf8Decode))?; -                                    match value.as_ref() { -                                        "get" => r#type = Some(IQType::Get), -                                        "set" => r#type = Some(IQType::Set), -                                        "result" => r#type = Some(IQType::Result), -                                        "error" => r#type = Some(IQType::Error), -                                        _ => return Err(JabberError::ParseError), -                                    } -                                } -                                QName(b"lang") => { -                                    lang = Some( -                                        attribute -                                            .decode_and_unescape_value(&reader) -                                            .or(Err(JabberError::Utf8Decode))? -                                            .into_owned(), -                                    ) -                                } -                                _ => return Err(JabberError::UnknownAttribute), -                            } -                        } -                        Ok(()) -                    })?; -                let iq = IQ { -                    to, -                    from, -                    id: id.ok_or(JabberError::NoID)?, -                    r#type: r#type.ok_or(JabberError::NoType)?, -                    lang, -                    child: element.child()?.to_owned(), -                }; -                return Ok(iq); -            } -        } -        Err(JabberError::ParseError) -    } -} diff --git a/src/stanza/message.rs b/src/stanza/message.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/message.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 13fc31e..c5a6da3 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -1,657 +1,11 @@ -// use quick_xml::events::BytesDecl; -  pub mod bind;  pub mod iq; +pub mod message; +pub mod presence;  pub mod sasl; +pub mod starttls;  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}; - -#[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>>, -} - -#[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, -        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; -        }; - -        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: 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, -        namespace_context: &BTreeMap<Option<S>, S>, -    ) -> bool { -        match self { -            Self::Element(e) => e.namespace_qualified(namespace_context), -            _ => true, -        } -    } -} - -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> { -    NotAStart(Event<'e>), -    NotText, -    NoChildren, -    NamespaceNotQualified, -    MultipleChildren, -} +use quick_xml::events::{BytesDecl, Event}; -#[derive(Debug)] -pub enum ElementParseError { -    DuplicateAttribute, -    NoNamespace, -} +pub static DECLARATION: Event = Event::Decl(BytesDecl::new("1.0", None, None)); diff --git a/src/stanza/presence.rs b/src/stanza/presence.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/presence.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs index 20cd063..8b13789 100644 --- a/src/stanza/sasl.rs +++ b/src/stanza/sasl.rs @@ -1,145 +1 @@ -use quick_xml::{ -    events::{BytesStart, BytesText, Event}, -    name::QName, -}; -use crate::error::SASLError; -use crate::JabberError; - -use super::Element; - -const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl"; - -#[derive(Debug)] -pub struct Auth<'e> { -    pub mechanism: &'e str, -    pub sasl_data: &'e str, -} - -impl<'e> IntoElement<'e> for Auth<'e> { -    fn event(&self) -> Event<'e> { -        let mut start = BytesStart::new("auth"); -        start.push_attribute(("xmlns", XMLNS)); -        start.push_attribute(("mechanism", self.mechanism)); -        Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        let sasl = BytesText::from_escaped(self.sasl_data); -        let sasl = Element { -            event: Event::Text(sasl), -            children: None, -        }; -        Some(vec![sasl]) -    } -} - -#[derive(Debug)] -pub struct Challenge { -    pub sasl_data: Vec<u8>, -} - -impl<'e> TryFrom<&Element<'e>> for Challenge { -    type Error = JabberError; - -    fn try_from(element: &Element<'e>) -> Result<Challenge, Self::Error> { -        if let Event::Start(start) = &element.event { -            if start.name() == QName(b"challenge") { -                let sasl_data: &Element<'_> = element.child()?; -                if let Event::Text(sasl_data) = &sasl_data.event { -                    let s = sasl_data.clone(); -                    let s = s.into_inner(); -                    let s = s.to_vec(); -                    return Ok(Challenge { sasl_data: s }); -                } -            } -        } -        Err(SASLError::NoChallenge.into()) -    } -} - -// impl<'e> TryFrom<Element<'e>> for Challenge { -//     type Error = JabberError; - -//     fn try_from(element: Element<'e>) -> Result<Challenge, Self::Error> { -//         if let Event::Start(start) = &element.event { -//             if start.name() == QName(b"challenge") { -//                 println!("one"); -//                 if let Some(children) = element.children.as_deref() { -//                     if children.len() == 1 { -//                         let sasl_data = children.first().unwrap(); -//                         if let Event::Text(sasl_data) = &sasl_data.event { -//                             return Ok(Challenge { -//                                 sasl_data: sasl_data.clone().into_inner().to_vec(), -//                             }); -//                         } else { -//                             return Err(SASLError::NoChallenge.into()); -//                         } -//                     } else { -//                         return Err(SASLError::NoChallenge.into()); -//                     } -//                 } else { -//                     return Err(SASLError::NoChallenge.into()); -//                 } -//             } -//         } -//         Err(SASLError::NoChallenge.into()) -//     } -// } - -#[derive(Debug)] -pub struct Response<'e> { -    pub sasl_data: &'e str, -} - -impl<'e> IntoElement<'e> for Response<'e> { -    fn event(&self) -> Event<'e> { -        let mut start = BytesStart::new("response"); -        start.push_attribute(("xmlns", XMLNS)); -        Event::Start(start) -    } - -    fn children(&self) -> Option<Vec<Element<'e>>> { -        let sasl = BytesText::from_escaped(self.sasl_data); -        let sasl = Element { -            event: Event::Text(sasl), -            children: None, -        }; -        Some(vec![sasl]) -    } -} - -#[derive(Debug)] -pub struct Success { -    pub sasl_data: Option<Vec<u8>>, -} - -impl<'e> TryFrom<&Element<'e>> for Success { -    type Error = JabberError; - -    fn try_from(element: &Element<'e>) -> Result<Success, Self::Error> { -        match &element.event { -            Event::Start(start) => { -                if start.name() == QName(b"success") { -                    match element.child() { -                        Ok(sasl_data) => { -                            if let Event::Text(sasl_data) = &sasl_data.event { -                                return Ok(Success { -                                    sasl_data: Some(sasl_data.clone().into_inner().to_vec()), -                                }); -                            } -                        } -                        Err(_) => return Ok(Success { sasl_data: None }), -                    }; -                } -            } -            Event::Empty(empty) => { -                if empty.name() == QName(b"success") { -                    return Ok(Success { sasl_data: None }); -                } -            } -            _ => {} -        } -        Err(SASLError::NoSuccess.into()) -    } -} diff --git a/src/stanza/starttls.rs b/src/stanza/starttls.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/stanza/starttls.rs @@ -0,0 +1 @@ + diff --git a/src/stanza/stream.rs b/src/stanza/stream.rs index f85166f..07f7e6e 100644 --- a/src/stanza/stream.rs +++ b/src/stanza/stream.rs @@ -1,190 +1,60 @@ -use std::str; - -use quick_xml::{ -    events::{BytesStart, Event}, -    name::QName, -}; - -use super::Element; -use crate::{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, +use serde::Serialize; + +use crate::JID; + +pub static XMLNS: &str = "http://etherx.jabber.org/streams"; +pub static XMLNS_CLIENT: &str = "jabber:client"; + +// MUST be qualified by stream namespace +#[derive(Serialize)] +pub struct Stream<'s> { +    #[serde(rename = "@from")] +    from: Option<&'s JID>, +    #[serde(rename = "@to")] +    to: Option<&'s JID>, +    #[serde(rename = "@id")] +    id: Option<&'s str>, +    #[serde(rename = "@version")] +    version: Option<&'s str>, +    // TODO: lang enum +    #[serde(rename = "@lang")] +    lang: Option<&'s str>, +    #[serde(rename = "@xmlns")] +    xmlns: &'s str, +    #[serde(rename = "@xmlns:stream")] +    xmlns_stream: &'s str,  }  impl Stream { -    pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self { +    pub fn new( +        from: Option<&JID>, +        to: Option<&JID>, +        id: Option<&str>, +        version: Option<&str>, +        lang: Option<&str>, +    ) -> Self {          Self { -            from: Some(from.clone()), +            from, +            to,              id, -            to: Some(to.clone()), -            version: Some(VERSION.to_owned()), +            version,              lang, -            ns: XMLNS::Client, -        } -    } - -    fn event(&self) -> Event<'static> { -        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())); -        } -        match &self.ns { -            XMLNS::Client => start.push_attribute(("xmlns", XMLNS::Client.into())), -            XMLNS::Server => start.push_attribute(("xmlns", XMLNS::Server.into())), -        } -        start.push_attribute(("xmlns:stream", XMLNS_STREAM)); -        Event::Start(start) -    } -} - -impl<'e> Into<Element<'e>> for Stream { -    fn into(self) -> Element<'e> { -        Element { -            event: self.event(), -            children: None, +            xmlns: XMLNS_CLIENT, +            xmlns_stream: XMLNS,          }      } -} - -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, -            }) -        } 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(children) = features_element.children { -            for feature_element in children { -                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(children) = feature_element.children { -                                for mechanism_element in children { -                                    if let Some(children) = mechanism_element.children { -                                        for mechanism_text in children { -                                            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), -                    }, -                    Event::Empty(e) => match e.name() { -                        QName(b"bind") => features.push(StreamFeature::Bind), -                        _ => features.push(StreamFeature::Unknown), -                    }, -                    _ => features.push(StreamFeature::Unknown), -                } -            } +    /// 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<&JID>, to: &JID, id: Option<&str>, lang: &str) -> Self { +        Self { +            from, +            to: Some(to), +            id, +            version: Some("1.0"), +            lang: Some(lang), +            xmlns: XMLNS_CLIENT, +            xmlns_stream: XMLNS,          } -        Ok(features)      }  } | 
