diff options
Diffstat (limited to 'src/stanza')
-rw-r--r-- | src/stanza/mod.rs | 4 | ||||
-rw-r--r-- | src/stanza/sasl.rs | 24 | ||||
-rw-r--r-- | src/stanza/stream.rs | 197 |
3 files changed, 173 insertions, 52 deletions
diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 4eaa4c2..02ea277 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -1,2 +1,6 @@ +// use quick_xml::events::BytesDecl; + pub mod sasl; pub mod stream; + +// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None); diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs index c0e41ab..1f77ffa 100644 --- a/src/stanza/sasl.rs +++ b/src/stanza/sasl.rs @@ -1,32 +1,8 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, PartialEq, Debug)] -pub struct Mechanisms { - #[serde(rename = "$value")] - pub mechanisms: Vec<Mechanism>, -} - -#[derive(Deserialize, PartialEq, Debug)] -pub struct Mechanism { - #[serde(rename = "$text")] - pub mechanism: String, -} - -#[derive(Serialize, Debug)] -#[serde(rename = "auth")] pub struct Auth { - #[serde(rename = "@xmlns")] - pub ns: String, - #[serde(rename = "@mechanism")] pub mechanism: String, - #[serde(rename = "$text")] pub sasl_data: Option<String>, } -#[derive(Deserialize, Debug)] pub struct Challenge { - #[serde(rename = "@xmlns")] - pub ns: String, - #[serde(rename = "$text")] pub sasl_data: String, } diff --git a/src/stanza/stream.rs b/src/stanza/stream.rs index 4c0addd..f0fb6a1 100644 --- a/src/stanza/stream.rs +++ b/src/stanza/stream.rs @@ -1,41 +1,182 @@ -use serde::{Deserialize, Serialize}; +use std::str; -use super::sasl::Mechanisms; +use quick_xml::{ + events::{BytesStart, Event}, + name::QName, +}; -#[derive(Serialize, Deserialize)] -#[serde(rename = "stream:stream")] -struct Stream { - #[serde(rename = "@from")] - from: Option<String>, - #[serde(rename = "@id")] +use crate::{element::Element, 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>, - #[serde(rename = "@to")] - to: Option<String>, - #[serde(rename = "@version")] - version: Option<f32>, - #[serde(rename = "@xml:lang")] + to: Option<JID>, + version: Option<String>, lang: Option<String>, - #[serde(rename = "@xmlns")] - namespace: Option<String>, - #[serde(rename = "@xmlns:stream")] - stream_namespace: Option<String>, + _ns: XMLNS, } -#[derive(Deserialize, Debug)] -#[serde(rename = "stream:features")] -pub struct StreamFeatures { - #[serde(rename = "$value")] - pub features: Vec<StreamFeature>, +impl Stream { + pub fn new_client(from: &JID, to: &JID, id: Option<String>, lang: Option<String>) -> Self { + Self { + from: Some(from.clone()), + id, + to: Some(to.clone()), + version: Some(VERSION.to_owned()), + lang, + _ns: XMLNS::Client, + } + } + + fn build(&self) -> BytesStart { + 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())); + } + start.push_attribute(("xmlns", XMLNS::Client.into())); + start.push_attribute(("xmlns:stream", XMLNS_STREAM)); + start + } +} + +impl<'e> Into<Element<'e>> for Stream { + fn into(self) -> Element<'e> { + Element { + event: Event::Start(self.build().to_owned()), + content: None, + } + } } -#[derive(Deserialize, PartialEq, Debug)] +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: ns, + }) + } else { + Err(JabberError::ParseError) + } + } +} + +#[derive(PartialEq, Debug)] pub enum StreamFeature { - #[serde(rename = "starttls")] StartTls, - // TODO: other stream features - #[serde(rename = "mechanisms")] - Sasl(Mechanisms), + Sasl(Vec<String>), Bind, - #[serde(other)] 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(content) = features_element.content { + for feature_element in content { + 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(content) = feature_element.content { + for mechanism_element in content { + if let Some(content) = mechanism_element.content { + for mechanism_text in content { + 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), + } + } + } + Ok(features) + } +} |