implement remaining rfc6120 xml schemas
This commit is contained in:
parent
be198ca15b
commit
7c2577d196
|
@ -2,19 +2,16 @@ use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use peanuts::element::{FromElement, IntoElement};
|
use peanuts::element::IntoElement;
|
||||||
use peanuts::{Reader, Writer};
|
use peanuts::{Reader, Writer};
|
||||||
use rsasl::prelude::{Mechname, SASLClient, SASLConfig};
|
use rsasl::prelude::{Mechname, SASLClient, SASLConfig};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter, ReadHalf, WriteHalf};
|
use tokio::io::{AsyncRead, AsyncWrite, ReadHalf, WriteHalf};
|
||||||
use tokio::time::timeout;
|
|
||||||
use tokio_native_tls::native_tls::TlsConnector;
|
use tokio_native_tls::native_tls::TlsConnector;
|
||||||
use tracing::{debug, info, instrument, trace};
|
use tracing::{debug, instrument};
|
||||||
use trust_dns_resolver::proto::rr::domain::IntoLabel;
|
|
||||||
|
|
||||||
use crate::connection::{Tls, Unencrypted};
|
use crate::connection::{Tls, Unencrypted};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::stanza::bind::{Bind, BindType, FullJidType, ResourceType};
|
use crate::stanza::bind::{Bind, BindType, FullJidType, ResourceType};
|
||||||
use crate::stanza::client::error::Error as ClientError;
|
|
||||||
use crate::stanza::client::iq::{Iq, IqType, Query};
|
use crate::stanza::client::iq::{Iq, IqType, Query};
|
||||||
use crate::stanza::sasl::{Auth, Challenge, Mechanisms, Response, ServerResponse};
|
use crate::stanza::sasl::{Auth, Challenge, Mechanisms, Response, ServerResponse};
|
||||||
use crate::stanza::starttls::{Proceed, StartTls};
|
use crate::stanza::starttls::{Proceed, StartTls};
|
||||||
|
@ -257,7 +254,7 @@ where
|
||||||
// server to client
|
// server to client
|
||||||
|
|
||||||
// may or may not send a declaration
|
// may or may not send a declaration
|
||||||
let decl = self.reader.read_prolog().await?;
|
let _decl = self.reader.read_prolog().await?;
|
||||||
|
|
||||||
// receive stream element and validate
|
// receive stream element and validate
|
||||||
let text = str::from_utf8(self.reader.buffer.data()).unwrap();
|
let text = str::from_utf8(self.reader.buffer.data()).unwrap();
|
||||||
|
@ -471,7 +468,7 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn negotiate() {
|
async fn negotiate() {
|
||||||
let jabber = Connection::connect_user("test@blos.sm", "slayed".to_string())
|
let _jabber = Connection::connect_user("test@blos.sm", "slayed".to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.ensure_tls()
|
.ensure_tls()
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -9,14 +9,20 @@ pub mod jid;
|
||||||
pub mod stanza;
|
pub mod stanza;
|
||||||
|
|
||||||
pub use connection::Connection;
|
pub use connection::Connection;
|
||||||
|
use connection::Tls;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use jabber::Jabber;
|
pub use jabber::Jabber;
|
||||||
pub use jid::JID;
|
pub use jid::JID;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
pub async fn login<J: TryInto<JID>, P: AsRef<str>>(jid: J, password: P) -> Result<Connection> {
|
pub async fn login<J: AsRef<str>, P: AsRef<str>>(jid: J, password: P) -> Result<Jabber<Tls>> {
|
||||||
todo!()
|
Ok(Connection::connect_user(jid, password.as_ref().to_string())
|
||||||
|
.await?
|
||||||
|
.ensure_tls()
|
||||||
|
.await?
|
||||||
|
.negotiate()
|
||||||
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::str::FromStr;
|
||||||
use peanuts::element::{FromElement, IntoElement};
|
use peanuts::element::{FromElement, IntoElement};
|
||||||
use peanuts::{DeserializeError, Element};
|
use peanuts::{DeserializeError, Element};
|
||||||
|
|
||||||
use crate::stanza::error::Text;
|
use crate::stanza::stanza_error::Error as StanzaError;
|
||||||
use crate::stanza::Error as StanzaError;
|
use crate::stanza::stanza_error::Text;
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,186 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use peanuts::{
|
||||||
|
element::{FromElement, IntoElement},
|
||||||
|
DeserializeError, Element, XML_NS,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::JID;
|
use crate::JID;
|
||||||
|
|
||||||
|
use super::XMLNS;
|
||||||
|
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
from: Option<JID>,
|
from: Option<JID>,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
to: Option<JID>,
|
to: Option<JID>,
|
||||||
r#type: Option<MessageType>,
|
// can be omitted, if so default to normal
|
||||||
|
r#type: MessageType,
|
||||||
|
lang: Option<String>,
|
||||||
// children
|
// children
|
||||||
subject: Option<Subject>,
|
subject: Option<Subject>,
|
||||||
body: Option<Body>,
|
body: Option<Body>,
|
||||||
thread: Option<Thread>,
|
thread: Option<Thread>,
|
||||||
lang: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Message {
|
||||||
|
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("message")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let from = element.attribute_opt("from")?;
|
||||||
|
let id = element.attribute_opt("id")?;
|
||||||
|
let to = element.attribute_opt("to")?;
|
||||||
|
let r#type = element.attribute_opt("type")?.unwrap_or_default();
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
|
||||||
|
let subject = element.child_opt()?;
|
||||||
|
let body = element.child_opt()?;
|
||||||
|
let thread = element.child_opt()?;
|
||||||
|
|
||||||
|
Ok(Message {
|
||||||
|
from,
|
||||||
|
id,
|
||||||
|
to,
|
||||||
|
r#type,
|
||||||
|
lang,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
thread,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Message {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("message", Some(XMLNS))
|
||||||
|
.push_attribute_opt("from", self.from.clone())
|
||||||
|
.push_attribute_opt("id", self.id.clone())
|
||||||
|
.push_attribute_opt("to", self.to.clone())
|
||||||
|
.push_attribute_opt("type", {
|
||||||
|
if self.r#type == MessageType::Normal {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.r#type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_child_opt(self.subject.clone())
|
||||||
|
.push_child_opt(self.body.clone())
|
||||||
|
.push_child_opt(self.thread.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum MessageType {
|
pub enum MessageType {
|
||||||
Chat,
|
Chat,
|
||||||
Error,
|
Error,
|
||||||
Groupchat,
|
Groupchat,
|
||||||
Headline,
|
Headline,
|
||||||
|
#[default]
|
||||||
Normal,
|
Normal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for MessageType {
|
||||||
|
type Err = DeserializeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"chat" => Ok(MessageType::Chat),
|
||||||
|
"error" => Ok(MessageType::Error),
|
||||||
|
"groupchat" => Ok(MessageType::Groupchat),
|
||||||
|
"headline" => Ok(MessageType::Headline),
|
||||||
|
"normal" => Ok(MessageType::Normal),
|
||||||
|
_ => Err(DeserializeError::FromStr(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for MessageType {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
MessageType::Chat => "chat".to_string(),
|
||||||
|
MessageType::Error => "error".to_string(),
|
||||||
|
MessageType::Groupchat => "groupchat".to_string(),
|
||||||
|
MessageType::Headline => "headline".to_string(),
|
||||||
|
MessageType::Normal => "normal".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Body {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("body")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
let body = element.pop_value_opt()?;
|
||||||
|
|
||||||
|
Ok(Body { lang, body })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Body {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("body", Some(XMLNS))
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_text_opt(self.body.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Subject {
|
pub struct Subject {
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
subject: Option<String>,
|
subject: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Subject {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("subject")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
let subject = element.pop_value_opt()?;
|
||||||
|
|
||||||
|
Ok(Subject { lang, subject })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Subject {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("subject", Some(XMLNS))
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_text_opt(self.subject.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
// TODO: NOT DONE
|
|
||||||
parent: Option<String>,
|
parent: Option<String>,
|
||||||
thread: Option<String>,
|
thread: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Thread {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("thread")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let parent = element.attribute_opt("parent")?;
|
||||||
|
let thread = element.pop_value_opt()?;
|
||||||
|
|
||||||
|
Ok(Thread { parent, thread })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Thread {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("thread", Some(XMLNS))
|
||||||
|
.push_attribute_opt("parent", self.parent.clone())
|
||||||
|
.push_text_opt(self.thread.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,48 @@
|
||||||
|
use iq::Iq;
|
||||||
|
use message::Message;
|
||||||
|
use peanuts::{
|
||||||
|
element::{FromElement, IntoElement},
|
||||||
|
DeserializeError,
|
||||||
|
};
|
||||||
|
use presence::Presence;
|
||||||
|
|
||||||
|
use super::stream::{self, Error as StreamError};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod iq;
|
pub mod iq;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod presence;
|
pub mod presence;
|
||||||
|
|
||||||
pub const XMLNS: &str = "jabber:client";
|
pub const XMLNS: &str = "jabber:client";
|
||||||
|
|
||||||
|
pub enum Stanza {
|
||||||
|
Message(Message),
|
||||||
|
Presence(Presence),
|
||||||
|
Iq(Iq),
|
||||||
|
Error(StreamError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromElement for Stanza {
|
||||||
|
fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
match element.identify() {
|
||||||
|
(Some(XMLNS), "message") => Ok(Stanza::Message(Message::from_element(element)?)),
|
||||||
|
(Some(XMLNS), "presence") => Ok(Stanza::Presence(Presence::from_element(element)?)),
|
||||||
|
(Some(XMLNS), "iq") => Ok(Stanza::Iq(Iq::from_element(element)?)),
|
||||||
|
(Some(stream::XMLNS), "error") => {
|
||||||
|
Ok(Stanza::Error(StreamError::from_element(element)?))
|
||||||
|
}
|
||||||
|
_ => Err(DeserializeError::UnexpectedElement(element)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Stanza {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
match self {
|
||||||
|
Stanza::Message(message) => message.builder(),
|
||||||
|
Stanza::Presence(presence) => presence.builder(),
|
||||||
|
Stanza::Iq(iq) => iq.builder(),
|
||||||
|
Stanza::Error(error) => error.builder(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +1,77 @@
|
||||||
use peanuts::element::{FromElement, IntoElement};
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use peanuts::{
|
||||||
|
element::{FromElement, IntoElement},
|
||||||
|
DeserializeError, Element, XML_NS,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::JID;
|
use crate::JID;
|
||||||
|
|
||||||
use super::error::Error;
|
use super::{error::Error, XMLNS};
|
||||||
|
|
||||||
pub struct Presence {
|
pub struct Presence {
|
||||||
from: Option<JID>,
|
from: Option<JID>,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
to: Option<JID>,
|
to: Option<JID>,
|
||||||
r#type: PresenceType,
|
r#type: Option<PresenceType>,
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
// children
|
// children
|
||||||
show: Option<Show>,
|
show: Option<Show>,
|
||||||
status: Option<Status>,
|
status: Option<Status>,
|
||||||
priority: Option<Priority>,
|
priority: Option<Priority>,
|
||||||
|
// TODO: ##other
|
||||||
|
// other: Vec<Other>,
|
||||||
errors: Vec<Error>,
|
errors: Vec<Error>,
|
||||||
// ##other
|
|
||||||
// content: Vec<Box<dyn AsElement>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Presence {
|
||||||
|
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("presence")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let from = element.attribute_opt("from")?;
|
||||||
|
let id = element.attribute_opt("id")?;
|
||||||
|
let to = element.attribute_opt("to")?;
|
||||||
|
let r#type = element.attribute_opt("type")?;
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
|
||||||
|
let show = element.child_opt()?;
|
||||||
|
let status = element.child_opt()?;
|
||||||
|
let priority = element.child_opt()?;
|
||||||
|
let errors = element.children()?;
|
||||||
|
|
||||||
|
Ok(Presence {
|
||||||
|
from,
|
||||||
|
id,
|
||||||
|
to,
|
||||||
|
r#type,
|
||||||
|
lang,
|
||||||
|
show,
|
||||||
|
status,
|
||||||
|
priority,
|
||||||
|
errors,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Presence {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("presence", Some(XMLNS))
|
||||||
|
.push_attribute_opt("from", self.from.clone())
|
||||||
|
.push_attribute_opt("id", self.id.clone())
|
||||||
|
.push_attribute_opt("to", self.to.clone())
|
||||||
|
.push_attribute_opt("type", self.r#type)
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_child_opt(self.show)
|
||||||
|
.push_child_opt(self.status.clone())
|
||||||
|
.push_child_opt(self.priority)
|
||||||
|
.push_children(self.errors.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Other {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub enum PresenceType {
|
pub enum PresenceType {
|
||||||
Error,
|
Error,
|
||||||
Probe,
|
Probe,
|
||||||
|
@ -29,6 +82,38 @@ pub enum PresenceType {
|
||||||
Unsubscribed,
|
Unsubscribed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for PresenceType {
|
||||||
|
type Err = DeserializeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"error" => Ok(PresenceType::Error),
|
||||||
|
"probe" => Ok(PresenceType::Probe),
|
||||||
|
"subscribe" => Ok(PresenceType::Subscribe),
|
||||||
|
"subscribed" => Ok(PresenceType::Subscribed),
|
||||||
|
"unavailable" => Ok(PresenceType::Unavailable),
|
||||||
|
"unsubscribe" => Ok(PresenceType::Unsubscribe),
|
||||||
|
"unsubscribed" => Ok(PresenceType::Unsubscribed),
|
||||||
|
s => Err(DeserializeError::FromStr(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for PresenceType {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
PresenceType::Error => "error".to_string(),
|
||||||
|
PresenceType::Probe => "probe".to_string(),
|
||||||
|
PresenceType::Subscribe => "subscribe".to_string(),
|
||||||
|
PresenceType::Subscribed => "subscribed".to_string(),
|
||||||
|
PresenceType::Unavailable => "unavailable".to_string(),
|
||||||
|
PresenceType::Unsubscribe => "unsubscribe".to_string(),
|
||||||
|
PresenceType::Unsubscribed => "unsubscribed".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub enum Show {
|
pub enum Show {
|
||||||
Away,
|
Away,
|
||||||
Chat,
|
Chat,
|
||||||
|
@ -36,13 +121,106 @@ pub enum Show {
|
||||||
Xa,
|
Xa,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromElement for Show {
|
||||||
|
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("show")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
Ok(element.pop_value()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Show {
|
||||||
|
type Err = DeserializeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"away" => Ok(Show::Away),
|
||||||
|
"chat" => Ok(Show::Chat),
|
||||||
|
"dnd" => Ok(Show::Dnd),
|
||||||
|
"xa" => Ok(Show::Xa),
|
||||||
|
s => Err(DeserializeError::FromStr(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Show {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("show", Some(XMLNS)).push_text(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Show {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Show::Away => "away".to_string(),
|
||||||
|
Show::Chat => "chat".to_string(),
|
||||||
|
Show::Dnd => "dnd".to_string(),
|
||||||
|
Show::Xa => "xa".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
status: String1024,
|
status: String1024,
|
||||||
}
|
}
|
||||||
|
|
||||||
// minLength 1 maxLength 1024
|
impl FromElement for Status {
|
||||||
pub struct String1024(String);
|
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("status")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
let status = element.pop_value()?;
|
||||||
|
|
||||||
|
Ok(Status { lang, status })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Status {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("status", Some(XMLNS))
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_text(self.status.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: enforce?
|
||||||
|
/// minLength 1 maxLength 1024
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct String1024(pub String);
|
||||||
|
|
||||||
|
impl FromStr for String1024 {
|
||||||
|
type Err = DeserializeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(String1024(s.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for String1024 {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// xs:byte
|
// xs:byte
|
||||||
pub struct Priority(u8);
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Priority(pub i8);
|
||||||
|
|
||||||
|
impl FromElement for Priority {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("priority")?;
|
||||||
|
element.check_namespace(XMLNS)?;
|
||||||
|
|
||||||
|
Ok(Priority(element.pop_value()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Priority {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("priority", Some(XMLNS)).push_text(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@ use peanuts::declaration::VersionInfo;
|
||||||
|
|
||||||
pub mod bind;
|
pub mod bind;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
|
||||||
pub mod sasl;
|
pub mod sasl;
|
||||||
|
pub mod stanza_error;
|
||||||
pub mod starttls;
|
pub mod starttls;
|
||||||
pub mod stream;
|
pub mod stream;
|
||||||
|
pub mod stream_error;
|
||||||
|
|
||||||
pub static XML_VERSION: VersionInfo = VersionInfo::One;
|
pub static XML_VERSION: VersionInfo = VersionInfo::One;
|
||||||
|
|
||||||
pub use error::Error;
|
|
||||||
|
|
|
@ -6,11 +6,12 @@ use peanuts::{element::Name, Element};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::stanza::bind;
|
use crate::stanza::bind;
|
||||||
use crate::{Error, JID};
|
use crate::JID;
|
||||||
|
|
||||||
use super::client;
|
|
||||||
use super::sasl::{self, Mechanisms};
|
use super::sasl::{self, Mechanisms};
|
||||||
use super::starttls::{self, StartTls};
|
use super::starttls::{self, StartTls};
|
||||||
|
use super::stream_error::{Error as StreamError, Text};
|
||||||
|
use super::{client, stream_error};
|
||||||
|
|
||||||
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
|
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
|
||||||
|
|
||||||
|
@ -162,3 +163,28 @@ impl FromElement for Feature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
use peanuts::{
|
||||||
|
element::{FromElement, IntoElement},
|
||||||
|
DeserializeError, Element, XML_NS,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-streams";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Error {
|
||||||
|
BadFormat,
|
||||||
|
BadNamespacePrefix,
|
||||||
|
Conflict,
|
||||||
|
ConnectionTimeout,
|
||||||
|
HostGone,
|
||||||
|
HostUnknown,
|
||||||
|
ImproperAddressing,
|
||||||
|
InternalServerError,
|
||||||
|
InvalidFrom,
|
||||||
|
InvalidId,
|
||||||
|
InvalidNamespace,
|
||||||
|
InvalidXml,
|
||||||
|
NotAuthorized,
|
||||||
|
NotWellFormed,
|
||||||
|
PolicyViolation,
|
||||||
|
RemoteConnectionFailed,
|
||||||
|
Reset,
|
||||||
|
ResourceConstraint,
|
||||||
|
RestrictedXml,
|
||||||
|
SeeOtherHost(Option<String>),
|
||||||
|
SystemShutdown,
|
||||||
|
UndefinedCondition,
|
||||||
|
UnsupportedEncoding,
|
||||||
|
UnsupportedStanzaType,
|
||||||
|
UnsupportedVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromElement for Error {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
let error;
|
||||||
|
match element.identify() {
|
||||||
|
(Some(XMLNS), "bad-format") => error = Error::BadFormat,
|
||||||
|
(Some(XMLNS), "bad-namespace-prefix") => error = Error::BadNamespacePrefix,
|
||||||
|
(Some(XMLNS), "conflict") => error = Error::Conflict,
|
||||||
|
(Some(XMLNS), "connection-timeout") => error = Error::ConnectionTimeout,
|
||||||
|
(Some(XMLNS), "host-gone") => error = Error::HostGone,
|
||||||
|
(Some(XMLNS), "host-unknown") => error = Error::HostUnknown,
|
||||||
|
(Some(XMLNS), "improper-addressing") => error = Error::ImproperAddressing,
|
||||||
|
(Some(XMLNS), "internal-server-error") => error = Error::InternalServerError,
|
||||||
|
(Some(XMLNS), "invalid-from") => error = Error::InvalidFrom,
|
||||||
|
(Some(XMLNS), "invalid-id") => error = Error::InvalidId,
|
||||||
|
(Some(XMLNS), "invalid-namespace") => error = Error::InvalidNamespace,
|
||||||
|
(Some(XMLNS), "invalid-xml") => error = Error::InvalidXml,
|
||||||
|
(Some(XMLNS), "not-authorized") => error = Error::NotAuthorized,
|
||||||
|
(Some(XMLNS), "not-well-formed") => error = Error::NotWellFormed,
|
||||||
|
(Some(XMLNS), "policy-violation") => error = Error::PolicyViolation,
|
||||||
|
(Some(XMLNS), "remote-connection-failed") => error = Error::RemoteConnectionFailed,
|
||||||
|
(Some(XMLNS), "reset") => error = Error::Reset,
|
||||||
|
(Some(XMLNS), "resource-constraint") => error = Error::ResourceConstraint,
|
||||||
|
(Some(XMLNS), "restricted-xml") => error = Error::RestrictedXml,
|
||||||
|
(Some(XMLNS), "see-other-host") => {
|
||||||
|
return Ok(Error::SeeOtherHost(element.pop_value_opt()?))
|
||||||
|
}
|
||||||
|
(Some(XMLNS), "system-shutdown") => error = Error::SystemShutdown,
|
||||||
|
(Some(XMLNS), "undefined-condition") => error = Error::UndefinedCondition,
|
||||||
|
(Some(XMLNS), "unsupported-encoding") => error = Error::UnsupportedEncoding,
|
||||||
|
(Some(XMLNS), "unsupported-stanza-type") => error = Error::UnsupportedStanzaType,
|
||||||
|
(Some(XMLNS), "unsupported-version") => error = Error::UnsupportedVersion,
|
||||||
|
_ => return Err(DeserializeError::UnexpectedElement(element)),
|
||||||
|
}
|
||||||
|
element.no_more_content()?;
|
||||||
|
return Ok(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Error {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
match self {
|
||||||
|
Error::BadFormat => Element::builder("bad-format", Some(XMLNS)),
|
||||||
|
Error::BadNamespacePrefix => Element::builder("bad-namespace-prefix", Some(XMLNS)),
|
||||||
|
Error::Conflict => Element::builder("conflict", Some(XMLNS)),
|
||||||
|
Error::ConnectionTimeout => Element::builder("connection-timeout", Some(XMLNS)),
|
||||||
|
Error::HostGone => Element::builder("host-gone", Some(XMLNS)),
|
||||||
|
Error::HostUnknown => Element::builder("host-unknown", Some(XMLNS)),
|
||||||
|
Error::ImproperAddressing => Element::builder("improper-addressing", Some(XMLNS)),
|
||||||
|
Error::InternalServerError => Element::builder("internal-server-error", Some(XMLNS)),
|
||||||
|
Error::InvalidFrom => Element::builder("invalid-from", Some(XMLNS)),
|
||||||
|
Error::InvalidId => Element::builder("invalid-id", Some(XMLNS)),
|
||||||
|
Error::InvalidNamespace => Element::builder("invalid-namespace", Some(XMLNS)),
|
||||||
|
Error::InvalidXml => Element::builder("invalid-xml", Some(XMLNS)),
|
||||||
|
Error::NotAuthorized => Element::builder("not-authorized", Some(XMLNS)),
|
||||||
|
Error::NotWellFormed => Element::builder("not-well-formed", Some(XMLNS)),
|
||||||
|
Error::PolicyViolation => Element::builder("policy-violation", Some(XMLNS)),
|
||||||
|
Error::RemoteConnectionFailed => {
|
||||||
|
Element::builder("remote-connection-failed", Some(XMLNS))
|
||||||
|
}
|
||||||
|
Error::Reset => Element::builder("reset", Some(XMLNS)),
|
||||||
|
Error::ResourceConstraint => Element::builder("resource-constraint", Some(XMLNS)),
|
||||||
|
Error::RestrictedXml => Element::builder("restricted-xml", Some(XMLNS)),
|
||||||
|
Error::SeeOtherHost(h) => {
|
||||||
|
Element::builder("see-other-host", Some(XMLNS)).push_text_opt(h.clone())
|
||||||
|
}
|
||||||
|
Error::SystemShutdown => Element::builder("system-shutdown", Some(XMLNS)),
|
||||||
|
Error::UndefinedCondition => Element::builder("undefined-condition", Some(XMLNS)),
|
||||||
|
Error::UnsupportedEncoding => Element::builder("unsupported-encoding", Some(XMLNS)),
|
||||||
|
Error::UnsupportedStanzaType => {
|
||||||
|
Element::builder("unsupported-stanza-type", Some(XMLNS))
|
||||||
|
}
|
||||||
|
Error::UnsupportedVersion => Element::builder("unsupported-version", Some(XMLNS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Text {
|
||||||
|
text: Option<String>,
|
||||||
|
lang: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromElement for Text {
|
||||||
|
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||||
|
element.check_name("text")?;
|
||||||
|
element.check_name(XMLNS)?;
|
||||||
|
|
||||||
|
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||||
|
let text = element.pop_value_opt()?;
|
||||||
|
|
||||||
|
Ok(Text { lang, text })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoElement for Text {
|
||||||
|
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||||
|
Element::builder("text", Some(XMLNS))
|
||||||
|
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||||
|
.push_text_opt(self.text.clone())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue