2024-11-23 22:39:44 +00:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2023-10-20 04:51:56 +01:00
|
|
|
|
2024-12-04 18:18:37 +00:00
|
|
|
use jid::JID;
|
2024-11-28 18:02:06 +00:00
|
|
|
use peanuts::element::{Content, ElementBuilder, FromElement, IntoElement, NamespaceDeclaration};
|
2024-11-23 22:39:44 +00:00
|
|
|
use peanuts::{element::Name, Element};
|
2023-10-20 04:51:56 +01:00
|
|
|
|
2024-12-04 18:18:37 +00:00
|
|
|
use crate::bind;
|
2024-11-23 22:39:44 +00:00
|
|
|
|
2024-11-29 02:11:02 +00:00
|
|
|
use super::sasl::{self, Mechanisms};
|
2024-11-28 18:02:06 +00:00
|
|
|
use super::starttls::{self, StartTls};
|
2024-12-03 03:51:26 +00:00
|
|
|
use super::stream_error::{Error as StreamError, Text};
|
|
|
|
use super::{client, stream_error};
|
2024-11-24 02:04:45 +00:00
|
|
|
|
2024-11-23 22:39:44 +00:00
|
|
|
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
|
2023-10-20 04:51:56 +01:00
|
|
|
|
|
|
|
// MUST be qualified by stream namespace
|
2024-11-23 22:39:44 +00:00
|
|
|
// #[derive(XmlSerialize, XmlDeserialize)]
|
|
|
|
// #[peanuts(xmlns = XMLNS)]
|
2024-11-24 02:04:45 +00:00
|
|
|
#[derive(Debug)]
|
2024-11-23 22:39:44 +00:00
|
|
|
pub struct Stream {
|
|
|
|
pub from: Option<JID>,
|
|
|
|
to: Option<JID>,
|
|
|
|
id: Option<String>,
|
|
|
|
version: Option<String>,
|
2023-10-20 04:51:56 +01:00
|
|
|
// TODO: lang enum
|
2024-11-23 22:39:44 +00:00
|
|
|
lang: Option<String>,
|
|
|
|
// #[peanuts(content)]
|
|
|
|
// content: Message,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromElement for Stream {
|
2024-11-28 18:02:06 +00:00
|
|
|
fn from_element(mut element: Element) -> std::result::Result<Self, peanuts::DeserializeError> {
|
|
|
|
element.check_namespace(XMLNS)?;
|
|
|
|
element.check_name("stream")?;
|
|
|
|
|
|
|
|
let from = element.attribute_opt("from")?;
|
|
|
|
let to = element.attribute_opt("to")?;
|
|
|
|
let id = element.attribute_opt("id")?;
|
|
|
|
let version = element.attribute_opt("version")?;
|
|
|
|
let lang = element.attribute_opt_namespaced("lang", peanuts::XML_NS)?;
|
|
|
|
|
|
|
|
Ok(Stream {
|
|
|
|
from,
|
|
|
|
to,
|
|
|
|
id,
|
|
|
|
version,
|
|
|
|
lang,
|
|
|
|
})
|
2024-11-23 22:39:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoElement for Stream {
|
2024-11-28 18:02:06 +00:00
|
|
|
fn builder(&self) -> ElementBuilder {
|
|
|
|
Element::builder("stream", Some(XMLNS.to_string()))
|
|
|
|
.push_namespace_declaration_override(Some("stream"), XMLNS)
|
2024-12-02 21:50:15 +00:00
|
|
|
.push_namespace_declaration_override(None::<&str>, client::XMLNS)
|
2024-11-28 18:02:06 +00:00
|
|
|
.push_attribute_opt("to", self.to.clone())
|
|
|
|
.push_attribute_opt("from", self.from.clone())
|
|
|
|
.push_attribute_opt("id", self.id.clone())
|
|
|
|
.push_attribute_opt("version", self.version.clone())
|
|
|
|
.push_attribute_opt_namespaced(peanuts::XML_NS, "to", self.lang.clone())
|
2024-11-23 22:39:44 +00:00
|
|
|
}
|
2023-06-19 19:23:54 +01:00
|
|
|
}
|
|
|
|
|
2024-11-23 22:39:44 +00:00
|
|
|
impl<'s> Stream {
|
2023-10-20 04:51:56 +01:00
|
|
|
pub fn new(
|
2024-11-23 22:39:44 +00:00
|
|
|
from: Option<JID>,
|
|
|
|
to: Option<JID>,
|
|
|
|
id: Option<String>,
|
|
|
|
version: Option<String>,
|
|
|
|
lang: Option<String>,
|
2023-10-20 04:51:56 +01:00
|
|
|
) -> Self {
|
2023-07-11 21:28:42 +01:00
|
|
|
Self {
|
2023-10-20 04:51:56 +01:00
|
|
|
from,
|
|
|
|
to,
|
2023-07-11 21:28:42 +01:00
|
|
|
id,
|
2023-10-20 04:51:56 +01:00
|
|
|
version,
|
2023-07-11 21:28:42 +01:00
|
|
|
lang,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-20 04:51:56 +01:00
|
|
|
/// 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.
|
2024-11-23 22:39:44 +00:00
|
|
|
pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self {
|
2023-10-20 04:51:56 +01:00
|
|
|
Self {
|
|
|
|
from,
|
|
|
|
to: Some(to),
|
|
|
|
id,
|
2024-11-23 22:39:44 +00:00
|
|
|
version: Some("1.0".to_string()),
|
2023-10-20 04:51:56 +01:00
|
|
|
lang: Some(lang),
|
2023-07-11 21:28:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-24 02:04:45 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Features {
|
2024-11-29 02:11:02 +00:00
|
|
|
pub features: Vec<Feature>,
|
2024-11-24 02:04:45 +00:00
|
|
|
}
|
|
|
|
|
2024-12-04 18:18:37 +00:00
|
|
|
impl Features {
|
|
|
|
pub fn negotiate(self) -> Option<Feature> {
|
|
|
|
if let Some(Feature::StartTls(s)) = self
|
|
|
|
.features
|
|
|
|
.iter()
|
|
|
|
.find(|feature| matches!(feature, Feature::StartTls(_s)))
|
|
|
|
{
|
|
|
|
// TODO: avoid clone
|
|
|
|
return Some(Feature::StartTls(s.clone()));
|
|
|
|
} else if let Some(Feature::Sasl(mechanisms)) = self
|
|
|
|
.features
|
|
|
|
.iter()
|
|
|
|
.find(|feature| matches!(feature, Feature::Sasl(_)))
|
|
|
|
{
|
|
|
|
// TODO: avoid clone
|
|
|
|
return Some(Feature::Sasl(mechanisms.clone()));
|
|
|
|
} else if let Some(Feature::Bind) = self
|
|
|
|
.features
|
|
|
|
.into_iter()
|
|
|
|
.find(|feature| matches!(feature, Feature::Bind))
|
|
|
|
{
|
|
|
|
Some(Feature::Bind)
|
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-24 02:04:45 +00:00
|
|
|
impl IntoElement for Features {
|
2024-11-28 18:02:06 +00:00
|
|
|
fn builder(&self) -> ElementBuilder {
|
|
|
|
Element::builder("features", Some(XMLNS)).push_children(self.features.clone())
|
2024-11-24 02:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromElement for Features {
|
2024-11-28 18:02:06 +00:00
|
|
|
fn from_element(
|
|
|
|
mut element: Element,
|
|
|
|
) -> std::result::Result<Features, peanuts::DeserializeError> {
|
|
|
|
element.check_namespace(XMLNS)?;
|
|
|
|
element.check_name("features")?;
|
|
|
|
|
|
|
|
let features = element.children()?;
|
|
|
|
|
|
|
|
Ok(Features { features })
|
2024-11-24 02:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-28 18:02:06 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2024-11-24 02:04:45 +00:00
|
|
|
pub enum Feature {
|
|
|
|
StartTls(StartTls),
|
2024-11-29 02:11:02 +00:00
|
|
|
Sasl(Mechanisms),
|
2024-11-24 02:04:45 +00:00
|
|
|
Bind,
|
|
|
|
Unknown,
|
|
|
|
}
|
2024-11-28 18:02:06 +00:00
|
|
|
|
|
|
|
impl IntoElement for Feature {
|
|
|
|
fn builder(&self) -> ElementBuilder {
|
|
|
|
match self {
|
|
|
|
Feature::StartTls(start_tls) => start_tls.builder(),
|
2024-11-29 02:11:02 +00:00
|
|
|
Feature::Sasl(mechanisms) => mechanisms.builder(),
|
2024-11-28 18:02:06 +00:00
|
|
|
Feature::Bind => todo!(),
|
|
|
|
Feature::Unknown => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromElement for Feature {
|
|
|
|
fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
|
|
|
|
match element.identify() {
|
|
|
|
(Some(starttls::XMLNS), "starttls") => {
|
|
|
|
Ok(Feature::StartTls(StartTls::from_element(element)?))
|
|
|
|
}
|
2024-11-29 02:11:02 +00:00
|
|
|
(Some(sasl::XMLNS), "mechanisms") => {
|
|
|
|
Ok(Feature::Sasl(Mechanisms::from_element(element)?))
|
|
|
|
}
|
2024-12-04 18:18:37 +00:00
|
|
|
(Some(bind::XMLNS), "bind") => Ok(Feature::Bind),
|
|
|
|
_ => Ok(Feature::Unknown),
|
2024-11-28 18:02:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-12-03 03:51:26 +00:00
|
|
|
|
2024-12-04 17:38:36 +00:00
|
|
|
#[derive(Debug)]
|
2024-12-03 03:51:26 +00:00
|
|
|
pub struct Error {
|
|
|
|
error: StreamError,
|
|
|
|
text: Option<Text>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromElement for Error {
|
|
|
|
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
|
|
|
|
element.check_name("error")?;
|
|
|
|
element.check_namespace(XMLNS)?;
|
|
|
|
|
|
|
|
let error = element.pop_child_one()?;
|
|
|
|
let text = element.pop_child_opt()?;
|
|
|
|
|
|
|
|
Ok(Error { error, text })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoElement for Error {
|
|
|
|
fn builder(&self) -> ElementBuilder {
|
|
|
|
Element::builder("error", Some(XMLNS))
|
|
|
|
.push_child(self.error.clone())
|
|
|
|
.push_child_opt(self.text.clone())
|
|
|
|
}
|
|
|
|
}
|