implement bind
This commit is contained in:
parent
859a19820d
commit
be198ca15b
|
@ -2,6 +2,7 @@ use std::str::Utf8Error;
|
|||
|
||||
use rsasl::mechname::MechanismNameError;
|
||||
|
||||
use crate::stanza::client::error::Error as ClientError;
|
||||
use crate::{jid::ParseError, stanza::sasl::Failure};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -22,12 +23,14 @@ pub enum Error {
|
|||
Negotiation,
|
||||
TlsRequired,
|
||||
UnexpectedEnd,
|
||||
UnexpectedElement,
|
||||
UnexpectedElement(peanuts::Element),
|
||||
UnexpectedText,
|
||||
XML(peanuts::Error),
|
||||
SASL(SASLError),
|
||||
JID(ParseError),
|
||||
Authentication(Failure),
|
||||
ClientError(ClientError),
|
||||
MissingError,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
111
src/jabber.rs
111
src/jabber.rs
|
@ -13,6 +13,9 @@ use trust_dns_resolver::proto::rr::domain::IntoLabel;
|
|||
|
||||
use crate::connection::{Tls, Unencrypted};
|
||||
use crate::error::Error;
|
||||
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::sasl::{Auth, Challenge, Mechanisms, Response, ServerResponse};
|
||||
use crate::stanza::starttls::{Proceed, StartTls};
|
||||
use crate::stanza::stream::{Feature, Features, Stream};
|
||||
|
@ -147,7 +150,96 @@ where
|
|||
}
|
||||
|
||||
pub async fn bind(&mut self) -> Result<()> {
|
||||
todo!()
|
||||
let iq_id = nanoid::nanoid!();
|
||||
if let Some(resource) = self.jid.clone().unwrap().resourcepart {
|
||||
let iq = Iq {
|
||||
from: None,
|
||||
id: iq_id.clone(),
|
||||
to: None,
|
||||
r#type: IqType::Set,
|
||||
lang: None,
|
||||
query: Some(Query::Bind(Bind {
|
||||
r#type: Some(BindType::Resource(ResourceType(resource))),
|
||||
})),
|
||||
errors: Vec::new(),
|
||||
};
|
||||
self.writer.write_full(&iq).await?;
|
||||
let result: Iq = self.reader.read().await?;
|
||||
match result {
|
||||
Iq {
|
||||
from: _,
|
||||
id,
|
||||
to: _,
|
||||
r#type: IqType::Result,
|
||||
lang: _,
|
||||
query:
|
||||
Some(Query::Bind(Bind {
|
||||
r#type: Some(BindType::Jid(FullJidType(jid))),
|
||||
})),
|
||||
errors: _,
|
||||
} if id == iq_id => {
|
||||
self.jid = Some(jid);
|
||||
return Ok(());
|
||||
}
|
||||
Iq {
|
||||
from: _,
|
||||
id,
|
||||
to: _,
|
||||
r#type: IqType::Error,
|
||||
lang: _,
|
||||
query: None,
|
||||
errors,
|
||||
} if id == iq_id => {
|
||||
return Err(Error::ClientError(
|
||||
errors.first().ok_or(Error::MissingError)?.clone(),
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::UnexpectedElement(result.into_element())),
|
||||
}
|
||||
} else {
|
||||
let iq = Iq {
|
||||
from: None,
|
||||
id: iq_id.clone(),
|
||||
to: None,
|
||||
r#type: IqType::Set,
|
||||
lang: None,
|
||||
query: Some(Query::Bind(Bind { r#type: None })),
|
||||
errors: Vec::new(),
|
||||
};
|
||||
self.writer.write_full(&iq).await?;
|
||||
let result: Iq = self.reader.read().await?;
|
||||
match result {
|
||||
Iq {
|
||||
from: _,
|
||||
id,
|
||||
to: _,
|
||||
r#type: IqType::Result,
|
||||
lang: _,
|
||||
query:
|
||||
Some(Query::Bind(Bind {
|
||||
r#type: Some(BindType::Jid(FullJidType(jid))),
|
||||
})),
|
||||
errors: _,
|
||||
} if id == iq_id => {
|
||||
self.jid = Some(jid);
|
||||
return Ok(());
|
||||
}
|
||||
Iq {
|
||||
from: _,
|
||||
id,
|
||||
to: _,
|
||||
r#type: IqType::Error,
|
||||
lang: _,
|
||||
query: None,
|
||||
errors,
|
||||
} if id == iq_id => {
|
||||
return Err(Error::ClientError(
|
||||
errors.first().ok_or(Error::MissingError)?.clone(),
|
||||
))
|
||||
}
|
||||
_ => return Err(Error::UnexpectedElement(result.into_element())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
|
@ -324,9 +416,12 @@ impl std::fmt::Debug for Jabber<Unencrypted> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use super::*;
|
||||
use crate::connection::Connection;
|
||||
use test_log::test;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn start_stream() {
|
||||
|
@ -373,4 +468,18 @@ mod tests {
|
|||
Feature::Unknown => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn negotiate() {
|
||||
let jabber = Connection::connect_user("test@blos.sm", "slayed".to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.ensure_tls()
|
||||
.await
|
||||
.unwrap()
|
||||
.negotiate()
|
||||
.await
|
||||
.unwrap();
|
||||
sleep(Duration::from_secs(5)).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,99 @@
|
|||
use peanuts::{
|
||||
element::{FromElement, IntoElement},
|
||||
Element,
|
||||
};
|
||||
|
||||
use crate::JID;
|
||||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Bind {
|
||||
pub r#type: Option<BindType>,
|
||||
}
|
||||
|
||||
impl FromElement for Bind {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
element.check_name("bind");
|
||||
element.check_name(XMLNS);
|
||||
|
||||
let r#type = element.pop_child_opt()?;
|
||||
|
||||
Ok(Bind { r#type })
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Bind {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("bind", Some(XMLNS)).push_child_opt(self.r#type.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum BindType {
|
||||
Resource(ResourceType),
|
||||
Jid(FullJidType),
|
||||
}
|
||||
|
||||
impl FromElement for BindType {
|
||||
fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
match element.identify() {
|
||||
(Some(XMLNS), "resource") => {
|
||||
Ok(BindType::Resource(ResourceType::from_element(element)?))
|
||||
}
|
||||
(Some(XMLNS), "jid") => Ok(BindType::Jid(FullJidType::from_element(element)?)),
|
||||
_ => Err(peanuts::DeserializeError::UnexpectedElement(element)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for BindType {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
match self {
|
||||
BindType::Resource(resource_type) => resource_type.builder(),
|
||||
BindType::Jid(full_jid_type) => full_jid_type.builder(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// minLength 8 maxLength 3071
|
||||
#[derive(Clone)]
|
||||
pub struct FullJidType(pub JID);
|
||||
|
||||
impl FromElement for FullJidType {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
element.check_name("jid");
|
||||
element.check_namespace(XMLNS);
|
||||
|
||||
let jid = element.pop_value()?;
|
||||
|
||||
Ok(FullJidType(jid))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for FullJidType {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("jid", Some(XMLNS)).push_text(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// minLength 1 maxLength 1023
|
||||
#[derive(Clone)]
|
||||
pub struct ResourceType(pub String);
|
||||
|
||||
impl FromElement for ResourceType {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
element.check_name("resource")?;
|
||||
element.check_namespace(XMLNS)?;
|
||||
|
||||
let resource = element.pop_value()?;
|
||||
|
||||
Ok(ResourceType(resource))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for ResourceType {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("resource", Some(XMLNS)).push_text(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use peanuts::element::{FromElement, IntoElement};
|
||||
use peanuts::{DeserializeError, Element};
|
||||
|
||||
use crate::stanza::error::Text;
|
||||
use crate::stanza::Error as StanzaError;
|
||||
|
||||
use super::XMLNS;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Error {
|
||||
by: Option<String>,
|
||||
r#type: ErrorType,
|
||||
// children (sequence)
|
||||
error: StanzaError,
|
||||
text: Option<Text>,
|
||||
}
|
||||
|
||||
impl FromElement for Error {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
element.check_name("error")?;
|
||||
element.check_name(XMLNS)?;
|
||||
|
||||
let by = element.attribute_opt("by")?;
|
||||
let r#type = element.attribute("type")?;
|
||||
let error = element.pop_child_one()?;
|
||||
let text = element.pop_child_opt()?;
|
||||
|
||||
Ok(Error {
|
||||
by,
|
||||
r#type,
|
||||
error,
|
||||
text,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Error {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("error", Some(XMLNS))
|
||||
.push_attribute_opt("by", self.by.clone())
|
||||
.push_attribute("type", self.r#type)
|
||||
.push_child(self.error.clone())
|
||||
.push_child_opt(self.text.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ErrorType {
|
||||
Auth,
|
||||
Cancel,
|
||||
Continue,
|
||||
Modify,
|
||||
Wait,
|
||||
}
|
||||
|
||||
impl FromStr for ErrorType {
|
||||
type Err = DeserializeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"auth" => Ok(ErrorType::Auth),
|
||||
"cancel" => Ok(ErrorType::Cancel),
|
||||
"continue" => Ok(ErrorType::Continue),
|
||||
"modify" => Ok(ErrorType::Modify),
|
||||
"wait" => Ok(ErrorType::Wait),
|
||||
_ => Err(DeserializeError::FromStr(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ErrorType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ErrorType::Auth => "auth".to_string(),
|
||||
ErrorType::Cancel => "cancel".to_string(),
|
||||
ErrorType::Continue => "continue".to_string(),
|
||||
ErrorType::Modify => "modify".to_string(),
|
||||
ErrorType::Wait => "wait".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use peanuts::{
|
||||
element::{FromElement, IntoElement},
|
||||
DeserializeError, Element, XML_NS,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
stanza::{
|
||||
bind::{self, Bind},
|
||||
client::error::Error,
|
||||
},
|
||||
JID,
|
||||
};
|
||||
|
||||
use super::XMLNS;
|
||||
|
||||
pub struct Iq {
|
||||
pub from: Option<JID>,
|
||||
pub id: String,
|
||||
pub to: Option<JID>,
|
||||
pub r#type: IqType,
|
||||
pub lang: Option<String>,
|
||||
// children
|
||||
// ##other
|
||||
pub query: Option<Query>,
|
||||
pub errors: Vec<Error>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Query {
|
||||
Bind(Bind),
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl FromElement for Query {
|
||||
fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
match element.identify() {
|
||||
(Some(bind::XMLNS), "bind") => Ok(Query::Bind(Bind::from_element(element)?)),
|
||||
_ => Ok(Query::Unsupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Query {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
match self {
|
||||
Query::Bind(bind) => bind.builder(),
|
||||
// TODO: consider what to do if attempt to serialize unsupported
|
||||
Query::Unsupported => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromElement for Iq {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
element.check_name("iq")?;
|
||||
element.check_namespace(XMLNS)?;
|
||||
|
||||
let from = element.attribute_opt("from")?;
|
||||
let id = element.attribute("id")?;
|
||||
let to = element.attribute_opt("to")?;
|
||||
let r#type = element.attribute("type")?;
|
||||
let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
|
||||
let query = element.pop_child_opt()?;
|
||||
let errors = element.pop_children()?;
|
||||
|
||||
Ok(Iq {
|
||||
from,
|
||||
id,
|
||||
to,
|
||||
r#type,
|
||||
lang,
|
||||
query,
|
||||
errors,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Iq {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("iq", Some(XMLNS))
|
||||
.push_attribute_opt("from", self.from.clone())
|
||||
.push_attribute("id", self.id.clone())
|
||||
.push_attribute_opt("to", self.to.clone())
|
||||
.push_attribute("type", self.r#type)
|
||||
.push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
|
||||
.push_child_opt(self.query.clone())
|
||||
.push_children(self.errors.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum IqType {
|
||||
Error,
|
||||
Get,
|
||||
Result,
|
||||
Set,
|
||||
}
|
||||
|
||||
impl FromStr for IqType {
|
||||
type Err = DeserializeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"error" => Ok(IqType::Error),
|
||||
"get" => Ok(IqType::Get),
|
||||
"result" => Ok(IqType::Result),
|
||||
"set" => Ok(IqType::Set),
|
||||
_ => Err(DeserializeError::FromStr(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IqType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
IqType::Error => "error".to_string(),
|
||||
IqType::Get => "get".to_string(),
|
||||
IqType::Result => "result".to_string(),
|
||||
IqType::Set => "set".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use crate::JID;
|
||||
|
||||
pub struct Message {
|
||||
from: Option<JID>,
|
||||
id: Option<String>,
|
||||
to: Option<JID>,
|
||||
r#type: Option<MessageType>,
|
||||
// children
|
||||
subject: Option<Subject>,
|
||||
body: Option<Body>,
|
||||
thread: Option<Thread>,
|
||||
lang: Option<String>,
|
||||
}
|
||||
|
||||
pub enum MessageType {
|
||||
Chat,
|
||||
Error,
|
||||
Groupchat,
|
||||
Headline,
|
||||
Normal,
|
||||
}
|
||||
|
||||
pub struct Body {
|
||||
lang: Option<String>,
|
||||
body: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Subject {
|
||||
lang: Option<String>,
|
||||
subject: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Thread {
|
||||
// TODO: NOT DONE
|
||||
parent: Option<String>,
|
||||
thread: Option<String>,
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
pub mod error;
|
||||
pub mod iq;
|
||||
pub mod message;
|
||||
pub mod presence;
|
||||
|
||||
pub const XMLNS: &str = "jabber:client";
|
|
@ -0,0 +1,48 @@
|
|||
use peanuts::element::{FromElement, IntoElement};
|
||||
|
||||
use crate::JID;
|
||||
|
||||
use super::error::Error;
|
||||
|
||||
pub struct Presence {
|
||||
from: Option<JID>,
|
||||
id: Option<String>,
|
||||
to: Option<JID>,
|
||||
r#type: PresenceType,
|
||||
lang: Option<String>,
|
||||
// children
|
||||
show: Option<Show>,
|
||||
status: Option<Status>,
|
||||
priority: Option<Priority>,
|
||||
errors: Vec<Error>,
|
||||
// ##other
|
||||
// content: Vec<Box<dyn AsElement>>,
|
||||
}
|
||||
|
||||
pub enum PresenceType {
|
||||
Error,
|
||||
Probe,
|
||||
Subscribe,
|
||||
Subscribed,
|
||||
Unavailable,
|
||||
Unsubscribe,
|
||||
Unsubscribed,
|
||||
}
|
||||
|
||||
pub enum Show {
|
||||
Away,
|
||||
Chat,
|
||||
Dnd,
|
||||
Xa,
|
||||
}
|
||||
|
||||
pub struct Status {
|
||||
lang: Option<String>,
|
||||
status: String1024,
|
||||
}
|
||||
|
||||
// minLength 1 maxLength 1024
|
||||
pub struct String1024(String);
|
||||
|
||||
// xs:byte
|
||||
pub struct Priority(u8);
|
|
@ -0,0 +1,126 @@
|
|||
// https://datatracker.ietf.org/doc/html/rfc6120#appendix-A.8
|
||||
|
||||
use peanuts::{
|
||||
element::{FromElement, IntoElement},
|
||||
Element, XML_NS,
|
||||
};
|
||||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-stanzas";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
BadRequest,
|
||||
Conflict,
|
||||
FeatureNotImplemented,
|
||||
Forbidden,
|
||||
Gone(Option<String>),
|
||||
InternalServerError,
|
||||
ItemNotFound,
|
||||
JidMalformed,
|
||||
NotAcceptable,
|
||||
NotAllowed,
|
||||
NotAuthorized,
|
||||
PolicyViolation,
|
||||
RecipientUnavailable,
|
||||
Redirect(Option<String>),
|
||||
RegistrationRequired,
|
||||
RemoteServerNotFound,
|
||||
RemoteServerTimeout,
|
||||
ResourceConstraint,
|
||||
ServiceUnavailable,
|
||||
SubscriptionRequired,
|
||||
UndefinedCondition,
|
||||
UnexpectedRequest,
|
||||
}
|
||||
|
||||
impl FromElement for Error {
|
||||
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
|
||||
let error;
|
||||
match element.identify() {
|
||||
(Some(XMLNS), "bad-request") => error = Error::BadRequest,
|
||||
(Some(XMLNS), "conflict") => error = Error::Conflict,
|
||||
(Some(XMLNS), "feature-not-implemented") => error = Error::FeatureNotImplemented,
|
||||
(Some(XMLNS), "forbidden") => error = Error::Forbidden,
|
||||
(Some(XMLNS), "gone") => return Ok(Error::Gone(element.pop_value_opt()?)),
|
||||
(Some(XMLNS), "internal-server-error") => error = Error::InternalServerError,
|
||||
(Some(XMLNS), "item-not-found") => error = Error::ItemNotFound,
|
||||
(Some(XMLNS), "jid-malformed") => error = Error::JidMalformed,
|
||||
(Some(XMLNS), "not-acceptable") => error = Error::NotAcceptable,
|
||||
(Some(XMLNS), "not-allowed") => error = Error::NotAllowed,
|
||||
(Some(XMLNS), "not-authorized") => error = Error::NotAuthorized,
|
||||
(Some(XMLNS), "policy-violation") => error = Error::PolicyViolation,
|
||||
(Some(XMLNS), "recipient-unavailable") => error = Error::RecipientUnavailable,
|
||||
(Some(XMLNS), "redirect") => return Ok(Error::Redirect(element.pop_value_opt()?)),
|
||||
(Some(XMLNS), "registration-required") => error = Error::RegistrationRequired,
|
||||
(Some(XMLNS), "remote-server-not-found") => error = Error::RemoteServerNotFound,
|
||||
(Some(XMLNS), "remote-server-timeout") => error = Error::RemoteServerTimeout,
|
||||
(Some(XMLNS), "resource-constraint") => error = Error::ResourceConstraint,
|
||||
(Some(XMLNS), "service-unavailable") => error = Error::ServiceUnavailable,
|
||||
(Some(XMLNS), "subscription-required") => error = Error::SubscriptionRequired,
|
||||
(Some(XMLNS), "undefined-condition") => error = Error::UndefinedCondition,
|
||||
(Some(XMLNS), "unexpected-request") => error = Error::UnexpectedRequest,
|
||||
_ => return Err(peanuts::DeserializeError::UnexpectedElement(element)),
|
||||
}
|
||||
element.no_more_content()?;
|
||||
return Ok(error);
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Error {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
match self {
|
||||
Error::BadRequest => Element::builder("bad-request", Some(XMLNS)),
|
||||
Error::Conflict => Element::builder("conflict", Some(XMLNS)),
|
||||
Error::FeatureNotImplemented => {
|
||||
Element::builder("feature-not-implemented", Some(XMLNS))
|
||||
}
|
||||
Error::Forbidden => Element::builder("forbidden", Some(XMLNS)),
|
||||
Error::Gone(r) => Element::builder("gone", Some(XMLNS)).push_text_opt(r.clone()),
|
||||
Error::InternalServerError => Element::builder("internal-server-error", Some(XMLNS)),
|
||||
Error::ItemNotFound => Element::builder("item-not-found", Some(XMLNS)),
|
||||
Error::JidMalformed => Element::builder("jid-malformed", Some(XMLNS)),
|
||||
Error::NotAcceptable => Element::builder("not-acceptable", Some(XMLNS)),
|
||||
Error::NotAllowed => Element::builder("not-allowed", Some(XMLNS)),
|
||||
Error::NotAuthorized => Element::builder("not-authorized", Some(XMLNS)),
|
||||
Error::PolicyViolation => Element::builder("policy-violation", Some(XMLNS)),
|
||||
Error::RecipientUnavailable => Element::builder("recipient-unavailable", Some(XMLNS)),
|
||||
Error::Redirect(r) => {
|
||||
Element::builder("redirect", Some(XMLNS)).push_text_opt(r.clone())
|
||||
}
|
||||
Error::RegistrationRequired => Element::builder("registration-required", Some(XMLNS)),
|
||||
Error::RemoteServerNotFound => Element::builder("remote-server-not-found", Some(XMLNS)),
|
||||
Error::RemoteServerTimeout => Element::builder("remote-server-timeout", Some(XMLNS)),
|
||||
Error::ResourceConstraint => Element::builder("resource-constraint", Some(XMLNS)),
|
||||
Error::ServiceUnavailable => Element::builder("service-unavailable", Some(XMLNS)),
|
||||
Error::SubscriptionRequired => Element::builder("subscription-required", Some(XMLNS)),
|
||||
Error::UndefinedCondition => Element::builder("undefined-condition", Some(XMLNS)),
|
||||
Error::UnexpectedRequest => Element::builder("unexpected-request", Some(XMLNS)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Text {
|
||||
lang: Option<String>,
|
||||
text: 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())
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use peanuts::declaration::VersionInfo;
|
||||
|
||||
pub mod bind;
|
||||
pub mod iq;
|
||||
pub mod message;
|
||||
pub mod presence;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod sasl;
|
||||
pub mod starttls;
|
||||
pub mod stream;
|
||||
|
||||
pub static XML_VERSION: VersionInfo = VersionInfo::One;
|
||||
|
||||
pub use error::Error;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -17,7 +17,7 @@ impl IntoElement for StartTls {
|
|||
let mut builder = Element::builder("starttls", Some(XMLNS));
|
||||
|
||||
if self.required {
|
||||
builder = builder.push_child(Element::builder("required", Some(XMLNS)))
|
||||
builder = builder.push_child(Required)
|
||||
}
|
||||
|
||||
builder
|
||||
|
@ -52,6 +52,12 @@ impl FromElement for Required {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Required {
|
||||
fn builder(&self) -> peanuts::element::ElementBuilder {
|
||||
Element::builder("required", Some(XMLNS))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Proceed;
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@ use peanuts::XML_NS;
|
|||
use peanuts::{element::Name, Element};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::stanza::bind;
|
||||
use crate::{Error, JID};
|
||||
|
||||
use super::client;
|
||||
use super::sasl::{self, Mechanisms};
|
||||
use super::starttls::{self, StartTls};
|
||||
|
||||
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
|
||||
pub const XMLNS_CLIENT: &str = "jabber:client";
|
||||
|
||||
// MUST be qualified by stream namespace
|
||||
// #[derive(XmlSerialize, XmlDeserialize)]
|
||||
|
@ -53,7 +54,7 @@ impl IntoElement for Stream {
|
|||
fn builder(&self) -> ElementBuilder {
|
||||
Element::builder("stream", Some(XMLNS.to_string()))
|
||||
.push_namespace_declaration_override(Some("stream"), XMLNS)
|
||||
.push_namespace_declaration_override(None::<&str>, XMLNS_CLIENT)
|
||||
.push_namespace_declaration_override(None::<&str>, client::XMLNS)
|
||||
.push_attribute_opt("to", self.to.clone())
|
||||
.push_attribute_opt("from", self.from.clone())
|
||||
.push_attribute_opt("id", self.id.clone())
|
||||
|
@ -150,6 +151,10 @@ impl FromElement for Feature {
|
|||
debug!("identified mechanisms");
|
||||
Ok(Feature::Sasl(Mechanisms::from_element(element)?))
|
||||
}
|
||||
(Some(bind::XMLNS), "bind") => {
|
||||
debug!("identified bind");
|
||||
Ok(Feature::Bind)
|
||||
}
|
||||
_ => {
|
||||
debug!("identified unknown feature");
|
||||
Ok(Feature::Unknown)
|
||||
|
|
Loading…
Reference in New Issue