diff options
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) } } |