mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
Compare commits
5 Commits
9180ac91e6
...
83dca24e13
Author | SHA1 | Date |
---|---|---|
Jonas Schäfer | 83dca24e13 | |
Jonas Schäfer | 384b366f5f | |
Lucas Kent | a291ab2e83 | |
Jonas Schäfer | b5de6cb82c | |
Jonas Schäfer | ed0a1cd8cf |
|
@ -14,7 +14,6 @@ use crate::ns;
|
|||
use crate::util::error::Error;
|
||||
use crate::Element;
|
||||
use jid::Jid;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -461,8 +460,6 @@ impl From<Reason> for Element {
|
|||
}
|
||||
}
|
||||
|
||||
type Lang = String;
|
||||
|
||||
/// Informs the recipient of something.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ReasonElement {
|
||||
|
@ -470,15 +467,18 @@ pub struct ReasonElement {
|
|||
pub reason: Reason,
|
||||
|
||||
/// A human-readable description of this reason.
|
||||
pub texts: BTreeMap<Lang, String>,
|
||||
pub text: Option<String>,
|
||||
|
||||
/// A machine-readable extension of the reason.
|
||||
// using a box here to save a significant amount of bytes in the common
|
||||
// (absent) case.
|
||||
pub other: Option<Box<Element>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ReasonElement {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", Element::from(self.reason.clone()).name())?;
|
||||
if let Some(text) = self.texts.get("en") {
|
||||
write!(fmt, ": {}", text)?;
|
||||
} else if let Some(text) = self.texts.get("") {
|
||||
if let Some(text) = self.text.as_ref() {
|
||||
write!(fmt, ": {}", text)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -492,17 +492,16 @@ impl TryFrom<Element> for ReasonElement {
|
|||
check_self!(elem, "reason", JINGLE);
|
||||
check_no_attributes!(elem, "reason");
|
||||
let mut reason = None;
|
||||
let mut texts = BTreeMap::new();
|
||||
let mut text = None;
|
||||
let mut other = None;
|
||||
for child in elem.children() {
|
||||
if child.is("text", ns::JINGLE) {
|
||||
check_no_children!(child, "text");
|
||||
check_no_unknown_attributes!(child, "text", ["xml:lang"]);
|
||||
let lang = get_attr!(elem, "xml:lang", Default);
|
||||
if texts.insert(lang, child.text()).is_some() {
|
||||
return Err(Error::ParseError(
|
||||
"Text element present twice for the same xml:lang.",
|
||||
));
|
||||
if text.is_some() {
|
||||
return Err(Error::ParseError("Multiple reason texts."));
|
||||
}
|
||||
text = Some(child.text());
|
||||
} else if child.has_ns(ns::JINGLE) {
|
||||
if reason.is_some() {
|
||||
return Err(Error::ParseError(
|
||||
|
@ -512,12 +511,18 @@ impl TryFrom<Element> for ReasonElement {
|
|||
check_no_children!(child, "reason");
|
||||
check_no_attributes!(child, "reason");
|
||||
reason = Some(child.name().parse()?);
|
||||
} else if other.is_none() {
|
||||
other = Some(Box::new(child.clone()));
|
||||
} else {
|
||||
return Err(Error::ParseError("Reason contains a foreign element."));
|
||||
}
|
||||
}
|
||||
let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?;
|
||||
Ok(ReasonElement { reason, texts })
|
||||
Ok(ReasonElement {
|
||||
reason,
|
||||
text,
|
||||
other,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,11 +530,13 @@ impl From<ReasonElement> for Element {
|
|||
fn from(reason: ReasonElement) -> Element {
|
||||
Element::builder("reason", ns::JINGLE)
|
||||
.append(Element::from(reason.reason))
|
||||
.append_all(reason.texts.into_iter().map(|(lang, text)| {
|
||||
Element::builder("text", ns::JINGLE)
|
||||
.attr("xml:lang", lang)
|
||||
.append(text)
|
||||
}))
|
||||
.append_all(
|
||||
reason
|
||||
.text
|
||||
.into_iter()
|
||||
.map(|text| Element::builder("text", ns::JINGLE).append(text)),
|
||||
)
|
||||
.append_all(reason.other.into_iter().map(|other| *other))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@ -690,9 +697,9 @@ mod tests {
|
|||
assert_size!(ContentId, 12);
|
||||
assert_size!(Content, 216);
|
||||
assert_size!(Reason, 1);
|
||||
assert_size!(ReasonElement, 16);
|
||||
assert_size!(ReasonElement, 20);
|
||||
assert_size!(SessionId, 12);
|
||||
assert_size!(Jingle, 104);
|
||||
assert_size!(Jingle, 108);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
@ -709,9 +716,9 @@ mod tests {
|
|||
#[cfg(feature = "stable")]
|
||||
assert_size!(Content, 440);
|
||||
assert_size!(Reason, 1);
|
||||
assert_size!(ReasonElement, 32);
|
||||
assert_size!(ReasonElement, 40);
|
||||
assert_size!(SessionId, 24);
|
||||
assert_size!(Jingle, 208);
|
||||
assert_size!(Jingle, 216);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -823,13 +830,13 @@ mod tests {
|
|||
let jingle = Jingle::try_from(elem).unwrap();
|
||||
let reason = jingle.reason.unwrap();
|
||||
assert_eq!(reason.reason, Reason::Success);
|
||||
assert_eq!(reason.texts, BTreeMap::new());
|
||||
assert_eq!(reason.text, None);
|
||||
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><success/><text>coucou</text></reason></jingle>".parse().unwrap();
|
||||
let jingle = Jingle::try_from(elem).unwrap();
|
||||
let reason = jingle.reason.unwrap();
|
||||
assert_eq!(reason.reason, Reason::Success);
|
||||
assert_eq!(reason.texts.get(""), Some(&String::from("coucou")));
|
||||
assert_eq!(reason.text, Some(String::from("coucou")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -856,7 +863,7 @@ mod tests {
|
|||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Reason contains a foreign element.");
|
||||
assert_eq!(message, "Reason doesn’t contain a valid reason.");
|
||||
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/></reason><reason/></jingle>".parse().unwrap();
|
||||
let error = Jingle::try_from(elem).unwrap_err();
|
||||
|
@ -872,7 +879,7 @@ mod tests {
|
|||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Text element present twice for the same xml:lang.");
|
||||
assert_eq!(message, "Multiple reason texts.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -204,6 +204,40 @@ impl Message {
|
|||
pub fn get_best_subject(&self, preferred_langs: Vec<&str>) -> Option<(Lang, &Subject)> {
|
||||
Message::get_best::<Subject>(&self.subjects, preferred_langs)
|
||||
}
|
||||
|
||||
/// Try to extract the given payload type from the message's payloads.
|
||||
///
|
||||
/// Returns the first matching payload element as parsed struct or its
|
||||
/// parse error. If no element matches, `Ok(None)` is returned. If an
|
||||
/// element matches, but fails to parse, it is nontheless removed from
|
||||
/// the message.
|
||||
///
|
||||
/// Elements which do not match the given type are not removed.
|
||||
pub fn extract_payload<T: TryFrom<Element, Error = Error>>(
|
||||
&mut self,
|
||||
) -> Result<Option<T>, Error> {
|
||||
let mut buf = Vec::with_capacity(self.payloads.len());
|
||||
let mut iter = self.payloads.drain(..);
|
||||
let mut result = Ok(None);
|
||||
for item in &mut iter {
|
||||
match T::try_from(item) {
|
||||
Ok(v) => {
|
||||
result = Ok(Some(v));
|
||||
break;
|
||||
}
|
||||
Err(Error::TypeMismatch(_, _, residual)) => {
|
||||
buf.push(residual);
|
||||
}
|
||||
Err(other) => {
|
||||
result = Err(other);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.extend(iter);
|
||||
std::mem::swap(&mut buf, &mut self.payloads);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Element> for Message {
|
||||
|
@ -460,4 +494,27 @@ mod tests {
|
|||
let elem2 = message.into();
|
||||
assert_eq!(elem1, elem2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_payload() {
|
||||
use super::super::attention::Attention;
|
||||
use super::super::pubsub::event::PubSubEvent;
|
||||
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><attention xmlns='urn:xmpp:attention:0'/></message>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<message xmlns='jabber:component:accept' to='coucou@example.org' type='chat'><attention xmlns='urn:xmpp:attention:0'/></message>".parse().unwrap();
|
||||
let mut message = Message::try_from(elem).unwrap();
|
||||
assert_eq!(message.payloads.len(), 1);
|
||||
match message.extract_payload::<PubSubEvent>() {
|
||||
Ok(None) => (),
|
||||
other => panic!("unexpected result: {:?}", other),
|
||||
};
|
||||
assert_eq!(message.payloads.len(), 1);
|
||||
match message.extract_payload::<Attention>() {
|
||||
Ok(Some(_)) => (),
|
||||
other => panic!("unexpected result: {:?}", other),
|
||||
};
|
||||
assert_eq!(message.payloads.len(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ impl<S: ScramProvider> Mechanism for Scram<S> {
|
|||
bare.extend(self.client_nonce.bytes());
|
||||
let mut data = Vec::new();
|
||||
data.extend(&gs2_header);
|
||||
data.extend(bare.clone());
|
||||
data.extend(&bare);
|
||||
self.state = ScramState::SentInitialMessage {
|
||||
initial_message: bare,
|
||||
gs2_header: gs2_header,
|
||||
|
|
Loading…
Reference in New Issue