summaryrefslogtreecommitdiffstats
path: root/src/stanza/stream.rs
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@blos.sm>2023-10-20 04:51:56 +0100
committerLibravatar cel 🌸 <cel@blos.sm>2023-10-20 04:51:56 +0100
commitba94ee66fafbabd63d6d1ed5edf435d4c46c6796 (patch)
treefe1bebc35914941b5c4fbd6f0286f4c9f8916154 /src/stanza/stream.rs
parent2536fa4937f0283b4187142cc6cede8e1dbfafa8 (diff)
downloadluz-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.rs226
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)
}
}