summaryrefslogtreecommitdiffstats
path: root/src/stanza
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
8 files changed, 56 insertions, 1189 deletions
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)
}
}