summaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/client/encrypted.rs16
-rw-r--r--src/client/mod.rs10
-rw-r--r--src/client/unencrypted.rs199
-rw-r--r--src/jabber.rs18
-rw-r--r--src/jid.rs (renamed from src/jid/mod.rs)19
-rw-r--r--src/lib.rs25
-rw-r--r--src/stanza/bind.rs47
-rw-r--r--src/stanza/iq.rs169
-rw-r--r--src/stanza/message.rs1
-rw-r--r--src/stanza/mod.rs656
-rw-r--r--src/stanza/presence.rs1
-rw-r--r--src/stanza/sasl.rs144
-rw-r--r--src/stanza/starttls.rs1
-rw-r--r--src/stanza/stream.rs226
14 files changed, 237 insertions, 1295 deletions
diff --git a/src/client/encrypted.rs b/src/client/encrypted.rs
index 47b2b2c..263d5ff 100644
--- a/src/client/encrypted.rs
+++ b/src/client/encrypted.rs
@@ -2,36 +2,26 @@ use std::{collections::BTreeMap, str};
use quick_xml::{
events::{BytesDecl, Event},
- Reader, Writer,
+ NsReader, Writer,
};
use rsasl::prelude::{Mechname, SASLClient};
use tokio::io::{BufReader, ReadHalf, WriteHalf};
use tokio::net::TcpStream;
use tokio_native_tls::TlsStream;
-use crate::stanza::{
- bind::Bind,
- iq::IQ,
- sasl::{Challenge, Success},
- Element,
-};
-use crate::stanza::{
- sasl::{Auth, Response},
- stream::{Stream, StreamFeature},
-};
use crate::Jabber;
use crate::JabberError;
use crate::Result;
pub struct JabberClient<'j> {
- pub reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
+ pub reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
pub writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
jabber: &'j mut Jabber<'j>,
}
impl<'j> JabberClient<'j> {
pub fn new(
- reader: Reader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
+ reader: NsReader<BufReader<ReadHalf<TlsStream<TcpStream>>>>,
writer: Writer<WriteHalf<TlsStream<TcpStream>>>,
jabber: &'j mut Jabber<'j>,
) -> Self {
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 280e0a1..01df4a4 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -1,22 +1,24 @@
-pub mod encrypted;
+// pub mod encrypted;
pub mod unencrypted;
// use async_trait::async_trait;
-use crate::stanza::stream::StreamFeature;
+// use crate::stanza::stream::StreamFeature;
use crate::JabberError;
use crate::Result;
pub enum JabberClientType<'j> {
- Encrypted(encrypted::JabberClient<'j>),
+ // Encrypted(encrypted::JabberClient<'j>),
Unencrypted(unencrypted::JabberClient<'j>),
}
impl<'j> JabberClientType<'j> {
+ /// ensures an encrypted jabber client
pub async fn ensure_tls(self) -> Result<encrypted::JabberClient<'j>> {
match self {
Self::Encrypted(c) => Ok(c),
Self::Unencrypted(mut c) => {
+ c.start_stream().await?;
let features = c.get_features().await?;
if features.contains(&StreamFeature::StartTls) {
Ok(c.starttls().await?)
@@ -28,7 +30,7 @@ impl<'j> JabberClientType<'j> {
}
}
-// TODO: jabber client trait over both client types
+// TODO: jabber client trait over both client types using macro
// #[async_trait]
// pub trait JabberTrait {
// async fn start_stream(&mut self) -> Result<()>;
diff --git a/src/client/unencrypted.rs b/src/client/unencrypted.rs
index 27b0a5f..4aa9c63 100644
--- a/src/client/unencrypted.rs
+++ b/src/client/unencrypted.rs
@@ -1,27 +1,30 @@
+use std::str;
+
use quick_xml::{
- events::{BytesDecl, BytesStart, Event},
+ events::{BytesStart, Event},
name::QName,
- Reader, Writer,
+ se, NsReader, Writer,
};
use tokio::io::{BufReader, ReadHalf, WriteHalf};
use tokio::net::TcpStream;
use tokio_native_tls::native_tls::TlsConnector;
+use try_map::FallibleMapExt;
-use crate::stanza::stream::StreamFeature;
-use crate::stanza::Element;
+use crate::error::JabberError;
+use crate::stanza::stream::Stream;
+use crate::stanza::DECLARATION;
use crate::Jabber;
use crate::Result;
-use crate::{error::JabberError, stanza::stream::Stream};
pub struct JabberClient<'j> {
- reader: Reader<BufReader<ReadHalf<TcpStream>>>,
+ reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
writer: Writer<WriteHalf<TcpStream>>,
jabber: &'j mut Jabber<'j>,
}
impl<'j> JabberClient<'j> {
pub fn new(
- reader: Reader<BufReader<ReadHalf<TcpStream>>>,
+ reader: NsReader<BufReader<ReadHalf<TcpStream>>>,
writer: Writer<WriteHalf<TcpStream>>,
jabber: &'j mut Jabber<'j>,
) -> Self {
@@ -34,60 +37,144 @@ impl<'j> JabberClient<'j> {
pub async fn start_stream(&mut self) -> Result<()> {
// client to server
- let declaration = BytesDecl::new("1.0", None, None);
+
+ // declaration
+ self.writer.write_event_async(DECLARATION).await?;
+
+ // opening stream element
let server = &self.jabber.server.to_owned().try_into()?;
- let stream_element =
- Stream::new_client(&self.jabber.jid, server, None, Some("en".to_string()));
- self.writer
- .write_event_async(Event::Decl(declaration))
- .await?;
- let stream_element: Element<'_> = stream_element.into();
- stream_element.write_start(&mut self.writer).await?;
- // server to client
- let mut buf = Vec::new();
- self.reader.read_event_into_async(&mut buf).await?;
- let _stream_response = Element::read_start(&mut self.reader).await?;
- Ok(())
- }
+ let stream_element = Stream::new_client(None, server, None, "en");
+ se::to_writer_with_root(&mut self.writer, "stream:stream", &stream_element);
- pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
- Element::read(&mut self.reader).await?.try_into()
- }
+ // server to client
- pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> {
- let mut starttls_element = BytesStart::new("starttls");
- starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"));
- self.writer
- .write_event_async(Event::Empty(starttls_element))
- .await
- .unwrap();
- let mut buf = Vec::new();
- match self.reader.read_event_into_async(&mut buf).await.unwrap() {
- Event::Empty(e) => match e.name() {
- QName(b"proceed") => {
- let connector = TlsConnector::new().unwrap();
- let stream = self
- .reader
- .into_inner()
- .into_inner()
- .unsplit(self.writer.into_inner());
- if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector)
- .connect(&self.jabber.server, stream)
- .await
- {
- let (read, write) = tokio::io::split(tlsstream);
- let reader = Reader::from_reader(BufReader::new(read));
- let writer = Writer::new(write);
- let mut client =
- super::encrypted::JabberClient::new(reader, writer, self.jabber);
- client.start_stream().await?;
- return Ok(client);
+ // may or may not send a declaration
+ let buf = Vec::new();
+ let mut first_event = self.reader.read_resolved_event_into_async(&mut buf).await?;
+ match first_event {
+ (quick_xml::name::ResolveResult::Unbound, Event::Decl(e)) => {
+ if let Ok(version) = e.version() {
+ if version.as_ref() == b"1.0" {
+ first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
+ } else {
+ // todo: error
+ todo!()
}
+ } else {
+ first_event = self.reader.read_resolved_event_into_async(&mut buf).await?
+ }
+ }
+ _ => (),
+ }
+
+ // receive stream element and validate
+ let stream_response: Stream;
+ match first_event {
+ (quick_xml::name::ResolveResult::Bound(ns), Event::Start(e)) => {
+ if ns.0 == crate::stanza::stream::XMLNS.as_bytes() {
+ // stream_response = Stream::new(
+ // e.try_get_attribute("from")?.try_map(|attribute| {
+ // str::from_utf8(attribute.value.as_ref())?
+ // .try_into()?
+ // .as_ref()
+ // })?,
+ // e.try_get_attribute("to")?.try_map(|attribute| {
+ // str::from_utf8(attribute.value.as_ref())?
+ // .try_into()?
+ // .as_ref()
+ // })?,
+ // e.try_get_attribute("id")?.try_map(|attribute| {
+ // str::from_utf8(attribute.value.as_ref())?
+ // .try_into()?
+ // .as_ref()
+ // })?,
+ // e.try_get_attribute("version")?.try_map(|attribute| {
+ // str::from_utf8(attribute.value.as_ref())?
+ // .try_into()?
+ // .as_ref()
+ // })?,
+ // e.try_get_attribute("lang")?.try_map(|attribute| {
+ // str::from_utf8(attribute.value.as_ref())?
+ // .try_into()?
+ // .as_ref()
+ // })?,
+ // );
+ return Ok(());
+ } else {
+ return Err(JabberError::BadStream);
}
- QName(_) => return Err(JabberError::TlsNegotiation),
- },
- _ => return Err(JabberError::TlsNegotiation),
+ }
+ // TODO: errors for incorrect namespace
+ (quick_xml::name::ResolveResult::Unbound, Event::Decl(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Start(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::End(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Empty(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Text(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::CData(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Comment(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Decl(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::PI(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::DocType(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unknown(_), Event::Eof) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::Start(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::End(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::Empty(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::Text(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::CData(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::Comment(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::PI(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::DocType(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Unbound, Event::Eof) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::End(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::Empty(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::Text(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::CData(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::Comment(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::Decl(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::PI(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::DocType(_)) => todo!(),
+ (quick_xml::name::ResolveResult::Bound(_), Event::Eof) => todo!(),
}
- Err(JabberError::TlsNegotiation)
}
+
+ // pub async fn get_features(&mut self) -> Result<Vec<StreamFeature>> {
+ // Element::read(&mut self.reader).await?.try_into()
+ // }
+
+ // pub async fn starttls(mut self) -> Result<super::encrypted::JabberClient<'j>> {
+ // let mut starttls_element = BytesStart::new("starttls");
+ // starttls_element.push_attribute(("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"));
+ // self.writer
+ // .write_event_async(Event::Empty(starttls_element))
+ // .await
+ // .unwrap();
+ // let mut buf = Vec::new();
+ // match self.reader.read_event_into_async(&mut buf).await.unwrap() {
+ // Event::Empty(e) => match e.name() {
+ // QName(b"proceed") => {
+ // let connector = TlsConnector::new().unwrap();
+ // let stream = self
+ // .reader
+ // .into_inner()
+ // .into_inner()
+ // .unsplit(self.writer.into_inner());
+ // if let Ok(tlsstream) = tokio_native_tls::TlsConnector::from(connector)
+ // .connect(&self.jabber.server, stream)
+ // .await
+ // {
+ // let (read, write) = tokio::io::split(tlsstream);
+ // let reader = Reader::from_reader(BufReader::new(read));
+ // let writer = Writer::new(write);
+ // let mut client =
+ // super::encrypted::JabberClient::new(reader, writer, self.jabber);
+ // client.start_stream().await?;
+ // return Ok(client);
+ // }
+ // }
+ // QName(_) => return Err(JabberError::TlsNegotiation),
+ // },
+ // _ => return Err(JabberError::TlsNegotiation),
+ // }
+ // Err(JabberError::TlsNegotiation)
+ // }
}
diff --git a/src/jabber.rs b/src/jabber.rs
index 1a7eddb..d48eb9c 100644
--- a/src/jabber.rs
+++ b/src/jabber.rs
@@ -3,7 +3,7 @@ use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::Arc;
-use quick_xml::{Reader, Writer};
+use quick_xml::{NsReader, Writer};
use rsasl::prelude::SASLConfig;
use tokio::io::BufReader;
use tokio::net::TcpStream;
@@ -22,7 +22,7 @@ pub struct Jabber<'j> {
}
impl<'j> Jabber<'j> {
- pub fn new(jid: JID, password: String) -> Result<Self> {
+ pub fn user(jid: JID, password: String) -> Result<Self> {
let server = jid.domainpart.clone();
let auth = SASLConfig::with_credentials(None, jid.localpart.clone().unwrap(), password)?;
println!("auth: {:?}", auth);
@@ -36,7 +36,7 @@ impl<'j> Jabber<'j> {
pub async fn login(&'j mut self) -> Result<JabberClient<'j>> {
let mut client = self.connect().await?.ensure_tls().await?;
- println!("negotiation");
+ client.start_stream().await?;
client.negotiate().await?;
Ok(client)
}
@@ -106,6 +106,7 @@ impl<'j> Jabber<'j> {
socket_addrs
}
+ /// establishes a connection to the server
pub async fn connect(&'j mut self) -> Result<JabberClientType> {
for (socket_addr, is_tls) in self.get_sockets().await {
println!("trying {}", socket_addr);
@@ -118,21 +119,18 @@ impl<'j> Jabber<'j> {
.await
{
let (read, write) = tokio::io::split(stream);
- let reader = Reader::from_reader(BufReader::new(read));
+ let reader = NsReader::from_reader(BufReader::new(read));
let writer = Writer::new(write);
- let mut client = client::encrypted::JabberClient::new(reader, writer, self);
- client.start_stream().await?;
+ let client = client::encrypted::JabberClient::new(reader, writer, self);
return Ok(JabberClientType::Encrypted(client));
}
}
false => {
if let Ok(stream) = TcpStream::connect(socket_addr).await {
let (read, write) = tokio::io::split(stream);
- let reader = Reader::from_reader(BufReader::new(read));
+ let reader = NsReader::from_reader(BufReader::new(read));
let writer = Writer::new(write);
- let mut client =
- client::unencrypted::JabberClient::new(reader, writer, self);
- client.start_stream().await?;
+ let client = client::unencrypted::JabberClient::new(reader, writer, self);
return Ok(JabberClientType::Unencrypted(client));
}
}
diff --git a/src/jid/mod.rs b/src/jid.rs
index e13fed7..65738dc 100644
--- a/src/jid/mod.rs
+++ b/src/jid.rs
@@ -1,5 +1,7 @@
use std::str::FromStr;
+use serde::Serialize;
+
#[derive(PartialEq, Debug, Clone)]
pub struct JID {
// TODO: validate localpart (length, char]
@@ -8,6 +10,15 @@ pub struct JID {
pub resourcepart: Option<String>,
}
+impl Serialize for JID {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(&self.to_string())
+ }
+}
+
pub enum JIDError {
NoResourcePart,
ParseError(ParseError),
@@ -97,6 +108,14 @@ impl TryFrom<String> for JID {
}
}
+impl TryFrom<&str> for JID {
+ type Error = ParseError;
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ value.parse()
+ }
+}
+
impl std::fmt::Display for JID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
diff --git a/src/lib.rs b/src/lib.rs
index 8162ccc..86da83d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,7 +8,7 @@ pub mod jabber;
pub mod jid;
pub mod stanza;
-pub use client::encrypted::JabberClient;
+// pub use client::encrypted::JabberClient;
pub use error::JabberError;
pub use jabber::Jabber;
pub use jid::JID;
@@ -22,30 +22,9 @@ mod tests {
use crate::Jabber;
use crate::JID;
- // #[tokio::test]
- // async fn get_sockets() {
- // let jabber = Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned());
- // println!("{:?}", jabber.get_sockets().await)
- // }
-
- // #[tokio::test]
- // async fn connect() {
- // Jabber::new(JID::from_str("cel@blos.sm").unwrap(), "password".to_owned())
- // .unwrap()
- // .connect()
- // .await
- // .unwrap()
- // .ensure_tls()
- // .await
- // .unwrap()
- // .start_stream()
- // .await
- // .unwrap();
- // }
-
#[tokio::test]
async fn login() {
- Jabber::new(
+ Jabber::user(
JID::from_str("test@blos.sm/clown").unwrap(),
"slayed".to_owned(),
)
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)
}
}