diff options
author | cel 🌸 <cel@blos.sm> | 2023-10-20 04:51:56 +0100 |
---|---|---|
committer | cel 🌸 <cel@blos.sm> | 2023-10-20 04:51:56 +0100 |
commit | ba94ee66fafbabd63d6d1ed5edf435d4c46c6796 (patch) | |
tree | fe1bebc35914941b5c4fbd6f0286f4c9f8916154 /src/stanza/stream.rs | |
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/stream.rs')
-rw-r--r-- | src/stanza/stream.rs | 226 |
1 files changed, 48 insertions, 178 deletions
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) } } |