From 2536fa4937f0283b4187142cc6cede8e1dbfafa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?cel=20=F0=9F=8C=B8?= Date: Fri, 20 Oct 2023 02:34:47 +0100 Subject: [PATCH] WIP: mess --- src/client/encrypted.rs | 7 +- src/stanza/bind.rs | 140 ++++++++-------------------------------- src/stanza/iq.rs | 1 - src/stanza/mod.rs | 132 +++++++++++++++++++++++++++++-------- src/stanza/sasl.rs | 1 - 5 files changed, 134 insertions(+), 147 deletions(-) diff --git a/src/client/encrypted.rs b/src/client/encrypted.rs index 03ae295..47b2b2c 100644 --- a/src/client/encrypted.rs +++ b/src/client/encrypted.rs @@ -1,8 +1,7 @@ -use std::str; +use std::{collections::BTreeMap, str}; use quick_xml::{ events::{BytesDecl, Event}, - name::QName, Reader, Writer, }; use rsasl::prelude::{Mechname, SASLClient}; @@ -53,7 +52,9 @@ impl<'j> JabberClient<'j> { .write_event_async(Event::Decl(declaration)) .await; let stream_element: Element<'_> = stream_element.into(); - stream_element.write_start(&mut self.writer).await?; + stream_element + .write_start(&mut self.writer, &BTreeMap::new()) + .await?; // server to client let mut buf = Vec::new(); self.reader.read_event_into_async(&mut buf).await?; diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs index 5e9704f..939a716 100644 --- a/src/stanza/bind.rs +++ b/src/stanza/bind.rs @@ -1,12 +1,4 @@ -use std::collections::BTreeMap; - -use quick_xml::{ - events::{BytesStart, BytesText, Event}, - name::QName, - Reader, -}; - -use super::{Element, IntoElement, Node}; +use super::{Element, ElementParseError}; use crate::{JabberError, JID}; const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind"; @@ -17,120 +9,40 @@ pub struct Bind { } impl From for Element { - fn from(value: Bind) -> Self { - let mut namespace_declarations = Box::new(BTreeMap::new()); - namespace_declarations.insert(None, XMLNS.to_owned()); - let mut children = Vec::new(); - if let Some(resource) = value.resource { - children.push(Node::Element( - Element { prefix: None, localname: "", namespace: , namespace_declarations: , attributes: , children: } - ) - - ) + 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) } - Self { - prefix: None, - localname: "bind".to_string(), - namespace: XMLNS.to_owned(), - namespace_declarations, - attributes: todo!(), - children: todo!(), + 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 IntoElement for Bind { - fn event(&self) -> quick_xml::events::Event<'static> { - let mut bind_event = BytesStart::new("bind"); - bind_event.push_attribute(("xmlns", XMLNS)); - if self.resource.is_none() && self.jid.is_none() { - return Event::Empty(bind_event); - } else { - return Event::Start(bind_event); - } - } - - fn children(&self) -> Option>> { - if let Some(resource) = &self.resource { - let resource_event: BytesStart<'static> = BytesStart::new("resource"); - let resource_child: BytesText<'static> = BytesText::new(resource).into_owned(); - let resource_child: Element<'static> = Element { - event: Event::Text(resource_child), - children: None, - }; - let resource_element: Element<'static> = Element { - event: Event::Start(resource_event), - children: Some(vec![resource_child]), - }; - return Some(vec![resource_element]); - } else if let Some(jid) = &self.jid { - let jid_event = BytesStart::new("jid"); - let jid_child = BytesText::new(&jid.to_string()).into_owned(); - let jid_child = Element { - event: Event::Text(jid_child), - children: None, - }; - let jid_element = Element { - event: Event::Start(jid_event), - children: Some(vec![jid_child]), - }; - return Some(vec![jid_element]); - } - None - } -} - -impl TryFrom> for Bind { +impl TryFrom for Bind { type Error = JabberError; - fn try_from(element: Element<'static>) -> Result { - if let Event::Start(start) = &element.event { - let buf: Vec = Vec::new(); - let reader = Reader::from_reader(buf); - if start.name() == QName(b"bind") - && start.try_get_attribute("xmlns")?.is_some_and(|attribute| { - attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS - }) - { - let child: Element<'static> = element.child()?.clone(); - if let Event::Start(start) = &child.event { - match start.name() { - QName(b"resource") => { - let resource_text = child.child()?; - if let Event::Text(text) = &resource_text.event { - return Ok(Self { - resource: Some(text.unescape()?.into_owned()), - jid: None, - }); - } - } - QName(b"jid") => { - let jid_text = child.child()?; - if let Event::Text(text) = &jid_text.event { - return Ok(Self { - jid: Some(text.unescape()?.into_owned().try_into()?), - resource: None, - }); - } - } - _ => return Err(JabberError::UnexpectedElement), - } + fn try_from(element: Element) -> Result { + if element.namespace() == XMLNS && element.localname() == "bind" { + let (resource, jid); + let child: &Element = element.child()?; + if child.namespace() == XMLNS { + match child.localname() { + "resource" => Bind::new(Some( + child + .text_content()? + .first() + .ok_or(ElementParseError::NoContent)?, + )), } } - } else if let Event::Empty(start) = &element.event { - let buf: Vec = Vec::new(); - let reader = Reader::from_reader(buf); - if start.name() == QName(b"bind") - && start.try_get_attribute("xmlns")?.is_some_and(|attribute| { - attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS - }) - { - return Ok(Bind { - resource: None, - jid: None, - }); - } } - Err(JabberError::UnexpectedElement) } } diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs index 8a373b2..6c7dee3 100644 --- a/src/stanza/iq.rs +++ b/src/stanza/iq.rs @@ -7,7 +7,6 @@ use quick_xml::{ use crate::{JabberClient, JabberError, JID}; -use super::{Element, IntoElement}; use crate::Result; #[derive(Debug)] diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 1bb3fc2..13fc31e 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -17,8 +17,6 @@ use tokio::io::{AsyncBufRead, AsyncWrite}; use crate::{JabberError, Result}; -pub type Prefix<'s> = Option<&'s str>; - #[derive(Clone, Debug)] /// represents an xml element as a tree of nodes pub struct Element { @@ -78,19 +76,6 @@ pub struct Element { children: Box>, } -impl Element { - pub fn new_empty>( - prefix: Option, - localname: S, - namespace: S, - namespace_declarations: BTreeMap, S>, - attributes: BTreeMap, - children: Vec, - ) { - } - pub fn push_child>(&mut self, node: C) {} -} - #[derive(Clone, Debug)] pub enum Node { Element(Element), @@ -244,22 +229,22 @@ impl Element { /// by a parent, or itself. fn namespace_qualified>( &self, - local_namespaces: &BTreeMap, S>, + namespace_context: &BTreeMap, S>, ) -> bool { + // create a new local_namespaces combining that in the context and those declared within the element + let mut local_namespaces = *namespace_context.clone(); + self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration)); + if let Some(namespace) = local_namespaces.get(self.prefix) { if namespace != self.namespace { return false; } + } else { + return false; }; - if let Some(namespace) = self.namespace_declarations.get(&self.prefix) { - if namespace != self.namespace { - return false; - } - } - for child in *self.children { - if child.namespace_qualified(local_namespaces) == false { + if child.namespace_qualified(&local_namespaces) == false { return false; } } @@ -267,6 +252,7 @@ impl Element { true } + /// writes an element to a writer. the element's namespace must be qualified by the /// context given in `local_namespaces` or the element's internal namespace declarations pub async fn write, W: AsyncWrite + Unpin + Send>( @@ -274,7 +260,7 @@ impl Element { writer: &mut Writer, local_namespaces: &BTreeMap, S>, ) -> Result<()> { - // TODO: NEXT: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s + // TODO: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s if self.namespace_qualified(local_namespaces) { let events: Vec = self.into(); for event in events { @@ -553,17 +539,107 @@ impl Node { fn namespace_qualified>( &self, - local_namespaces: &BTreeMap, S>, + namespace_context: &BTreeMap, S>, ) -> bool { match self { - Self::Element(e) => e.namespace_qualified(local_namespaces), + Self::Element(e) => e.namespace_qualified(namespace_context), _ => true, } } } -// the issue is i don't know how to validate that an element always has a namespace when it is being written -// TODO: ElementBuilder that makes it easier to build an element under a namespace +pub enum NodeBuilder { + Text(String), + Element(ElementBuilder), +} + +pub struct ElementBuilder { + localname: String, + prefix: Option, + namespace: String, + namespace_declarations: BTreeMap, String>, + attributes: BTreeMap, + children: Vec, +} + +impl ElementBuilder { + pub fn new(localname: S, prefix: Option, 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( + &mut self, + (prefix, namespace): (Option, S), + ) -> Option { + self.namespace_declarations.insert(prefix, namespace) + } + + pub fn push_attribute(&mut self, (key, value): (S, S)) -> Option { + 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>( + &self + ) -> bool { + self.namespace_conflict_recursive(&BTreeMap::new()) + } + + fn namespace_conflict_recursive>( + &self, + parent_namespaces: &BTreeMap, 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 { + for child in self.children { + match child { + Node::Element(e) => { + if !e.namespace_conflict() + } + } + } + Element { + prefix: self.prefix, + localname: self.localname, + namespace: self.namespace, + namespace_declarations: self.namespace_declarations, + attributes: self.attributes, + children: self.children, + } + } +} #[derive(Debug)] pub enum ElementError<'e> { diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs index 50ffd83..20cd063 100644 --- a/src/stanza/sasl.rs +++ b/src/stanza/sasl.rs @@ -7,7 +7,6 @@ use crate::error::SASLError; use crate::JabberError; use super::Element; -use super::IntoElement; const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl";