mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-06-29 01:48:42 +02:00
Run cargo fmt
.
This commit is contained in:
parent
d517b8e32e
commit
efd7bd5f2f
|
@ -8,7 +8,9 @@ use crate::message::MessagePayload;
|
|||
|
||||
generate_empty_element!(
|
||||
/// Requests the attention of the recipient.
|
||||
Attention, "attention", ATTENTION
|
||||
Attention,
|
||||
"attention",
|
||||
ATTENTION
|
||||
);
|
||||
|
||||
impl MessagePayload for Attention {}
|
||||
|
@ -16,9 +18,9 @@ impl MessagePayload for Attention {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
|
@ -33,7 +35,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>".parse().unwrap();
|
||||
let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Attention::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -44,7 +48,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Attention::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
43
src/bind.rs
43
src/bind.rs
|
@ -4,16 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::iq::{IqResultPayload, IqSetPayload};
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
use jid::Jid;
|
||||
use crate::ns;
|
||||
use crate::iq::{IqSetPayload, IqResultPayload};
|
||||
|
||||
/// The request for resource binding, which is the process by which a client
|
||||
/// can obtain a full JID and start exchanging on the XMPP network.
|
||||
///
|
||||
|
@ -74,23 +72,16 @@ impl TryFrom<Element> for Bind {
|
|||
impl From<Bind> for Element {
|
||||
fn from(bind: Bind) -> Element {
|
||||
Element::builder("bind")
|
||||
.ns(ns::BIND)
|
||||
.append(match bind {
|
||||
Bind::None => vec!(),
|
||||
Bind::Resource(resource) => vec!(
|
||||
Element::builder("resource")
|
||||
.ns(ns::BIND)
|
||||
.append(resource)
|
||||
.build()
|
||||
),
|
||||
Bind::Jid(jid) => vec!(
|
||||
Element::builder("jid")
|
||||
.ns(ns::BIND)
|
||||
.append(jid)
|
||||
.build()
|
||||
),
|
||||
})
|
||||
.build()
|
||||
.ns(ns::BIND)
|
||||
.append(match bind {
|
||||
Bind::None => vec![],
|
||||
Bind::Resource(resource) => vec![Element::builder("resource")
|
||||
.ns(ns::BIND)
|
||||
.append(resource)
|
||||
.build()],
|
||||
Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()],
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +103,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>".parse().unwrap();
|
||||
let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let bind = Bind::try_from(elem).unwrap();
|
||||
assert_eq!(bind, Bind::None);
|
||||
}
|
||||
|
|
|
@ -4,20 +4,19 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_empty_element!(
|
||||
/// The element requesting the blocklist, the result iq will contain a
|
||||
/// [BlocklistResult].
|
||||
BlocklistRequest, "blocklist", BLOCKING
|
||||
BlocklistRequest,
|
||||
"blocklist",
|
||||
BLOCKING
|
||||
);
|
||||
|
||||
impl IqGetPayload for BlocklistRequest {}
|
||||
|
@ -67,7 +66,8 @@ macro_rules! generate_blocking_element {
|
|||
generate_blocking_element!(
|
||||
/// The element containing the current blocklist, as a reply from
|
||||
/// [BlocklistRequest].
|
||||
BlocklistResult, "blocklist"
|
||||
BlocklistResult,
|
||||
"blocklist"
|
||||
);
|
||||
|
||||
impl IqResultPayload for BlocklistResult {}
|
||||
|
@ -75,7 +75,8 @@ impl IqResultPayload for BlocklistResult {}
|
|||
// TODO: Prevent zero elements from being allowed.
|
||||
generate_blocking_element!(
|
||||
/// A query to block one or more JIDs.
|
||||
Block, "block"
|
||||
Block,
|
||||
"block"
|
||||
);
|
||||
|
||||
impl IqSetPayload for Block {}
|
||||
|
@ -84,14 +85,17 @@ generate_blocking_element!(
|
|||
/// A query to unblock one or more JIDs, or all of them.
|
||||
///
|
||||
/// Warning: not putting any JID there means clearing out the blocklist.
|
||||
Unblock, "unblock"
|
||||
Unblock,
|
||||
"unblock"
|
||||
);
|
||||
|
||||
impl IqSetPayload for Unblock {}
|
||||
|
||||
generate_empty_element!(
|
||||
/// The application-specific error condition when a message is blocked.
|
||||
Blocked, "blocked", BLOCKING_ERRORS
|
||||
Blocked,
|
||||
"blocked",
|
||||
BLOCKING_ERRORS
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -138,7 +142,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_items() {
|
||||
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
|
||||
let two_items = vec!(
|
||||
let two_items = vec![
|
||||
Jid {
|
||||
node: Some(String::from("coucou")),
|
||||
domain: String::from("coucou"),
|
||||
|
@ -149,7 +153,7 @@ mod tests {
|
|||
domain: String::from("domain"),
|
||||
resource: None,
|
||||
},
|
||||
);
|
||||
];
|
||||
|
||||
let request_elem = elem.clone();
|
||||
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
||||
|
@ -174,7 +178,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let request_elem = elem.clone();
|
||||
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
||||
let message = match error {
|
||||
|
@ -191,7 +197,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Unknown attribute in blocklist element.");
|
||||
|
||||
let elem: Element = "<block xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<block xmlns='urn:xmpp:blocking' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Block::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -199,7 +207,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Unknown attribute in block element.");
|
||||
|
||||
let elem: Element = "<unblock xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<unblock xmlns='urn:xmpp:blocking' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Unblock::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -8,7 +8,9 @@ use jid::Jid;
|
|||
|
||||
generate_attribute!(
|
||||
/// Whether a conference bookmark should be joined automatically.
|
||||
Autojoin, "autojoin", bool
|
||||
Autojoin,
|
||||
"autojoin",
|
||||
bool
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -70,9 +72,9 @@ impl Storage {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -111,7 +113,10 @@ mod tests {
|
|||
assert_eq!(storage.urls[0].url, "https://example.org/");
|
||||
assert_eq!(storage.conferences.len(), 1);
|
||||
assert_eq!(storage.conferences[0].autojoin, Autojoin::True);
|
||||
assert_eq!(storage.conferences[0].jid, Jid::bare("test-muc", "muc.localhost"));
|
||||
assert_eq!(
|
||||
storage.conferences[0].jid,
|
||||
Jid::bare("test-muc", "muc.localhost")
|
||||
);
|
||||
assert_eq!(storage.conferences[0].name, "Test MUC");
|
||||
assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou");
|
||||
assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret");
|
||||
|
|
79
src/caps.rs
79
src/caps.rs
|
@ -4,23 +4,20 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery};
|
||||
use crate::data_forms::DataForm;
|
||||
use crate::hashes::{Hash, Algo};
|
||||
|
||||
use minidom::Element;
|
||||
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
|
||||
use crate::error::Error;
|
||||
use crate::hashes::{Algo, Hash};
|
||||
use crate::ns;
|
||||
use crate::presence::PresencePayload;
|
||||
use base64;
|
||||
|
||||
use blake2::VarBlake2b;
|
||||
use digest::{Digest, Input, VariableOutput};
|
||||
use minidom::Element;
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha256, Sha512};
|
||||
use sha3::{Sha3_256, Sha3_512};
|
||||
use blake2::VarBlake2b;
|
||||
use digest::{Digest, VariableOutput, Input};
|
||||
use try_from::TryFrom;
|
||||
|
||||
/// Represents a capability hash for a given client.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -65,12 +62,12 @@ impl TryFrom<Element> for Caps {
|
|||
impl From<Caps> for Element {
|
||||
fn from(caps: Caps) -> Element {
|
||||
Element::builder("c")
|
||||
.ns(ns::CAPS)
|
||||
.attr("ext", caps.ext)
|
||||
.attr("hash", caps.hash.algo)
|
||||
.attr("node", caps.node)
|
||||
.attr("ver", base64::encode(&caps.hash.hash))
|
||||
.build()
|
||||
.ns(ns::CAPS)
|
||||
.attr("ext", caps.ext)
|
||||
.attr("hash", caps.hash.algo)
|
||||
.attr("node", caps.node)
|
||||
.attr("ver", base64::encode(&caps.hash.hash))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +78,8 @@ fn compute_item(field: &str) -> Vec<u8> {
|
|||
}
|
||||
|
||||
fn compute_items<T, F: Fn(&T) -> Vec<u8>>(things: &[T], encode: F) -> Vec<u8> {
|
||||
let mut string: Vec<u8> = vec!();
|
||||
let mut accumulator: Vec<Vec<u8>> = vec!();
|
||||
let mut string: Vec<u8> = vec![];
|
||||
let mut accumulator: Vec<Vec<u8>> = vec![];
|
||||
for thing in things {
|
||||
let bytes = encode(thing);
|
||||
accumulator.push(bytes);
|
||||
|
@ -114,7 +111,7 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
|
|||
|
||||
fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
||||
compute_items(extensions, |extension| {
|
||||
let mut bytes = vec!();
|
||||
let mut bytes = vec![];
|
||||
// TODO: maybe handle the error case?
|
||||
if let Some(ref form_type) = extension.form_type {
|
||||
bytes.extend_from_slice(form_type.as_bytes());
|
||||
|
@ -125,8 +122,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
|||
continue;
|
||||
}
|
||||
bytes.append(&mut compute_item(&field.var));
|
||||
bytes.append(&mut compute_items(&field.values,
|
||||
|value| compute_item(value)));
|
||||
bytes.append(&mut compute_items(&field.values, |value| {
|
||||
compute_item(value)
|
||||
}));
|
||||
}
|
||||
bytes
|
||||
})
|
||||
|
@ -143,7 +141,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
|
|||
let features_string = compute_features(&disco.features);
|
||||
let extensions_string = compute_extensions(&disco.extensions);
|
||||
|
||||
let mut final_string = vec!();
|
||||
let mut final_string = vec![];
|
||||
final_string.extend(identities_string);
|
||||
final_string.extend(features_string);
|
||||
final_string.extend(extensions_string);
|
||||
|
@ -164,33 +162,33 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result<Hash, String> {
|
|||
Algo::Sha_1 => {
|
||||
let hash = Sha1::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha_256 => {
|
||||
let hash = Sha256::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha_512 => {
|
||||
let hash = Sha512::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha3_256 => {
|
||||
let hash = Sha3_256::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha3_512 => {
|
||||
let hash = Sha3_512::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Blake2b_256 => {
|
||||
let mut hasher = VarBlake2b::new(32).unwrap();
|
||||
hasher.input(data);
|
||||
hasher.vec_result()
|
||||
},
|
||||
}
|
||||
Algo::Blake2b_512 => {
|
||||
let mut hasher = VarBlake2b::new(64).unwrap();
|
||||
hasher.input(data);
|
||||
hasher.vec_result()
|
||||
},
|
||||
}
|
||||
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
|
||||
},
|
||||
algo: algo,
|
||||
|
@ -229,7 +227,10 @@ mod tests {
|
|||
let caps = Caps::try_from(elem).unwrap();
|
||||
assert_eq!(caps.node, String::from("coucou"));
|
||||
assert_eq!(caps.hash.algo, Algo::Sha_256);
|
||||
assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap());
|
||||
assert_eq!(
|
||||
caps.hash.hash,
|
||||
base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -262,7 +263,9 @@ mod tests {
|
|||
<feature var='http://jabber.org/protocol/disco#items'/>
|
||||
<feature var='http://jabber.org/protocol/muc'/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let data = b"client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<";
|
||||
let mut expected = Vec::with_capacity(data.len());
|
||||
|
@ -272,7 +275,10 @@ mod tests {
|
|||
assert_eq!(caps, expected);
|
||||
|
||||
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
|
||||
assert_eq!(sha_1.hash, base64::decode("QgayPKawpkPSDYmwT/WM94uAlu0=").unwrap());
|
||||
assert_eq!(
|
||||
sha_1.hash,
|
||||
base64::decode("QgayPKawpkPSDYmwT/WM94uAlu0=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -308,7 +314,9 @@ mod tests {
|
|||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let data = b"client/pc/el/\xce\xa8 0.11<client/pc/en/Psi 0.11<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<urn:xmpp:dataforms:softwareinfo<ip_version<ipv4<ipv6<os<Mac<os_version<10.5.1<software<Psi<software_version<0.11<";
|
||||
let mut expected = Vec::with_capacity(data.len());
|
||||
expected.extend_from_slice(data);
|
||||
|
@ -317,6 +325,9 @@ mod tests {
|
|||
assert_eq!(caps, expected);
|
||||
|
||||
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
|
||||
assert_eq!(sha_1.hash, base64::decode("q07IKJEyjvHSyhy//CH0CxmKi8w=").unwrap());
|
||||
assert_eq!(
|
||||
sha_1.hash,
|
||||
base64::decode("q07IKJEyjvHSyhy//CH0CxmKi8w=").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,10 +32,10 @@ impl MessagePayload for ChatState {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
|
@ -44,13 +44,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<active xmlns='http://jabber.org/protocol/chatstates'/>".parse().unwrap();
|
||||
let elem: Element = "<active xmlns='http://jabber.org/protocol/chatstates'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
ChatState::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<coucou xmlns='http://jabber.org/protocol/chatstates'/>".parse().unwrap();
|
||||
let elem: Element = "<coucou xmlns='http://jabber.org/protocol/chatstates'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = ChatState::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -61,7 +65,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>".parse().unwrap();
|
||||
let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = ChatState::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -72,7 +78,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = ChatState::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use minidom::{Node, Element};
|
||||
use minidom::{Element, Node};
|
||||
|
||||
pub trait NamespaceAwareCompare {
|
||||
/// Namespace-aware comparison for tests
|
||||
|
@ -14,10 +14,10 @@ pub trait NamespaceAwareCompare {
|
|||
impl NamespaceAwareCompare for Node {
|
||||
fn compare_to(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(&Node::Element(ref elem1), &Node::Element(ref elem2)) =>
|
||||
Element::compare_to(elem1, elem2),
|
||||
(&Node::Text(ref text1), &Node::Text(ref text2)) =>
|
||||
text1 == text2,
|
||||
(&Node::Element(ref elem1), &Node::Element(ref elem2)) => {
|
||||
Element::compare_to(elem1, elem2)
|
||||
}
|
||||
(&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,21 @@ impl NamespaceAwareCompare for Node {
|
|||
|
||||
impl NamespaceAwareCompare for Element {
|
||||
fn compare_to(&self, other: &Self) -> bool {
|
||||
if self.name() == other.name() &&
|
||||
self.ns() == other.ns() &&
|
||||
self.attrs().eq(other.attrs())
|
||||
if self.name() == other.name() && self.ns() == other.ns() && self.attrs().eq(other.attrs())
|
||||
{
|
||||
let child_elems = self.children().count();
|
||||
let text_is_whitespace = self.texts()
|
||||
let text_is_whitespace = self
|
||||
.texts()
|
||||
.all(|text| text.chars().all(char::is_whitespace));
|
||||
if child_elems > 0 && text_is_whitespace {
|
||||
// Ignore all the whitespace text nodes
|
||||
self.children().zip(other.children())
|
||||
self.children()
|
||||
.zip(other.children())
|
||||
.all(|(node1, node2)| node1.compare_to(node2))
|
||||
} else {
|
||||
// Compare with text nodes
|
||||
self.nodes().zip(other.nodes())
|
||||
self.nodes()
|
||||
.zip(other.nodes())
|
||||
.all(|(node1, node2)| node1.compare_to(node2))
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::helpers::PlainText;
|
||||
use sha1::Sha1;
|
||||
use digest::Digest;
|
||||
use sha1::Sha1;
|
||||
|
||||
generate_element!(
|
||||
/// The main authentication mechanism for components.
|
||||
|
@ -25,9 +25,7 @@ generate_element!(
|
|||
impl Handshake {
|
||||
/// Creates a successful reply from a server.
|
||||
pub fn new() -> Handshake {
|
||||
Handshake {
|
||||
data: None,
|
||||
}
|
||||
Handshake { data: None }
|
||||
}
|
||||
|
||||
/// Creates an authentication request from the component.
|
||||
|
@ -44,8 +42,8 @@ impl Handshake {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -61,11 +59,15 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<handshake xmlns='jabber:component:accept'/>".parse().unwrap();
|
||||
let elem: Element = "<handshake xmlns='jabber:component:accept'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let handshake = Handshake::try_from(elem).unwrap();
|
||||
assert_eq!(handshake.data, None);
|
||||
|
||||
let elem: Element = "<handshake xmlns='jabber:component:accept'>Coucou</handshake>".parse().unwrap();
|
||||
let elem: Element = "<handshake xmlns='jabber:component:accept'>Coucou</handshake>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let handshake = Handshake::try_from(elem).unwrap();
|
||||
assert_eq!(handshake.data, Some(String::from("Coucou")));
|
||||
}
|
||||
|
@ -76,6 +78,9 @@ mod tests {
|
|||
assert_eq!(handshake.data, None);
|
||||
|
||||
let handshake = Handshake::from_password_and_stream_id("123456", "sid");
|
||||
assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e")));
|
||||
assert_eq!(
|
||||
handshake.data,
|
||||
Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
|
||||
use crate::media_element::MediaElement;
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_element!(
|
||||
/// Represents one of the possible values for a list- field.
|
||||
|
@ -98,8 +95,7 @@ pub struct Field {
|
|||
|
||||
impl Field {
|
||||
fn is_list(&self) -> bool {
|
||||
self.type_ == FieldType::ListSingle ||
|
||||
self.type_ == FieldType::ListMulti
|
||||
self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,9 +110,9 @@ impl TryFrom<Element> for Field {
|
|||
type_: get_attr!(elem, "type", default),
|
||||
label: get_attr!(elem, "label", optional),
|
||||
required: false,
|
||||
options: vec!(),
|
||||
values: vec!(),
|
||||
media: vec!(),
|
||||
options: vec![],
|
||||
values: vec![],
|
||||
media: vec![],
|
||||
};
|
||||
for element in elem.children() {
|
||||
if element.is("value", ns::DATA_FORMS) {
|
||||
|
@ -140,7 +136,9 @@ impl TryFrom<Element> for Field {
|
|||
let media_element = MediaElement::try_from(element.clone())?;
|
||||
field.media.push(media_element);
|
||||
} else {
|
||||
return Err(Error::ParseError("Field child isn’t a value, option or media element."));
|
||||
return Err(Error::ParseError(
|
||||
"Field child isn’t a value, option or media element.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(field)
|
||||
|
@ -150,17 +148,30 @@ impl TryFrom<Element> for Field {
|
|||
impl From<Field> for Element {
|
||||
fn from(field: Field) -> Element {
|
||||
Element::builder("field")
|
||||
.ns(ns::DATA_FORMS)
|
||||
.attr("var", field.var)
|
||||
.attr("type", field.type_)
|
||||
.attr("label", field.label)
|
||||
.append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None })
|
||||
.append(field.options)
|
||||
.append(field.values.into_iter().map(|value| {
|
||||
Element::builder("value").ns(ns::DATA_FORMS).append(value).build()
|
||||
}).collect::<Vec<_>>())
|
||||
.append(field.media)
|
||||
.build()
|
||||
.ns(ns::DATA_FORMS)
|
||||
.attr("var", field.var)
|
||||
.attr("type", field.type_)
|
||||
.attr("label", field.label)
|
||||
.append(if field.required {
|
||||
Some(Element::builder("required").ns(ns::DATA_FORMS).build())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.append(field.options)
|
||||
.append(
|
||||
field
|
||||
.values
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
Element::builder("value")
|
||||
.ns(ns::DATA_FORMS)
|
||||
.append(value)
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.append(field.media)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +226,7 @@ impl TryFrom<Element> for DataForm {
|
|||
form_type: None,
|
||||
title: None,
|
||||
instructions: None,
|
||||
fields: vec!(),
|
||||
fields: vec![],
|
||||
};
|
||||
for child in elem.children() {
|
||||
if child.is("title", ns::DATA_FORMS) {
|
||||
|
@ -227,7 +238,9 @@ impl TryFrom<Element> for DataForm {
|
|||
form.title = Some(child.text());
|
||||
} else if child.is("instructions", ns::DATA_FORMS) {
|
||||
if form.instructions.is_some() {
|
||||
return Err(Error::ParseError("More than one instructions in form element."));
|
||||
return Err(Error::ParseError(
|
||||
"More than one instructions in form element.",
|
||||
));
|
||||
}
|
||||
check_no_children!(child, "instructions");
|
||||
check_no_attributes!(child, "instructions");
|
||||
|
@ -255,12 +268,19 @@ impl TryFrom<Element> for DataForm {
|
|||
impl From<DataForm> for Element {
|
||||
fn from(form: DataForm) -> Element {
|
||||
Element::builder("x")
|
||||
.ns(ns::DATA_FORMS)
|
||||
.attr("type", form.type_)
|
||||
.append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)))
|
||||
.append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text)))
|
||||
.append(form.fields)
|
||||
.build()
|
||||
.ns(ns::DATA_FORMS)
|
||||
.attr("type", form.type_)
|
||||
.append(
|
||||
form.title
|
||||
.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)),
|
||||
)
|
||||
.append(form.instructions.map(|text| {
|
||||
Element::builder("instructions")
|
||||
.ns(ns::DATA_FORMS)
|
||||
.append(text)
|
||||
}))
|
||||
.append(form.fields)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,7 +338,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_wrong_child() {
|
||||
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>".parse().unwrap();
|
||||
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DataForm::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -329,12 +351,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn option() {
|
||||
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou !'><value>coucou</value></option>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<option xmlns='jabber:x:data' label='Coucou !'><value>coucou</value></option>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let option = Option_::try_from(elem).unwrap();
|
||||
assert_eq!(&option.label.unwrap(), "Coucou !");
|
||||
assert_eq!(&option.value, "coucou");
|
||||
|
||||
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou !'/>".parse().unwrap();
|
||||
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou !'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Option_::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -348,6 +375,9 @@ mod tests {
|
|||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Element option must not have more than one value child.");
|
||||
assert_eq!(
|
||||
message,
|
||||
"Element option must not have more than one value child."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
11
src/date.rs
11
src/date.rs
|
@ -4,12 +4,10 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use minidom::{IntoAttributeValue, IntoElements, ElementEmitter};
|
||||
use chrono::{DateTime as ChronoDateTime, FixedOffset};
|
||||
|
||||
use crate::error::Error;
|
||||
use chrono::{DateTime as ChronoDateTime, FixedOffset};
|
||||
use minidom::{ElementEmitter, IntoAttributeValue, IntoElements};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Implements the DateTime profile of XEP-0082, which represents a
|
||||
/// non-recurring moment in time, with an accuracy of seconds or fraction of
|
||||
|
@ -114,7 +112,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let date = DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap());
|
||||
let date =
|
||||
DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap());
|
||||
let attr = date.into_attribute_value();
|
||||
assert_eq!(attr, Some(String::from("2017-05-21T20:19:55+01:00")));
|
||||
}
|
||||
|
|
32
src/delay.rs
32
src/delay.rs
|
@ -4,14 +4,12 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::date::DateTime;
|
||||
use crate::helpers::PlainText;
|
||||
use crate::message::MessagePayload;
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::date::DateTime;
|
||||
|
||||
use jid::Jid;
|
||||
|
||||
use crate::helpers::PlainText;
|
||||
|
||||
generate_element!(
|
||||
/// Notes when and by whom a message got stored for later delivery.
|
||||
Delay, "delay", DELAY,
|
||||
|
@ -34,10 +32,10 @@ impl PresencePayload for Delay {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -53,16 +51,24 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<delay xmlns='urn:xmpp:delay' from='capulet.com' stamp='2002-09-10T23:08:25Z'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<delay xmlns='urn:xmpp:delay' from='capulet.com' stamp='2002-09-10T23:08:25Z'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let delay = Delay::try_from(elem).unwrap();
|
||||
assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap()));
|
||||
assert_eq!(delay.stamp, DateTime::from_str("2002-09-10T23:08:25Z").unwrap());
|
||||
assert_eq!(
|
||||
delay.stamp,
|
||||
DateTime::from_str("2002-09-10T23:08:25Z").unwrap()
|
||||
);
|
||||
assert_eq!(delay.data, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Delay::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -73,7 +79,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<delay xmlns='urn:xmpp:delay'><coucou/></delay>".parse().unwrap();
|
||||
let elem: Element = "<delay xmlns='urn:xmpp:delay'><coucou/></delay>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Delay::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -84,7 +92,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'/>".parse().unwrap();
|
||||
let elem: Element = "<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let delay = Delay {
|
||||
from: None,
|
||||
stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(),
|
||||
|
|
129
src/disco.rs
129
src/disco.rs
|
@ -4,16 +4,13 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
|
||||
use crate::iq::{IqGetPayload, IqResultPayload};
|
||||
use crate::data_forms::{DataForm, DataFormType};
|
||||
use crate::error::Error;
|
||||
use crate::iq::{IqGetPayload, IqResultPayload};
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_element!(
|
||||
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element.
|
||||
|
@ -59,16 +56,24 @@ impl TryFrom<Element> for Identity {
|
|||
fn try_from(elem: Element) -> Result<Identity, Error> {
|
||||
check_self!(elem, "identity", DISCO_INFO, "disco#info identity");
|
||||
check_no_children!(elem, "disco#info identity");
|
||||
check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]);
|
||||
check_no_unknown_attributes!(
|
||||
elem,
|
||||
"disco#info identity",
|
||||
["category", "type", "xml:lang", "name"]
|
||||
);
|
||||
|
||||
let category = get_attr!(elem, "category", required);
|
||||
if category == "" {
|
||||
return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
|
||||
return Err(Error::ParseError(
|
||||
"Identity must have a non-empty 'category' attribute.",
|
||||
));
|
||||
}
|
||||
|
||||
let type_ = get_attr!(elem, "type", required);
|
||||
if type_ == "" {
|
||||
return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
|
||||
return Err(Error::ParseError(
|
||||
"Identity must have a non-empty 'type' attribute.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Identity {
|
||||
|
@ -83,12 +88,12 @@ impl TryFrom<Element> for Identity {
|
|||
impl From<Identity> for Element {
|
||||
fn from(identity: Identity) -> Element {
|
||||
Element::builder("identity")
|
||||
.ns(ns::DISCO_INFO)
|
||||
.attr("category", identity.category)
|
||||
.attr("type", identity.type_)
|
||||
.attr("xml:lang", identity.lang)
|
||||
.attr("name", identity.name)
|
||||
.build()
|
||||
.ns(ns::DISCO_INFO)
|
||||
.attr("category", identity.category)
|
||||
.attr("type", identity.type_)
|
||||
.attr("xml:lang", identity.lang)
|
||||
.attr("name", identity.name)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,9 +127,9 @@ impl TryFrom<Element> for DiscoInfoResult {
|
|||
|
||||
let mut result = DiscoInfoResult {
|
||||
node: get_attr!(elem, "node", optional),
|
||||
identities: vec!(),
|
||||
features: vec!(),
|
||||
extensions: vec!(),
|
||||
identities: vec![],
|
||||
features: vec![],
|
||||
extensions: vec![],
|
||||
};
|
||||
|
||||
for child in elem.children() {
|
||||
|
@ -137,7 +142,9 @@ impl TryFrom<Element> for DiscoInfoResult {
|
|||
} else if child.is("x", ns::DATA_FORMS) {
|
||||
let data_form = DataForm::try_from(child.clone())?;
|
||||
if data_form.type_ != DataFormType::Result_ {
|
||||
return Err(Error::ParseError("Data form must have a 'result' type in disco#info."));
|
||||
return Err(Error::ParseError(
|
||||
"Data form must have a 'result' type in disco#info.",
|
||||
));
|
||||
}
|
||||
if data_form.form_type.is_none() {
|
||||
return Err(Error::ParseError("Data form found without a FORM_TYPE."));
|
||||
|
@ -149,13 +156,21 @@ impl TryFrom<Element> for DiscoInfoResult {
|
|||
}
|
||||
|
||||
if result.identities.is_empty() {
|
||||
return Err(Error::ParseError("There must be at least one identity in disco#info."));
|
||||
return Err(Error::ParseError(
|
||||
"There must be at least one identity in disco#info.",
|
||||
));
|
||||
}
|
||||
if result.features.is_empty() {
|
||||
return Err(Error::ParseError("There must be at least one feature in disco#info."));
|
||||
return Err(Error::ParseError(
|
||||
"There must be at least one feature in disco#info.",
|
||||
));
|
||||
}
|
||||
if !result.features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) {
|
||||
return Err(Error::ParseError("disco#info feature not present in disco#info."));
|
||||
if !result.features.contains(&Feature {
|
||||
var: ns::DISCO_INFO.to_owned(),
|
||||
}) {
|
||||
return Err(Error::ParseError(
|
||||
"disco#info feature not present in disco#info.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
@ -165,12 +180,12 @@ impl TryFrom<Element> for DiscoInfoResult {
|
|||
impl From<DiscoInfoResult> for Element {
|
||||
fn from(disco: DiscoInfoResult) -> Element {
|
||||
Element::builder("query")
|
||||
.ns(ns::DISCO_INFO)
|
||||
.attr("node", disco.node)
|
||||
.append(disco.identities)
|
||||
.append(disco.features)
|
||||
.append(disco.extensions)
|
||||
.build()
|
||||
.ns(ns::DISCO_INFO)
|
||||
.attr("node", disco.node)
|
||||
.append(disco.identities)
|
||||
.append(disco.features)
|
||||
.append(disco.extensions)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +310,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -306,7 +324,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_identity() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -314,13 +335,19 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'category' missing.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Identity must have a non-empty 'category' attribute.");
|
||||
assert_eq!(
|
||||
message,
|
||||
"Identity must have a non-empty 'category' attribute."
|
||||
);
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
|
@ -341,7 +368,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_feature() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -352,13 +382,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_result() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "There must be at least one identity in disco#info.");
|
||||
assert_eq!(
|
||||
message,
|
||||
"There must be at least one identity in disco#info."
|
||||
);
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/></query>".parse().unwrap();
|
||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||
|
@ -379,23 +414,31 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple_items() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let query = DiscoItemsQuery::try_from(elem).unwrap();
|
||||
assert!(query.node.is_none());
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let query = DiscoItemsQuery::try_from(elem).unwrap();
|
||||
assert_eq!(query.node, Some(String::from("coucou")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_items_result() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let query = DiscoItemsResult::try_from(elem).unwrap();
|
||||
assert!(query.node.is_none());
|
||||
assert!(query.items.is_empty());
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let query = DiscoItemsResult::try_from(elem).unwrap();
|
||||
assert_eq!(query.node, Some(String::from("coucou")));
|
||||
assert!(query.items.is_empty());
|
||||
|
|
336
src/ecaps2.rs
336
src/ecaps2.rs
|
@ -4,18 +4,16 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery};
|
||||
use crate::data_forms::DataForm;
|
||||
use crate::hashes::{Hash, Algo};
|
||||
|
||||
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
|
||||
use crate::hashes::{Algo, Hash};
|
||||
use crate::ns;
|
||||
use crate::presence::PresencePayload;
|
||||
use base64;
|
||||
|
||||
use blake2::VarBlake2b;
|
||||
use digest::{Digest, Input, VariableOutput};
|
||||
use sha2::{Sha256, Sha512};
|
||||
use sha3::{Sha3_256, Sha3_512};
|
||||
use blake2::VarBlake2b;
|
||||
use digest::{Digest, VariableOutput, Input};
|
||||
|
||||
generate_element!(
|
||||
/// Represents a set of capability hashes, all of them must correspond to
|
||||
|
@ -37,8 +35,8 @@ fn compute_item(field: &str) -> Vec<u8> {
|
|||
}
|
||||
|
||||
fn compute_items<T, F: Fn(&T) -> Vec<u8>>(things: &[T], separator: u8, encode: F) -> Vec<u8> {
|
||||
let mut string: Vec<u8> = vec!();
|
||||
let mut accumulator: Vec<Vec<u8>> = vec!();
|
||||
let mut string: Vec<u8> = vec![];
|
||||
let mut accumulator: Vec<Vec<u8>> = vec![];
|
||||
for thing in things {
|
||||
let bytes = encode(thing);
|
||||
accumulator.push(bytes);
|
||||
|
@ -60,8 +58,12 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
|
|||
compute_items(identities, 0x1c, |identity| {
|
||||
let mut bytes = compute_item(&identity.category);
|
||||
bytes.append(&mut compute_item(&identity.type_));
|
||||
bytes.append(&mut compute_item(&identity.lang.clone().unwrap_or_default()));
|
||||
bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default()));
|
||||
bytes.append(&mut compute_item(
|
||||
&identity.lang.clone().unwrap_or_default(),
|
||||
));
|
||||
bytes.append(&mut compute_item(
|
||||
&identity.name.clone().unwrap_or_default(),
|
||||
));
|
||||
bytes.push(0x1e);
|
||||
bytes
|
||||
})
|
||||
|
@ -71,8 +73,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
|||
compute_items(extensions, 0x1c, |extension| {
|
||||
compute_items(&extension.fields, 0x1d, |field| {
|
||||
let mut bytes = compute_item(&field.var);
|
||||
bytes.append(&mut compute_items(&field.values, 0x1e,
|
||||
|value| compute_item(value)));
|
||||
bytes.append(&mut compute_items(&field.values, 0x1e, |value| {
|
||||
compute_item(value)
|
||||
}));
|
||||
bytes
|
||||
})
|
||||
})
|
||||
|
@ -86,7 +89,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
|
|||
let identities_string = compute_identities(&disco.identities);
|
||||
let extensions_string = compute_extensions(&disco.extensions);
|
||||
|
||||
let mut final_string = vec!();
|
||||
let mut final_string = vec![];
|
||||
final_string.extend(features_string);
|
||||
final_string.extend(identities_string);
|
||||
final_string.extend(extensions_string);
|
||||
|
@ -107,29 +110,29 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, String> {
|
|||
Algo::Sha_256 => {
|
||||
let hash = Sha256::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha_512 => {
|
||||
let hash = Sha512::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha3_256 => {
|
||||
let hash = Sha3_256::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Sha3_512 => {
|
||||
let hash = Sha3_512::digest(data);
|
||||
get_hash_vec(hash.as_slice())
|
||||
},
|
||||
}
|
||||
Algo::Blake2b_256 => {
|
||||
let mut hasher = VarBlake2b::new(32).unwrap();
|
||||
hasher.input(data);
|
||||
hasher.vec_result()
|
||||
},
|
||||
}
|
||||
Algo::Blake2b_512 => {
|
||||
let mut hasher = VarBlake2b::new(64).unwrap();
|
||||
hasher.input(data);
|
||||
hasher.vec_result()
|
||||
},
|
||||
}
|
||||
Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")),
|
||||
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
|
||||
},
|
||||
|
@ -141,16 +144,21 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, String> {
|
|||
/// ecaps2 hash.
|
||||
pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
|
||||
DiscoInfoQuery {
|
||||
node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))),
|
||||
node: Some(format!(
|
||||
"{}#{}.{}",
|
||||
ns::ECAPS2,
|
||||
String::from(hash.algo),
|
||||
base64::encode(&hash.hash)
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -170,9 +178,15 @@ mod tests {
|
|||
let ecaps2 = ECaps2::try_from(elem).unwrap();
|
||||
assert_eq!(ecaps2.hashes.len(), 2);
|
||||
assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256);
|
||||
assert_eq!(ecaps2.hashes[0].hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap());
|
||||
assert_eq!(
|
||||
ecaps2.hashes[0].hash,
|
||||
base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()
|
||||
);
|
||||
assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256);
|
||||
assert_eq!(ecaps2.hashes[1].hash, base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap());
|
||||
assert_eq!(
|
||||
ecaps2.hashes[1].hash,
|
||||
base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -217,50 +231,52 @@ mod tests {
|
|||
<feature var="jabber:iq:roster"/>
|
||||
<feature var="jabber:iq:last"/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104,
|
||||
97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
|
||||
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||
114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
|
||||
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111,
|
||||
102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
|
||||
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98,
|
||||
98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97,
|
||||
98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111,
|
||||
110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105,
|
||||
112, 116, 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116,
|
||||
105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111,
|
||||
98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111,
|
||||
100, 31, 30, 28, 28];
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let expected = vec![
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109,
|
||||
115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116,
|
||||
101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
|
||||
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35,
|
||||
105, 116, 101, 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104,
|
||||
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116,
|
||||
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
||||
111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105,
|
||||
47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
|
||||
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116,
|
||||
31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31,
|
||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97,
|
||||
98, 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58,
|
||||
120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110,
|
||||
103, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
|
||||
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99,
|
||||
108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98,
|
||||
117, 115, 77, 111, 100, 31, 30, 28, 28,
|
||||
];
|
||||
let disco = DiscoInfoResult::try_from(elem).unwrap();
|
||||
let ecaps2 = compute_disco(&disco);
|
||||
assert_eq!(ecaps2.len(), 0x1d9);
|
||||
assert_eq!(ecaps2, expected);
|
||||
|
||||
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
|
||||
assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap());
|
||||
assert_eq!(
|
||||
sha_256.hash,
|
||||
base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()
|
||||
);
|
||||
let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
|
||||
assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap());
|
||||
assert_eq!(
|
||||
sha3_256.hash,
|
||||
base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -329,121 +345,111 @@ mod tests {
|
|||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97,
|
||||
99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116,
|
||||
121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114,
|
||||
101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98,
|
||||
98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,
|
||||
111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111,
|
||||
109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106,
|
||||
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
||||
111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102,
|
||||
111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||
100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102,
|
||||
101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116,
|
||||
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111,
|
||||
99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47,
|
||||
103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47,
|
||||
105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111,
|
||||
99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111,
|
||||
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
|
||||
99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104,
|
||||
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47,
|
||||
112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116,
|
||||
114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111,
|
||||
111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101,
|
||||
115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||
119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101,
|
||||
120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45,
|
||||
48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101,
|
||||
116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31,
|
||||
106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97,
|
||||
114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111,
|
||||
119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98,
|
||||
101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31,
|
||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31,
|
||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106,
|
||||
97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99,
|
||||
121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111,
|
||||
115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109,
|
||||
112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97,
|
||||
100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114,
|
||||
110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114,
|
||||
110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
|
||||
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109,
|
||||
101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101,
|
||||
110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101,
|
||||
110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
|
||||
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79,
|
||||
82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112,
|
||||
112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111,
|
||||
102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115,
|
||||
31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118,
|
||||
101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102,
|
||||
116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
|
||||
115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105,
|
||||
111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50,
|
||||
48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108,
|
||||
47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28];
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let expected = vec![
|
||||
103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106,
|
||||
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||
97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
|
||||
116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101,
|
||||
109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
|
||||
116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116,
|
||||
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
||||
111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111,
|
||||
111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111,
|
||||
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101,
|
||||
114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
|
||||
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
|
||||
99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108,
|
||||
101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46,
|
||||
102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109,
|
||||
101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119,
|
||||
46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111,
|
||||
110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115,
|
||||
45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97,
|
||||
98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98,
|
||||
101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97,
|
||||
110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105,
|
||||
110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106,
|
||||
97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58,
|
||||
105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105,
|
||||
113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101,
|
||||
114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97,
|
||||
31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98,
|
||||
98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58,
|
||||
97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116,
|
||||
105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105,
|
||||
101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
|
||||
99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
|
||||
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89,
|
||||
80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114,
|
||||
109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111,
|
||||
115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115,
|
||||
105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107,
|
||||
97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114,
|
||||
115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49,
|
||||
49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54,
|
||||
98, 50, 41, 31, 30, 29, 28,
|
||||
];
|
||||
let disco = DiscoInfoResult::try_from(elem).unwrap();
|
||||
let ecaps2 = compute_disco(&disco);
|
||||
assert_eq!(ecaps2.len(), 0x543);
|
||||
assert_eq!(ecaps2, expected);
|
||||
|
||||
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
|
||||
assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap());
|
||||
assert_eq!(
|
||||
sha_256.hash,
|
||||
base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()
|
||||
);
|
||||
let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
|
||||
assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap());
|
||||
assert_eq!(
|
||||
sha3_256.hash,
|
||||
base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blake2b_512() {
|
||||
let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap();
|
||||
let known_hash: Vec<u8> = vec!(
|
||||
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9,
|
||||
0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1,
|
||||
0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95,
|
||||
0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
|
||||
);
|
||||
let known_hash: Vec<u8> = vec![
|
||||
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12,
|
||||
0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F,
|
||||
0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52,
|
||||
0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A,
|
||||
0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
|
||||
];
|
||||
assert_eq!(hash.hash, known_hash);
|
||||
}
|
||||
}
|
||||
|
|
25
src/eme.rs
25
src/eme.rs
|
@ -24,9 +24,9 @@ impl MessagePayload for ExplicitMessageEncryption {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -42,7 +42,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'/>".parse().unwrap();
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let encryption = ExplicitMessageEncryption::try_from(elem).unwrap();
|
||||
assert_eq!(encryption.namespace, "urn:xmpp:otr:0");
|
||||
assert_eq!(encryption.name, None);
|
||||
|
@ -55,7 +57,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_unknown() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -66,7 +70,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0'><coucou/></encryption>".parse().unwrap();
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0'><coucou/></encryption>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -77,8 +83,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>".parse().unwrap();
|
||||
let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None };
|
||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let eme = ExplicitMessageEncryption {
|
||||
namespace: String::from("coucou"),
|
||||
name: None,
|
||||
};
|
||||
let elem2 = eme.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
11
src/error.rs
11
src/error.rs
|
@ -4,15 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use base64;
|
||||
use chrono;
|
||||
use jid;
|
||||
use std::convert::From;
|
||||
use std::num;
|
||||
use std::string;
|
||||
use std::fmt;
|
||||
use std::net;
|
||||
|
||||
use base64;
|
||||
use jid;
|
||||
use chrono;
|
||||
use std::num;
|
||||
use std::string;
|
||||
|
||||
/// Contains one of the potential errors triggered while parsing an
|
||||
/// [Element](../struct.Element.html) into a specialised struct.
|
||||
|
|
|
@ -26,9 +26,9 @@ generate_element!(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -50,7 +50,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><coucou/></forwarded>".parse().unwrap();
|
||||
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><coucou/></forwarded>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Forwarded::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -62,7 +64,10 @@ mod tests {
|
|||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'/>".parse().unwrap();
|
||||
let forwarded = Forwarded { delay: None, stanza: None };
|
||||
let forwarded = Forwarded {
|
||||
delay: None,
|
||||
stanza: None,
|
||||
};
|
||||
let elem2 = forwarded.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use minidom::IntoAttributeValue;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::helpers::Base64;
|
||||
use base64;
|
||||
use minidom::IntoAttributeValue;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// List of the algorithms we support, or Unknown.
|
||||
#[allow(non_camel_case_types)]
|
||||
|
@ -114,10 +111,7 @@ generate_element!(
|
|||
impl Hash {
|
||||
/// Creates a [Hash] element with the given algo and data.
|
||||
pub fn new(algo: Algo, hash: Vec<u8>) -> Hash {
|
||||
Hash {
|
||||
algo,
|
||||
hash,
|
||||
}
|
||||
Hash { algo, hash }
|
||||
}
|
||||
|
||||
/// Like [new](#method.new) but takes base64-encoded data before decoding
|
||||
|
@ -130,8 +124,8 @@ impl Hash {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -152,12 +146,17 @@ mod tests {
|
|||
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=</hash>".parse().unwrap();
|
||||
let hash = Hash::try_from(elem).unwrap();
|
||||
assert_eq!(hash.algo, Algo::Sha_256);
|
||||
assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap());
|
||||
assert_eq!(
|
||||
hash.hash,
|
||||
base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Hash::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -168,7 +167,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>".parse().unwrap();
|
||||
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Hash::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use base64;
|
||||
use crate::error::Error;
|
||||
use base64;
|
||||
|
||||
/// Codec for plain text content.
|
||||
pub struct PlainText;
|
||||
|
@ -19,9 +19,7 @@ impl PlainText {
|
|||
}
|
||||
|
||||
pub fn encode(string: &Option<String>) -> Option<String> {
|
||||
string.as_ref().map(|text| {
|
||||
text.to_owned()
|
||||
})
|
||||
string.as_ref().map(|text| text.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
35
src/ibb.rs
35
src/ibb.rs
|
@ -8,8 +8,9 @@ use crate::helpers::Base64;
|
|||
use crate::iq::IqSetPayload;
|
||||
|
||||
generate_id!(
|
||||
/// An identifier matching a stream.
|
||||
StreamId);
|
||||
/// An identifier matching a stream.
|
||||
StreamId
|
||||
);
|
||||
|
||||
generate_attribute!(
|
||||
/// Which stanza type to use to exchange data.
|
||||
|
@ -71,10 +72,10 @@ impl IqSetPayload for Close {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::error::Error as StdError;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -98,26 +99,36 @@ mod tests {
|
|||
fn test_simple() {
|
||||
let sid = StreamId(String::from("coucou"));
|
||||
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let open = Open::try_from(elem).unwrap();
|
||||
assert_eq!(open.block_size, 3);
|
||||
assert_eq!(open.sid, sid);
|
||||
assert_eq!(open.stanza, Stanza::Iq);
|
||||
|
||||
let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let data = Data::try_from(elem).unwrap();
|
||||
assert_eq!(data.seq, 0);
|
||||
assert_eq!(data.sid, sid);
|
||||
assert_eq!(data.data, vec!(0, 0, 0));
|
||||
|
||||
let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let close = Close::try_from(elem).unwrap();
|
||||
assert_eq!(close.sid, sid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Open::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -125,7 +136,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'block-size' missing.");
|
||||
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Open::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
|
@ -133,7 +146,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message.description(), "invalid digit found in string");
|
||||
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
|
||||
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Open::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(error) => error,
|
||||
|
|
93
src/ibr.rs
93
src/ibr.rs
|
@ -4,18 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::data_forms::DataForm;
|
||||
use crate::error::Error;
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use std::collections::HashMap;
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
||||
use crate::data_forms::DataForm;
|
||||
|
||||
use crate::ns;
|
||||
|
||||
/// Query for registering against a service.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Query {
|
||||
|
@ -31,7 +27,6 @@ pub struct Query {
|
|||
|
||||
/// A data form the user must fill before being allowed to register.
|
||||
pub form: Option<DataForm>,
|
||||
|
||||
// Not yet implemented.
|
||||
//pub oob: Option<Oob>,
|
||||
}
|
||||
|
@ -55,9 +50,26 @@ impl TryFrom<Element> for Query {
|
|||
let namespace = child.ns().unwrap();
|
||||
if namespace == ns::REGISTER {
|
||||
let name = child.name();
|
||||
let fields = vec!["address", "city", "date", "email", "first", "instructions",
|
||||
"key", "last", "misc", "name", "nick", "password", "phone",
|
||||
"state", "text", "url", "username", "zip"];
|
||||
let fields = vec![
|
||||
"address",
|
||||
"city",
|
||||
"date",
|
||||
"email",
|
||||
"first",
|
||||
"instructions",
|
||||
"key",
|
||||
"last",
|
||||
"misc",
|
||||
"name",
|
||||
"nick",
|
||||
"password",
|
||||
"phone",
|
||||
"state",
|
||||
"text",
|
||||
"url",
|
||||
"username",
|
||||
"zip",
|
||||
];
|
||||
if fields.binary_search(&name).is_ok() {
|
||||
query.fields.insert(name.to_owned(), child.text());
|
||||
} else if name == "registered" {
|
||||
|
@ -80,14 +92,26 @@ impl TryFrom<Element> for Query {
|
|||
impl From<Query> for Element {
|
||||
fn from(query: Query) -> Element {
|
||||
Element::builder("query")
|
||||
.ns(ns::REGISTER)
|
||||
.append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None })
|
||||
.append(query.fields.into_iter().map(|(name, value)| {
|
||||
Element::builder(name).ns(ns::REGISTER).append(value)
|
||||
}).collect::<Vec<_>>())
|
||||
.append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None })
|
||||
.append(query.form)
|
||||
.build()
|
||||
.ns(ns::REGISTER)
|
||||
.append(if query.registered {
|
||||
Some(Element::builder("registered").ns(ns::REGISTER))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.append(
|
||||
query
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.append(if query.remove {
|
||||
Some(Element::builder("remove").ns(ns::REGISTER))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.append(query.form)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +150,9 @@ mod tests {
|
|||
<password/>
|
||||
<email/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let query = Query::try_from(elem).unwrap();
|
||||
assert_eq!(query.registered, false);
|
||||
assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n ");
|
||||
|
@ -172,16 +198,27 @@ mod tests {
|
|||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let query = Query::try_from(elem).unwrap();
|
||||
assert_eq!(query.registered, false);
|
||||
assert!(!query.fields["instructions"].is_empty());
|
||||
let form = query.form.clone().unwrap();
|
||||
assert!(!form.instructions.unwrap().is_empty());
|
||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
|
||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
|
||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
|
||||
assert!(form
|
||||
.fields
|
||||
.binary_search_by(|field| field.var.cmp(&String::from("first")))
|
||||
.is_ok());
|
||||
assert!(form
|
||||
.fields
|
||||
.binary_search_by(|field| field.var.cmp(&String::from("x-gender")))
|
||||
.is_ok());
|
||||
assert!(form
|
||||
.fields
|
||||
.binary_search_by(|field| field.var.cmp(&String::from("coucou")))
|
||||
.is_err());
|
||||
let elem2 = query.into();
|
||||
assert!(elem1.compare_to(&elem2));
|
||||
}
|
||||
|
@ -208,7 +245,9 @@ mod tests {
|
|||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let query = Query::try_from(elem).unwrap();
|
||||
assert_eq!(query.registered, false);
|
||||
|
|
48
src/idle.rs
48
src/idle.rs
|
@ -4,8 +4,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::date::DateTime;
|
||||
use crate::presence::PresencePayload;
|
||||
|
||||
generate_element!(
|
||||
/// Represents the last time the user interacted with their system.
|
||||
|
@ -21,11 +21,11 @@ impl PresencePayload for Idle {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use std::str::FromStr;
|
||||
use minidom::Element;
|
||||
use std::error::Error as StdError;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
|
@ -34,13 +34,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Idle::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -63,7 +67,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_invalid_date() {
|
||||
// There is no thirteenth month.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-13-01T12:23:34Z'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-13-01T12:23:34Z'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -72,7 +78,9 @@ mod tests {
|
|||
assert_eq!(message.description(), "input is out of range");
|
||||
|
||||
// Timezone ≥24:00 aren’t allowed.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+25:00'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+25:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -81,7 +89,9 @@ mod tests {
|
|||
assert_eq!(message.description(), "input is out of range");
|
||||
|
||||
// Timezone without the : separator aren’t allowed.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+0100'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+0100'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -90,7 +100,9 @@ mod tests {
|
|||
assert_eq!(message.description(), "input contains invalid characters");
|
||||
|
||||
// No seconds, error message could be improved.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11+01:00'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11+01:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -99,7 +111,9 @@ mod tests {
|
|||
assert_eq!(message.description(), "input contains invalid characters");
|
||||
|
||||
// TODO: maybe we’ll want to support this one, as per XEP-0082 §4.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='20170527T12:11:02+01:00'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='20170527T12:11:02+01:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -108,7 +122,9 @@ mod tests {
|
|||
assert_eq!(message.description(), "input contains invalid characters");
|
||||
|
||||
// No timezone.
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02'/>".parse().unwrap();
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Idle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ChronoParseError(string) => string,
|
||||
|
@ -119,8 +135,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>".parse().unwrap();
|
||||
let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() };
|
||||
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let idle = Idle {
|
||||
since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap(),
|
||||
};
|
||||
let elem2 = idle.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
111
src/iq.rs
111
src/iq.rs
|
@ -5,18 +5,13 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
use crate::stanza_error::StanzaError;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use minidom::IntoAttributeValue;
|
||||
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
|
||||
use crate::stanza_error::StanzaError;
|
||||
use try_from::TryFrom;
|
||||
|
||||
/// Should be implemented on every known payload of an `<iq type='get'/>`.
|
||||
pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
|
||||
|
@ -45,12 +40,15 @@ pub enum IqType {
|
|||
|
||||
impl<'a> IntoAttributeValue for &'a IqType {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
Some(match *self {
|
||||
IqType::Get(_) => "get",
|
||||
IqType::Set(_) => "set",
|
||||
IqType::Result(_) => "result",
|
||||
IqType::Error(_) => "error",
|
||||
}.to_owned())
|
||||
Some(
|
||||
match *self {
|
||||
IqType::Get(_) => "get",
|
||||
IqType::Set(_) => "set",
|
||||
IqType::Result(_) => "result",
|
||||
IqType::Error(_) => "error",
|
||||
}
|
||||
.to_owned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,16 +199,14 @@ impl TryFrom<Element> for Iq {
|
|||
impl From<Iq> for Element {
|
||||
fn from(iq: Iq) -> Element {
|
||||
let mut stanza = Element::builder("iq")
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", iq.from)
|
||||
.attr("to", iq.to)
|
||||
.attr("id", iq.id)
|
||||
.attr("type", &iq.payload)
|
||||
.build();
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", iq.from)
|
||||
.attr("to", iq.to)
|
||||
.attr("id", iq.id)
|
||||
.attr("type", &iq.payload)
|
||||
.build();
|
||||
let elem = match iq.payload {
|
||||
IqType::Get(elem)
|
||||
| IqType::Set(elem)
|
||||
| IqType::Result(Some(elem)) => elem,
|
||||
IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem,
|
||||
IqType::Error(error) => error.into(),
|
||||
IqType::Result(None) => return stanza,
|
||||
};
|
||||
|
@ -222,9 +218,9 @@ impl From<Iq> for Element {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::stanza_error::{ErrorType, DefinedCondition};
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use crate::disco::DiscoInfoQuery;
|
||||
use crate::stanza_error::{DefinedCondition, ErrorType};
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -259,11 +255,15 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='get'>
|
||||
<foo xmlns='bar'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
|
||||
<foo xmlns='bar'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
|
||||
assert_eq!(iq.from, None);
|
||||
|
@ -271,7 +271,7 @@ mod tests {
|
|||
assert_eq!(iq.id, None);
|
||||
assert!(match iq.payload {
|
||||
IqType::Get(element) => element.compare_to(&query),
|
||||
_ => false
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -280,11 +280,15 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='set'>
|
||||
<vCard xmlns='vcard-temp'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
|
||||
<vCard xmlns='vcard-temp'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
|
||||
assert_eq!(iq.from, None);
|
||||
|
@ -292,7 +296,7 @@ mod tests {
|
|||
assert_eq!(iq.id, None);
|
||||
assert!(match iq.payload {
|
||||
IqType::Set(element) => element.compare_to(&vcard),
|
||||
_ => false
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -301,7 +305,9 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
assert_eq!(iq.from, None);
|
||||
assert_eq!(iq.to, None);
|
||||
|
@ -317,13 +323,19 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='result'>
|
||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
|
||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
|
||||
let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(iq.from, None);
|
||||
assert_eq!(iq.to, None);
|
||||
assert_eq!(iq.id, None);
|
||||
|
@ -341,14 +353,18 @@ mod tests {
|
|||
<error type='cancel'>
|
||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||
</error>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
|
||||
<ping xmlns='urn:xmpp:ping'/>
|
||||
<error type='cancel'>
|
||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||
</error>
|
||||
</iq>".parse().unwrap();
|
||||
</iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq = Iq::try_from(elem).unwrap();
|
||||
assert_eq!(iq.from, None);
|
||||
assert_eq!(iq.to, None);
|
||||
|
@ -357,10 +373,13 @@ mod tests {
|
|||
IqType::Error(error) => {
|
||||
assert_eq!(error.type_, ErrorType::Cancel);
|
||||
assert_eq!(error.by, None);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
|
||||
assert_eq!(
|
||||
error.defined_condition,
|
||||
DefinedCondition::ServiceUnavailable
|
||||
);
|
||||
assert_eq!(error.texts.len(), 0);
|
||||
assert_eq!(error.other, None);
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -368,9 +387,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_children_invalid() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>".parse().unwrap();
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Iq::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -384,7 +407,9 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
|
||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let iq2 = Iq {
|
||||
from: None,
|
||||
to: None,
|
||||
|
|
|
@ -4,15 +4,13 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
use crate::iq::IqSetPayload;
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_attribute!(
|
||||
/// The action attribute.
|
||||
|
@ -351,7 +349,8 @@ impl From<Reason> for Element {
|
|||
Reason::Timeout => "timeout",
|
||||
Reason::UnsupportedApplications => "unsupported-applications",
|
||||
Reason::UnsupportedTransports => "unsupported-transports",
|
||||
}).build()
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,19 +378,25 @@ impl TryFrom<Element> for ReasonElement {
|
|||
match child.name() {
|
||||
"text" => {
|
||||
if text.is_some() {
|
||||
return Err(Error::ParseError("Reason must not have more than one text."));
|
||||
return Err(Error::ParseError(
|
||||
"Reason must not have more than one text.",
|
||||
));
|
||||
}
|
||||
text = Some(child.text());
|
||||
},
|
||||
}
|
||||
name => {
|
||||
if reason.is_some() {
|
||||
return Err(Error::ParseError("Reason must not have more than one reason."));
|
||||
return Err(Error::ParseError(
|
||||
"Reason must not have more than one reason.",
|
||||
));
|
||||
}
|
||||
reason = Some(name.parse()?);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
let reason = reason.ok_or(Error::ParseError("Reason doesn’t contain a valid reason."))?;
|
||||
let reason = reason.ok_or(Error::ParseError(
|
||||
"Reason doesn’t contain a valid reason.",
|
||||
))?;
|
||||
Ok(ReasonElement {
|
||||
reason: reason,
|
||||
text: text,
|
||||
|
@ -402,9 +407,9 @@ impl TryFrom<Element> for ReasonElement {
|
|||
impl From<ReasonElement> for Element {
|
||||
fn from(reason: ReasonElement) -> Element {
|
||||
Element::builder("reason")
|
||||
.append(Element::from(reason.reason))
|
||||
.append(reason.text)
|
||||
.build()
|
||||
.append(Element::from(reason.reason))
|
||||
.append(reason.text)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,9 +496,9 @@ impl TryFrom<Element> for Jingle {
|
|||
initiator: get_attr!(root, "initiator", optional),
|
||||
responder: get_attr!(root, "responder", optional),
|
||||
sid: get_attr!(root, "sid", required),
|
||||
contents: vec!(),
|
||||
contents: vec![],
|
||||
reason: None,
|
||||
other: vec!(),
|
||||
other: vec![],
|
||||
};
|
||||
|
||||
for child in root.children().cloned() {
|
||||
|
@ -502,7 +507,9 @@ impl TryFrom<Element> for Jingle {
|
|||
jingle.contents.push(content);
|
||||
} else if child.is("reason", ns::JINGLE) {
|
||||
if jingle.reason.is_some() {
|
||||
return Err(Error::ParseError("Jingle must not have more than one reason."));
|
||||
return Err(Error::ParseError(
|
||||
"Jingle must not have more than one reason.",
|
||||
));
|
||||
}
|
||||
let reason = ReasonElement::try_from(child)?;
|
||||
jingle.reason = Some(reason);
|
||||
|
@ -518,14 +525,14 @@ impl TryFrom<Element> for Jingle {
|
|||
impl From<Jingle> for Element {
|
||||
fn from(jingle: Jingle) -> Element {
|
||||
Element::builder("jingle")
|
||||
.ns(ns::JINGLE)
|
||||
.attr("action", jingle.action)
|
||||
.attr("initiator", jingle.initiator)
|
||||
.attr("responder", jingle.responder)
|
||||
.attr("sid", jingle.sid)
|
||||
.append(jingle.contents)
|
||||
.append(jingle.reason)
|
||||
.build()
|
||||
.ns(ns::JINGLE)
|
||||
.attr("action", jingle.action)
|
||||
.attr("initiator", jingle.initiator)
|
||||
.attr("responder", jingle.responder)
|
||||
.attr("sid", jingle.sid)
|
||||
.append(jingle.contents)
|
||||
.append(jingle.reason)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -565,7 +572,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let jingle = Jingle::try_from(elem).unwrap();
|
||||
assert_eq!(jingle.action, Action::SessionInitiate);
|
||||
assert_eq!(jingle.sid, SessionId(String::from("coucou")));
|
||||
|
@ -581,7 +591,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'action' missing.");
|
||||
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-info'/>".parse().unwrap();
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-info'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Jingle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -589,7 +601,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'sid' missing.");
|
||||
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='coucou' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='coucou' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Jingle::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
196
src/jingle_ft.rs
196
src/jingle_ft.rs
|
@ -4,19 +4,15 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::hashes::Hash;
|
||||
use crate::jingle::{Creator, ContentId};
|
||||
use crate::date::DateTime;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::hashes::Hash;
|
||||
use crate::jingle::{ContentId, Creator};
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_element!(
|
||||
/// Represents a range in a file.
|
||||
|
@ -153,7 +149,7 @@ impl TryFrom<Element> for File {
|
|||
descs: BTreeMap::new(),
|
||||
size: None,
|
||||
range: None,
|
||||
hashes: vec!(),
|
||||
hashes: vec![],
|
||||
};
|
||||
|
||||
for child in elem.children() {
|
||||
|
@ -164,7 +160,9 @@ impl TryFrom<Element> for File {
|
|||
file.date = Some(child.text().parse()?);
|
||||
} else if child.is("media-type", ns::JINGLE_FT) {
|
||||
if file.media_type.is_some() {
|
||||
return Err(Error::ParseError("File must not have more than one media-type."));
|
||||
return Err(Error::ParseError(
|
||||
"File must not have more than one media-type.",
|
||||
));
|
||||
}
|
||||
file.media_type = Some(child.text());
|
||||
} else if child.is("name", ns::JINGLE_FT) {
|
||||
|
@ -176,7 +174,9 @@ impl TryFrom<Element> for File {
|
|||
let lang = get_attr!(child, "xml:lang", default);
|
||||
let desc = Desc(child.text());
|
||||
if file.descs.insert(lang, desc).is_some() {
|
||||
return Err(Error::ParseError("Desc element present twice for the same xml:lang."));
|
||||
return Err(Error::ParseError(
|
||||
"Desc element present twice for the same xml:lang.",
|
||||
));
|
||||
}
|
||||
} else if child.is("size", ns::JINGLE_FT) {
|
||||
if file.size.is_some() {
|
||||
|
@ -201,38 +201,47 @@ impl TryFrom<Element> for File {
|
|||
|
||||
impl From<File> for Element {
|
||||
fn from(file: File) -> Element {
|
||||
let mut root = Element::builder("file")
|
||||
.ns(ns::JINGLE_FT);
|
||||
let mut root = Element::builder("file").ns(ns::JINGLE_FT);
|
||||
if let Some(date) = file.date {
|
||||
root = root.append(Element::builder("date")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(date)
|
||||
.build());
|
||||
root = root.append(
|
||||
Element::builder("date")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(date)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
if let Some(media_type) = file.media_type {
|
||||
root = root.append(Element::builder("media-type")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(media_type)
|
||||
.build());
|
||||
root = root.append(
|
||||
Element::builder("media-type")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(media_type)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
if let Some(name) = file.name {
|
||||
root = root.append(Element::builder("name")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(name)
|
||||
.build());
|
||||
root = root.append(
|
||||
Element::builder("name")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(name)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
for (lang, desc) in file.descs.into_iter() {
|
||||
root = root.append(Element::builder("desc")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.attr("xml:lang", lang)
|
||||
.append(desc.0)
|
||||
.build());
|
||||
root = root.append(
|
||||
Element::builder("desc")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.attr("xml:lang", lang)
|
||||
.append(desc.0)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
if let Some(size) = file.size {
|
||||
root = root.append(Element::builder("size")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(format!("{}", size))
|
||||
.build());
|
||||
root = root.append(
|
||||
Element::builder("size")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(format!("{}", size))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
if let Some(range) = file.range {
|
||||
root = root.append(range);
|
||||
|
@ -260,12 +269,16 @@ impl TryFrom<Element> for Description {
|
|||
let mut file = None;
|
||||
for child in elem.children() {
|
||||
if file.is_some() {
|
||||
return Err(Error::ParseError("JingleFT description element must have exactly one child."));
|
||||
return Err(Error::ParseError(
|
||||
"JingleFT description element must have exactly one child.",
|
||||
));
|
||||
}
|
||||
file = Some(File::try_from(child.clone())?);
|
||||
}
|
||||
if file.is_none() {
|
||||
return Err(Error::ParseError("JingleFT description element must have exactly one child."));
|
||||
return Err(Error::ParseError(
|
||||
"JingleFT description element must have exactly one child.",
|
||||
));
|
||||
}
|
||||
Ok(Description {
|
||||
file: file.unwrap(),
|
||||
|
@ -276,9 +289,9 @@ impl TryFrom<Element> for Description {
|
|||
impl From<Description> for Element {
|
||||
fn from(description: Description) -> Element {
|
||||
Element::builder("description")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(description.file)
|
||||
.build()
|
||||
.ns(ns::JINGLE_FT)
|
||||
.append(description.file)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,12 +317,16 @@ impl TryFrom<Element> for Checksum {
|
|||
let mut file = None;
|
||||
for child in elem.children() {
|
||||
if file.is_some() {
|
||||
return Err(Error::ParseError("JingleFT checksum element must have exactly one child."));
|
||||
return Err(Error::ParseError(
|
||||
"JingleFT checksum element must have exactly one child.",
|
||||
));
|
||||
}
|
||||
file = Some(File::try_from(child.clone())?);
|
||||
}
|
||||
if file.is_none() {
|
||||
return Err(Error::ParseError("JingleFT checksum element must have exactly one child."));
|
||||
return Err(Error::ParseError(
|
||||
"JingleFT checksum element must have exactly one child.",
|
||||
));
|
||||
}
|
||||
Ok(Checksum {
|
||||
name: get_attr!(elem, "name", required),
|
||||
|
@ -322,11 +339,11 @@ impl TryFrom<Element> for Checksum {
|
|||
impl From<Checksum> for Element {
|
||||
fn from(checksum: Checksum) -> Element {
|
||||
Element::builder("checksum")
|
||||
.ns(ns::JINGLE_FT)
|
||||
.attr("name", checksum.name)
|
||||
.attr("creator", checksum.creator)
|
||||
.append(checksum.file)
|
||||
.build()
|
||||
.ns(ns::JINGLE_FT)
|
||||
.attr("name", checksum.name)
|
||||
.attr("creator", checksum.creator)
|
||||
.append(checksum.file)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,16 +398,24 @@ mod tests {
|
|||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||
</file>
|
||||
</description>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let desc = Description::try_from(elem).unwrap();
|
||||
assert_eq!(desc.file.media_type, Some(String::from("text/plain")));
|
||||
assert_eq!(desc.file.name, Some(String::from("test.txt")));
|
||||
assert_eq!(desc.file.descs, BTreeMap::new());
|
||||
assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok());
|
||||
assert_eq!(
|
||||
desc.file.date,
|
||||
DateTime::from_str("2015-07-26T21:46:00+01:00").ok()
|
||||
);
|
||||
assert_eq!(desc.file.size, Some(6144u64));
|
||||
assert_eq!(desc.file.range, None);
|
||||
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
|
||||
assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap());
|
||||
assert_eq!(
|
||||
desc.file.hashes[0].hash,
|
||||
base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -402,7 +427,9 @@ mod tests {
|
|||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||
</file>
|
||||
</description>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let desc = Description::try_from(elem).unwrap();
|
||||
assert_eq!(desc.file.media_type, None);
|
||||
assert_eq!(desc.file.name, None);
|
||||
|
@ -411,7 +438,10 @@ mod tests {
|
|||
assert_eq!(desc.file.size, None);
|
||||
assert_eq!(desc.file.range, None);
|
||||
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
|
||||
assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap());
|
||||
assert_eq!(
|
||||
desc.file.hashes[0].hash,
|
||||
base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -426,11 +456,19 @@ mod tests {
|
|||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||
</file>
|
||||
</description>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let desc = Description::try_from(elem).unwrap();
|
||||
assert_eq!(desc.file.descs.keys().cloned().collect::<Vec<_>>(), ["en", "fr"]);
|
||||
assert_eq!(
|
||||
desc.file.descs.keys().cloned().collect::<Vec<_>>(),
|
||||
["en", "fr"]
|
||||
);
|
||||
assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!")));
|
||||
assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret !")));
|
||||
assert_eq!(
|
||||
desc.file.descs["fr"],
|
||||
Desc(String::from("Fichier secret !"))
|
||||
);
|
||||
|
||||
let elem: Element = r#"
|
||||
<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
|
||||
|
@ -442,7 +480,9 @@ mod tests {
|
|||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||
</file>
|
||||
</description>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Description::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -478,7 +518,10 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Unknown attribute in received element.");
|
||||
|
||||
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Received::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -498,16 +541,31 @@ mod tests {
|
|||
#[test]
|
||||
fn test_checksum() {
|
||||
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
|
||||
let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143);
|
||||
let hash = vec![
|
||||
195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138,
|
||||
63, 143,
|
||||
];
|
||||
let checksum = Checksum::try_from(elem).unwrap();
|
||||
assert_eq!(checksum.name, ContentId(String::from("coucou")));
|
||||
assert_eq!(checksum.creator, Creator::Initiator);
|
||||
assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() }));
|
||||
assert_eq!(
|
||||
checksum.file.hashes,
|
||||
vec!(Hash {
|
||||
algo: Algo::Sha_1,
|
||||
hash: hash.clone()
|
||||
})
|
||||
);
|
||||
let elem2 = Element::from(checksum);
|
||||
let checksum2 = Checksum::try_from(elem2).unwrap();
|
||||
assert_eq!(checksum2.name, ContentId(String::from("coucou")));
|
||||
assert_eq!(checksum2.creator, Creator::Initiator);
|
||||
assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() }));
|
||||
assert_eq!(
|
||||
checksum2.file.hashes,
|
||||
vec!(Hash {
|
||||
algo: Algo::Sha_1,
|
||||
hash: hash.clone()
|
||||
})
|
||||
);
|
||||
|
||||
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></checksum>".parse().unwrap();
|
||||
let error = Checksum::try_from(elem).unwrap_err();
|
||||
|
@ -544,14 +602,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_range() {
|
||||
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5'/>".parse().unwrap();
|
||||
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let range = Range::try_from(elem).unwrap();
|
||||
assert_eq!(range.offset, 0);
|
||||
assert_eq!(range.length, None);
|
||||
assert_eq!(range.hashes, vec!());
|
||||
|
||||
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' offset='2048' length='1024'><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>kHp5RSzW/h7Gm1etSf90Mr5PC/k=</hash></range>".parse().unwrap();
|
||||
let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) });
|
||||
let hashes = vec![Hash {
|
||||
algo: Algo::Sha_1,
|
||||
hash: vec![
|
||||
144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79,
|
||||
11, 249,
|
||||
],
|
||||
}];
|
||||
let range = Range::try_from(elem).unwrap();
|
||||
assert_eq!(range.offset, 2048);
|
||||
assert_eq!(range.length, Some(1024));
|
||||
|
@ -562,7 +628,9 @@ mod tests {
|
|||
assert_eq!(range2.length, Some(1024));
|
||||
assert_eq!(range2.hashes, hashes);
|
||||
|
||||
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Range::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -24,10 +24,10 @@ attributes: [
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::error::Error as StdError;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -43,7 +43,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='3' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='3' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let transport = Transport::try_from(elem).unwrap();
|
||||
assert_eq!(transport.block_size, 3);
|
||||
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
||||
|
@ -52,7 +55,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1'/>".parse().unwrap();
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Transport::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -60,15 +65,23 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'block-size' missing.");
|
||||
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='65536'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='65536'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Transport::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message.description(), "number too large to fit in target type");
|
||||
assert_eq!(
|
||||
message.description(),
|
||||
"number too large to fit in target type"
|
||||
);
|
||||
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='-5'/>".parse().unwrap();
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='-5'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Transport::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
|
@ -76,7 +89,10 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message.description(), "invalid digit found in string");
|
||||
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128'/>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Transport::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -4,15 +4,11 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::jingle::SessionId;
|
||||
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
/// Defines a protocol for broadcasting Jingle requests to all of the clients
|
||||
/// of a user.
|
||||
|
@ -72,9 +68,11 @@ impl TryFrom<Element> for JingleMI {
|
|||
}
|
||||
JingleMI::Propose {
|
||||
sid: get_sid(elem)?,
|
||||
description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?,
|
||||
description: description.ok_or(Error::ParseError(
|
||||
"Propose element doesn’t contain a description.",
|
||||
))?,
|
||||
}
|
||||
},
|
||||
}
|
||||
"retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?),
|
||||
"accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
|
||||
"proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?),
|
||||
|
@ -87,33 +85,24 @@ impl TryFrom<Element> for JingleMI {
|
|||
impl From<JingleMI> for Element {
|
||||
fn from(jingle_mi: JingleMI) -> Element {
|
||||
match jingle_mi {
|
||||
JingleMI::Propose { sid, description } => {
|
||||
Element::builder("propose")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
.append(description)
|
||||
},
|
||||
JingleMI::Retract(sid) => {
|
||||
Element::builder("retract")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
}
|
||||
JingleMI::Accept(sid) => {
|
||||
Element::builder("accept")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
}
|
||||
JingleMI::Proceed(sid) => {
|
||||
Element::builder("proceed")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
}
|
||||
JingleMI::Reject(sid) => {
|
||||
Element::builder("reject")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
}
|
||||
}.build()
|
||||
JingleMI::Propose { sid, description } => Element::builder("propose")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid)
|
||||
.append(description),
|
||||
JingleMI::Retract(sid) => Element::builder("retract")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid),
|
||||
JingleMI::Accept(sid) => Element::builder("accept")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid),
|
||||
JingleMI::Proceed(sid) => Element::builder("proceed")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid),
|
||||
JingleMI::Reject(sid) => Element::builder("reject")
|
||||
.ns(ns::JINGLE_MESSAGE)
|
||||
.attr("id", sid),
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,13 +124,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
JingleMI::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = JingleMI::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use std::net::IpAddr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_attribute!(
|
||||
/// The type of the connection being proposed by this candidate.
|
||||
|
@ -187,37 +184,50 @@ impl TryFrom<Element> for Transport {
|
|||
let mut payload = None;
|
||||
for child in elem.children() {
|
||||
payload = Some(if child.is("candidate", ns::JINGLE_S5B) {
|
||||
let mut candidates = match payload {
|
||||
Some(TransportPayload::Candidates(candidates)) => candidates,
|
||||
Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")),
|
||||
None => vec!(),
|
||||
};
|
||||
let mut candidates =
|
||||
match payload {
|
||||
Some(TransportPayload::Candidates(candidates)) => candidates,
|
||||
Some(_) => return Err(Error::ParseError(
|
||||
"Non-candidate child already present in JingleS5B transport element.",
|
||||
)),
|
||||
None => vec![],
|
||||
};
|
||||
candidates.push(Candidate::try_from(child.clone())?);
|
||||
TransportPayload::Candidates(candidates)
|
||||
} else if child.is("activated", ns::JINGLE_S5B) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element."));
|
||||
return Err(Error::ParseError(
|
||||
"Non-activated child already present in JingleS5B transport element.",
|
||||
));
|
||||
}
|
||||
let cid = get_attr!(child, "cid", required);
|
||||
TransportPayload::Activated(cid)
|
||||
} else if child.is("candidate-error", ns::JINGLE_S5B) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element."));
|
||||
return Err(Error::ParseError(
|
||||
"Non-candidate-error child already present in JingleS5B transport element.",
|
||||
));
|
||||
}
|
||||
TransportPayload::CandidateError
|
||||
} else if child.is("candidate-used", ns::JINGLE_S5B) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element."));
|
||||
return Err(Error::ParseError(
|
||||
"Non-candidate-used child already present in JingleS5B transport element.",
|
||||
));
|
||||
}
|
||||
let cid = get_attr!(child, "cid", required);
|
||||
TransportPayload::CandidateUsed(cid)
|
||||
} else if child.is("proxy-error", ns::JINGLE_S5B) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element."));
|
||||
return Err(Error::ParseError(
|
||||
"Non-proxy-error child already present in JingleS5B transport element.",
|
||||
));
|
||||
}
|
||||
TransportPayload::ProxyError
|
||||
} else {
|
||||
return Err(Error::ParseError("Unknown child in JingleS5B transport element."));
|
||||
return Err(Error::ParseError(
|
||||
"Unknown child in JingleS5B transport element.",
|
||||
));
|
||||
});
|
||||
}
|
||||
let payload = payload.unwrap_or(TransportPayload::None);
|
||||
|
@ -233,49 +243,40 @@ impl TryFrom<Element> for Transport {
|
|||
impl From<Transport> for Element {
|
||||
fn from(transport: Transport) -> Element {
|
||||
Element::builder("transport")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("sid", transport.sid)
|
||||
.attr("dstaddr", transport.dstaddr)
|
||||
.attr("mode", transport.mode)
|
||||
.append(match transport.payload {
|
||||
TransportPayload::Candidates(candidates) => {
|
||||
candidates.into_iter()
|
||||
.map(Element::from)
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
TransportPayload::Activated(cid) => {
|
||||
vec!(Element::builder("activated")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("cid", cid)
|
||||
.build())
|
||||
},
|
||||
TransportPayload::CandidateError => {
|
||||
vec!(Element::builder("candidate-error")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.build())
|
||||
},
|
||||
TransportPayload::CandidateUsed(cid) => {
|
||||
vec!(Element::builder("candidate-used")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("cid", cid)
|
||||
.build())
|
||||
},
|
||||
TransportPayload::ProxyError => {
|
||||
vec!(Element::builder("proxy-error")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.build())
|
||||
},
|
||||
TransportPayload::None => vec!(),
|
||||
})
|
||||
.build()
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("sid", transport.sid)
|
||||
.attr("dstaddr", transport.dstaddr)
|
||||
.attr("mode", transport.mode)
|
||||
.append(match transport.payload {
|
||||
TransportPayload::Candidates(candidates) => candidates
|
||||
.into_iter()
|
||||
.map(Element::from)
|
||||
.collect::<Vec<_>>(),
|
||||
TransportPayload::Activated(cid) => vec![Element::builder("activated")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("cid", cid)
|
||||
.build()],
|
||||
TransportPayload::CandidateError => vec![Element::builder("candidate-error")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.build()],
|
||||
TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used")
|
||||
.ns(ns::JINGLE_S5B)
|
||||
.attr("cid", cid)
|
||||
.build()],
|
||||
TransportPayload::ProxyError => {
|
||||
vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()]
|
||||
}
|
||||
TransportPayload::None => vec![],
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -303,7 +304,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:s5b:1' sid='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:s5b:1' sid='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let transport = Transport::try_from(elem).unwrap();
|
||||
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
||||
assert_eq!(transport.dstaddr, None);
|
||||
|
@ -334,14 +337,14 @@ mod tests {
|
|||
sid: StreamId(String::from("coucou")),
|
||||
dstaddr: None,
|
||||
mode: Mode::Tcp,
|
||||
payload: TransportPayload::Candidates(vec!(Candidate {
|
||||
payload: TransportPayload::Candidates(vec![Candidate {
|
||||
cid: CandidateId(String::from("coucou")),
|
||||
host: IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
jid: Jid::from_str("coucou@coucou").unwrap(),
|
||||
port: None,
|
||||
priority: 0u32,
|
||||
type_: Type::Direct,
|
||||
})),
|
||||
}]),
|
||||
};
|
||||
let elem2: Element = transport.into();
|
||||
assert!(elem.compare_to(&elem2));
|
||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -24,15 +24,15 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
extern crate minidom;
|
||||
extern crate jid;
|
||||
extern crate base64;
|
||||
extern crate blake2;
|
||||
extern crate chrono;
|
||||
extern crate digest;
|
||||
extern crate jid;
|
||||
extern crate minidom;
|
||||
extern crate sha1;
|
||||
extern crate sha2;
|
||||
extern crate sha3;
|
||||
extern crate blake2;
|
||||
extern crate chrono;
|
||||
extern crate try_from;
|
||||
|
||||
pub use minidom::Element;
|
||||
|
@ -52,20 +52,20 @@ mod macros;
|
|||
/// Namespace-aware comparison for tests
|
||||
mod compare_elements;
|
||||
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod bind;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod iq;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod message;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod presence;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod iq;
|
||||
pub mod sasl;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod stanza_error;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod stream;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod sasl;
|
||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||
pub mod bind;
|
||||
|
||||
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
|
||||
pub mod roster;
|
||||
|
|
161
src/macros.rs
161
src/macros.rs
|
@ -5,34 +5,40 @@
|
|||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
macro_rules! get_attr {
|
||||
($elem:ident, $attr:tt, $type:tt) => (
|
||||
($elem:ident, $attr:tt, $type:tt) => {
|
||||
get_attr!($elem, $attr, $type, value, value.parse()?)
|
||||
);
|
||||
($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => (
|
||||
};
|
||||
($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some("") => None,
|
||||
Some($value) => Some($func),
|
||||
None => None,
|
||||
}
|
||||
);
|
||||
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
|
||||
};
|
||||
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some($value) => Some($func),
|
||||
None => None,
|
||||
}
|
||||
);
|
||||
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
|
||||
};
|
||||
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some($value) => $func,
|
||||
None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))),
|
||||
None => {
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"Required attribute '",
|
||||
$attr,
|
||||
"' missing."
|
||||
)))
|
||||
}
|
||||
}
|
||||
);
|
||||
($elem:ident, $attr:tt, default, $value:ident, $func:expr) => (
|
||||
};
|
||||
($elem:ident, $attr:tt, default, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some($value) => $func,
|
||||
None => ::std::default::Default::default(),
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_attribute {
|
||||
|
@ -211,38 +217,54 @@ macro_rules! generate_attribute_enum {
|
|||
}
|
||||
|
||||
macro_rules! check_self {
|
||||
($elem:ident, $name:tt, $ns:ident) => (
|
||||
($elem:ident, $name:tt, $ns:ident) => {
|
||||
check_self!($elem, $name, $ns, $name);
|
||||
);
|
||||
($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => (
|
||||
};
|
||||
($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
|
||||
if !$elem.is($name, crate::ns::$ns) {
|
||||
return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"This is not a ",
|
||||
$pretty_name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_ns_only {
|
||||
($elem:ident, $name:tt, $ns:ident) => (
|
||||
($elem:ident, $name:tt, $ns:ident) => {
|
||||
if !$elem.has_ns(crate::ns::$ns) {
|
||||
return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"This is not a ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_no_children {
|
||||
($elem:ident, $name:tt) => (
|
||||
($elem:ident, $name:tt) => {
|
||||
for _ in $elem.children() {
|
||||
return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"Unknown child in ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_no_attributes {
|
||||
($elem:ident, $name:tt) => (
|
||||
($elem:ident, $name:tt) => {
|
||||
for _ in $elem.attrs() {
|
||||
return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"Unknown attribute in ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_no_unknown_attributes {
|
||||
|
@ -351,76 +373,95 @@ macro_rules! start_decl {
|
|||
}
|
||||
|
||||
macro_rules! start_parse_elem {
|
||||
($temp:ident: Vec) => (
|
||||
($temp:ident: Vec) => {
|
||||
let mut $temp = Vec::new();
|
||||
);
|
||||
($temp:ident: Option) => (
|
||||
};
|
||||
($temp:ident: Option) => {
|
||||
let mut $temp = None;
|
||||
);
|
||||
($temp:ident: Required) => (
|
||||
};
|
||||
($temp:ident: Required) => {
|
||||
let mut $temp = None;
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! do_parse {
|
||||
($elem:ident, Element) => (
|
||||
($elem:ident, Element) => {
|
||||
$elem.clone()
|
||||
);
|
||||
($elem:ident, String) => (
|
||||
};
|
||||
($elem:ident, String) => {
|
||||
$elem.text()
|
||||
);
|
||||
($elem:ident, $constructor:ident) => (
|
||||
};
|
||||
($elem:ident, $constructor:ident) => {
|
||||
$constructor::try_from($elem.clone())?
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! do_parse_elem {
|
||||
($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
|
||||
($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
$temp.push(do_parse!($elem, $constructor));
|
||||
);
|
||||
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
|
||||
};
|
||||
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
if $temp.is_some() {
|
||||
return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"Element ",
|
||||
$parent_name,
|
||||
" must not have more than one ",
|
||||
$name,
|
||||
" child."
|
||||
)));
|
||||
}
|
||||
$temp = Some(do_parse!($elem, $constructor));
|
||||
);
|
||||
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
|
||||
};
|
||||
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
if $temp.is_some() {
|
||||
return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
|
||||
return Err(crate::error::Error::ParseError(concat!(
|
||||
"Element ",
|
||||
$parent_name,
|
||||
" must not have more than one ",
|
||||
$name,
|
||||
" child."
|
||||
)));
|
||||
}
|
||||
$temp = Some(do_parse!($elem, $constructor));
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! finish_parse_elem {
|
||||
($temp:ident: Vec = $name:tt, $parent_name:tt) => (
|
||||
($temp:ident: Vec = $name:tt, $parent_name:tt) => {
|
||||
$temp
|
||||
);
|
||||
($temp:ident: Option = $name:tt, $parent_name:tt) => (
|
||||
};
|
||||
($temp:ident: Option = $name:tt, $parent_name:tt) => {
|
||||
$temp
|
||||
);
|
||||
($temp:ident: Required = $name:tt, $parent_name:tt) => (
|
||||
$temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))?
|
||||
);
|
||||
};
|
||||
($temp:ident: Required = $name:tt, $parent_name:tt) => {
|
||||
$temp.ok_or(crate::error::Error::ParseError(concat!(
|
||||
"Missing child ",
|
||||
$name,
|
||||
" in ",
|
||||
$parent_name,
|
||||
" element."
|
||||
)))?
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_serialiser {
|
||||
($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
|
||||
($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
|
||||
::minidom::Element::builder($name)
|
||||
.ns(crate::ns::$ns)
|
||||
.append($parent.$elem)
|
||||
.build()
|
||||
);
|
||||
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
|
||||
$parent.$elem.map(|elem|
|
||||
};
|
||||
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
|
||||
$parent.$elem.map(|elem| {
|
||||
::minidom::Element::builder($name)
|
||||
.ns(crate::ns::$ns)
|
||||
.append(elem)
|
||||
.build())
|
||||
);
|
||||
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
|
||||
.build()
|
||||
})
|
||||
};
|
||||
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||
$parent.$elem
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_element {
|
||||
|
|
112
src/mam.rs
112
src/mam.rs
|
@ -4,21 +4,17 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::message::MessagePayload;
|
||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
||||
use crate::data_forms::DataForm;
|
||||
use crate::rsm::{SetQuery, SetResult};
|
||||
use crate::error::Error;
|
||||
use crate::forwarding::Forwarded;
|
||||
use crate::pubsub::NodeName;
|
||||
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use crate::message::MessagePayload;
|
||||
use crate::ns;
|
||||
use crate::pubsub::NodeName;
|
||||
use crate::rsm::{SetQuery, SetResult};
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_id!(
|
||||
/// An identifier matching a result message to the query requesting it.
|
||||
|
@ -70,7 +66,9 @@ impl MessagePayload for Result_ {}
|
|||
|
||||
generate_attribute!(
|
||||
/// True when the end of a MAM query has been reached.
|
||||
Complete, "complete", bool
|
||||
Complete,
|
||||
"complete",
|
||||
bool
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -133,8 +131,8 @@ impl TryFrom<Element> for Prefs {
|
|||
fn try_from(elem: Element) -> Result<Prefs, Error> {
|
||||
check_self!(elem, "prefs", MAM);
|
||||
check_no_unknown_attributes!(elem, "prefs", ["default"]);
|
||||
let mut always = vec!();
|
||||
let mut never = vec!();
|
||||
let mut always = vec![];
|
||||
let mut never = vec![];
|
||||
for child in elem.children() {
|
||||
if child.is("always", ns::MAM) {
|
||||
for jid_elem in child.children() {
|
||||
|
@ -155,7 +153,11 @@ impl TryFrom<Element> for Prefs {
|
|||
}
|
||||
}
|
||||
let default_ = get_attr!(elem, "default", required);
|
||||
Ok(Prefs { default_, always, never })
|
||||
Ok(Prefs {
|
||||
default_,
|
||||
always,
|
||||
never,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,26 +165,27 @@ fn serialise_jid_list(name: &str, jids: Vec<Jid>) -> Option<Element> {
|
|||
if jids.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Element::builder(name)
|
||||
.ns(ns::MAM)
|
||||
.append(jids.into_iter()
|
||||
.map(|jid| Element::builder("jid")
|
||||
.ns(ns::MAM)
|
||||
.append(jid)
|
||||
.build())
|
||||
.collect::<Vec<_>>())
|
||||
.build())
|
||||
Some(
|
||||
Element::builder(name)
|
||||
.ns(ns::MAM)
|
||||
.append(
|
||||
jids.into_iter()
|
||||
.map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Prefs> for Element {
|
||||
fn from(prefs: Prefs) -> Element {
|
||||
Element::builder("prefs")
|
||||
.ns(ns::MAM)
|
||||
.attr("default", prefs.default_)
|
||||
.append(serialise_jid_list("always", prefs.always))
|
||||
.append(serialise_jid_list("never", prefs.never))
|
||||
.build()
|
||||
.ns(ns::MAM)
|
||||
.attr("default", prefs.default_)
|
||||
.append(serialise_jid_list("always", prefs.always))
|
||||
.append(serialise_jid_list("never", prefs.never))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,7 +236,9 @@ mod tests {
|
|||
</message>
|
||||
</forwarded>
|
||||
</result>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = r#"
|
||||
<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
|
||||
|
@ -257,7 +262,9 @@ mod tests {
|
|||
<last>09af3-cc343-b409f</last>
|
||||
</set>
|
||||
</fin>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
Fin::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
@ -274,7 +281,9 @@ mod tests {
|
|||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
Query::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
@ -294,13 +303,17 @@ mod tests {
|
|||
<max>10</max>
|
||||
</set>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
Query::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prefs_get() {
|
||||
let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>".parse().unwrap();
|
||||
let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let prefs = Prefs::try_from(elem).unwrap();
|
||||
assert_eq!(prefs.always, vec!());
|
||||
assert_eq!(prefs.never, vec!());
|
||||
|
@ -310,7 +323,9 @@ mod tests {
|
|||
<always/>
|
||||
<never/>
|
||||
</prefs>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let prefs = Prefs::try_from(elem).unwrap();
|
||||
assert_eq!(prefs.always, vec!());
|
||||
assert_eq!(prefs.never, vec!());
|
||||
|
@ -327,10 +342,18 @@ mod tests {
|
|||
<jid>montague@montague.lit</jid>
|
||||
</never>
|
||||
</prefs>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let prefs = Prefs::try_from(elem).unwrap();
|
||||
assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap()));
|
||||
assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap()));
|
||||
assert_eq!(
|
||||
prefs.always,
|
||||
vec!(Jid::from_str("romeo@montague.lit").unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
prefs.never,
|
||||
vec!(Jid::from_str("montague@montague.lit").unwrap())
|
||||
);
|
||||
|
||||
let elem2 = Element::from(prefs.clone());
|
||||
println!("{:?}", elem2);
|
||||
|
@ -342,7 +365,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Query::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -354,7 +379,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
|
||||
let replace = Query { queryid: None, node: None, form: None, set: None };
|
||||
let replace = Query {
|
||||
queryid: None,
|
||||
node: None,
|
||||
form: None,
|
||||
set: None,
|
||||
};
|
||||
let elem2 = replace.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
|
@ -45,11 +45,11 @@ generate_element!(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use crate::data_forms::DataForm;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::error::Error as StdError;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -76,7 +76,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_width_height() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let media = MediaElement::try_from(elem).unwrap();
|
||||
assert_eq!(media.width.unwrap(), 32);
|
||||
assert_eq!(media.height.unwrap(), 32);
|
||||
|
@ -93,15 +95,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_width_height() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let error = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(error.description(), "cannot parse integer from empty string");
|
||||
assert_eq!(
|
||||
error.description(),
|
||||
"cannot parse integer from empty string"
|
||||
);
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let error = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
|
@ -109,15 +118,22 @@ mod tests {
|
|||
};
|
||||
assert_eq!(error.description(), "invalid digit found in string");
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let error = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(error.description(), "cannot parse integer from empty string");
|
||||
assert_eq!(
|
||||
error.description(),
|
||||
"cannot parse integer from empty string"
|
||||
);
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let error = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
|
@ -128,7 +144,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_unknown_child() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -139,7 +157,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_bad_uri() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -147,7 +168,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Required attribute 'type' missing.");
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MediaElement::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -169,17 +192,28 @@ mod tests {
|
|||
<uri type='audio/mpeg'>
|
||||
http://victim.example.com/challenges/speech.mp3?F3A6292C
|
||||
</uri>
|
||||
</media>"#.parse().unwrap();
|
||||
</media>"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let media = MediaElement::try_from(elem).unwrap();
|
||||
assert!(media.width.is_none());
|
||||
assert!(media.height.is_none());
|
||||
assert_eq!(media.uris.len(), 3);
|
||||
assert_eq!(media.uris[0].type_, "audio/x-wav");
|
||||
assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
|
||||
assert_eq!(
|
||||
media.uris[0].uri,
|
||||
"http://victim.example.com/challenges/speech.wav?F3A6292C"
|
||||
);
|
||||
assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
|
||||
assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
|
||||
assert_eq!(
|
||||
media.uris[1].uri,
|
||||
"cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
|
||||
);
|
||||
assert_eq!(media.uris[2].type_, "audio/mpeg");
|
||||
assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
|
||||
assert_eq!(
|
||||
media.uris[2].uri,
|
||||
"http://victim.example.com/challenges/speech.mp3?F3A6292C"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -200,15 +234,23 @@ mod tests {
|
|||
</media>
|
||||
</field>
|
||||
[ ... ]
|
||||
</x>"#.parse().unwrap();
|
||||
</x>"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let form = DataForm::try_from(elem).unwrap();
|
||||
assert_eq!(form.fields.len(), 1);
|
||||
assert_eq!(form.fields[0].var, "ocr");
|
||||
assert_eq!(form.fields[0].media[0].width, Some(290));
|
||||
assert_eq!(form.fields[0].media[0].height, Some(80));
|
||||
assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
|
||||
assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
|
||||
assert_eq!(
|
||||
form.fields[0].media[0].uris[0].uri,
|
||||
"http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
|
||||
);
|
||||
assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
|
||||
assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
|
||||
assert_eq!(
|
||||
form.fields[0].media[0].uris[1].uri,
|
||||
"cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
140
src/message.rs
140
src/message.rs
|
@ -4,16 +4,12 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use std::collections::BTreeMap;
|
||||
use try_from::TryFrom;
|
||||
|
||||
/// Should be implemented on every known payload of a `<message/>`.
|
||||
pub trait MessagePayload: TryFrom<Element> + Into<Element> {}
|
||||
|
@ -44,18 +40,24 @@ type Lang = String;
|
|||
generate_elem_id!(
|
||||
/// Represents one `<body/>` element, that is the free form text content of
|
||||
/// a message.
|
||||
Body, "body", DEFAULT_NS
|
||||
Body,
|
||||
"body",
|
||||
DEFAULT_NS
|
||||
);
|
||||
|
||||
generate_elem_id!(
|
||||
/// Defines the subject of a room, or of an email-like normal message.
|
||||
Subject, "subject", DEFAULT_NS
|
||||
Subject,
|
||||
"subject",
|
||||
DEFAULT_NS
|
||||
);
|
||||
|
||||
generate_elem_id!(
|
||||
/// A thread identifier, so that other people can specify to which message
|
||||
/// they are replying.
|
||||
Thread, "thread", DEFAULT_NS
|
||||
Thread,
|
||||
"thread",
|
||||
DEFAULT_NS
|
||||
);
|
||||
|
||||
/// The main structure representing the `<message/>` stanza.
|
||||
|
@ -102,11 +104,14 @@ impl Message {
|
|||
bodies: BTreeMap::new(),
|
||||
subjects: BTreeMap::new(),
|
||||
thread: None,
|
||||
payloads: vec!(),
|
||||
payloads: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_best<'a, T>(map: &'a BTreeMap<Lang, T>, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> {
|
||||
fn get_best<'a, T>(
|
||||
map: &'a BTreeMap<Lang, T>,
|
||||
preferred_langs: Vec<&str>,
|
||||
) -> Option<(Lang, &'a T)> {
|
||||
if map.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -156,21 +161,25 @@ impl TryFrom<Element> for Message {
|
|||
let mut bodies = BTreeMap::new();
|
||||
let mut subjects = BTreeMap::new();
|
||||
let mut thread = None;
|
||||
let mut payloads = vec!();
|
||||
let mut payloads = vec![];
|
||||
for elem in root.children() {
|
||||
if elem.is("body", ns::DEFAULT_NS) {
|
||||
check_no_children!(elem, "body");
|
||||
let lang = get_attr!(elem, "xml:lang", default);
|
||||
let body = Body(elem.text());
|
||||
if bodies.insert(lang, body).is_some() {
|
||||
return Err(Error::ParseError("Body element present twice for the same xml:lang."));
|
||||
return Err(Error::ParseError(
|
||||
"Body element present twice for the same xml:lang.",
|
||||
));
|
||||
}
|
||||
} else if elem.is("subject", ns::DEFAULT_NS) {
|
||||
check_no_children!(elem, "subject");
|
||||
let lang = get_attr!(elem, "xml:lang", default);
|
||||
let subject = Subject(elem.text());
|
||||
if subjects.insert(lang, subject).is_some() {
|
||||
return Err(Error::ParseError("Subject element present twice for the same xml:lang."));
|
||||
return Err(Error::ParseError(
|
||||
"Subject element present twice for the same xml:lang.",
|
||||
));
|
||||
}
|
||||
} else if elem.is("thread", ns::DEFAULT_NS) {
|
||||
if thread.is_some() {
|
||||
|
@ -198,41 +207,55 @@ impl TryFrom<Element> for Message {
|
|||
impl From<Message> for Element {
|
||||
fn from(message: Message) -> Element {
|
||||
Element::builder("message")
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", message.from)
|
||||
.attr("to", message.to)
|
||||
.attr("id", message.id)
|
||||
.attr("type", message.type_)
|
||||
.append(message.subjects.into_iter()
|
||||
.map(|(lang, subject)| {
|
||||
let mut subject = Element::from(subject);
|
||||
subject.set_attr("xml:lang", match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
});
|
||||
subject
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
.append(message.bodies.into_iter()
|
||||
.map(|(lang, body)| {
|
||||
let mut body = Element::from(body);
|
||||
body.set_attr("xml:lang", match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
});
|
||||
body
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
.append(message.payloads)
|
||||
.build()
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", message.from)
|
||||
.attr("to", message.to)
|
||||
.attr("id", message.id)
|
||||
.attr("type", message.type_)
|
||||
.append(
|
||||
message
|
||||
.subjects
|
||||
.into_iter()
|
||||
.map(|(lang, subject)| {
|
||||
let mut subject = Element::from(subject);
|
||||
subject.set_attr(
|
||||
"xml:lang",
|
||||
match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
},
|
||||
);
|
||||
subject
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.append(
|
||||
message
|
||||
.bodies
|
||||
.into_iter()
|
||||
.map(|(lang, body)| {
|
||||
let mut body = Element::from(body);
|
||||
body.set_attr(
|
||||
"xml:lang",
|
||||
match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
},
|
||||
);
|
||||
body
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.append(message.payloads)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -259,7 +282,9 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<message xmlns='jabber:component:accept'/>".parse().unwrap();
|
||||
let elem: Element = "<message xmlns='jabber:component:accept'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let message = Message::try_from(elem).unwrap();
|
||||
assert_eq!(message.from, None);
|
||||
assert_eq!(message.to, None);
|
||||
|
@ -273,7 +298,9 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<message xmlns='jabber:component:accept'/>".parse().unwrap();
|
||||
let elem: Element = "<message xmlns='jabber:component:accept'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let mut message = Message::new(None);
|
||||
message.type_ = MessageType::Normal;
|
||||
let elem2 = message.into();
|
||||
|
@ -291,7 +318,7 @@ mod tests {
|
|||
assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap());
|
||||
|
||||
{
|
||||
let (lang, body) = message.get_best_body(vec!("en")).unwrap();
|
||||
let (lang, body) = message.get_best_body(vec!["en"]).unwrap();
|
||||
assert_eq!(lang, "");
|
||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||
}
|
||||
|
@ -307,7 +334,9 @@ mod tests {
|
|||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<message xmlns='jabber:component:accept' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
|
||||
let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap()));
|
||||
message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap());
|
||||
message
|
||||
.bodies
|
||||
.insert(String::from(""), Body::from_str("Hello world!").unwrap());
|
||||
let elem2 = message.into();
|
||||
assert!(elem.compare_to(&elem2));
|
||||
}
|
||||
|
@ -320,10 +349,13 @@ mod tests {
|
|||
let elem: Element = "<message xmlns='jabber:component:accept' to='coucou@example.org' type='chat'><subject>Hello world!</subject></message>".parse().unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let message = Message::try_from(elem).unwrap();
|
||||
assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap());
|
||||
assert_eq!(
|
||||
message.subjects[""],
|
||||
Subject::from_str("Hello world!").unwrap()
|
||||
);
|
||||
|
||||
{
|
||||
let (lang, subject) = message.get_best_subject(vec!("en")).unwrap();
|
||||
let (lang, subject) = message.get_best_subject(vec!["en"]).unwrap();
|
||||
assert_eq!(lang, "");
|
||||
assert_eq!(subject, &Subject::from_str("Hello world!").unwrap());
|
||||
}
|
||||
|
@ -342,28 +374,28 @@ mod tests {
|
|||
|
||||
// Tests basic feature.
|
||||
{
|
||||
let (lang, body) = message.get_best_body(vec!("fr")).unwrap();
|
||||
let (lang, body) = message.get_best_body(vec!["fr"]).unwrap();
|
||||
assert_eq!(lang, "fr");
|
||||
assert_eq!(body, &Body::from_str("Salut le monde !").unwrap());
|
||||
}
|
||||
|
||||
// Tests order.
|
||||
{
|
||||
let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap();
|
||||
let (lang, body) = message.get_best_body(vec!["en", "de"]).unwrap();
|
||||
assert_eq!(lang, "de");
|
||||
assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap());
|
||||
}
|
||||
|
||||
// Tests fallback.
|
||||
{
|
||||
let (lang, body) = message.get_best_body(vec!()).unwrap();
|
||||
let (lang, body) = message.get_best_body(vec![]).unwrap();
|
||||
assert_eq!(lang, "");
|
||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||
}
|
||||
|
||||
// Tests fallback.
|
||||
{
|
||||
let (lang, body) = message.get_best_body(vec!("ja")).unwrap();
|
||||
let (lang, body) = message.get_best_body(vec!["ja"]).unwrap();
|
||||
assert_eq!(lang, "");
|
||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ impl MessagePayload for Replace {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -39,13 +39,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Replace::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Replace::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -56,7 +60,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'><coucou/></replace>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'><coucou/></replace>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Replace::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -67,7 +73,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_id() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Replace::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -78,8 +86,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>".parse().unwrap();
|
||||
let replace = Replace { id: String::from("coucou") };
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let replace = Replace {
|
||||
id: String::from("coucou"),
|
||||
};
|
||||
let elem2 = replace.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
14
src/mood.rs
14
src/mood.rs
|
@ -263,14 +263,16 @@ generate_element_enum!(
|
|||
|
||||
generate_elem_id!(
|
||||
/// Free-form text description of the mood.
|
||||
Text, "text", MOOD
|
||||
Text,
|
||||
"text",
|
||||
MOOD
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -288,14 +290,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<happy xmlns='http://jabber.org/protocol/mood'/>".parse().unwrap();
|
||||
let elem: Element = "<happy xmlns='http://jabber.org/protocol/mood'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let mood = MoodEnum::try_from(elem).unwrap();
|
||||
assert_eq!(mood, MoodEnum::Happy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text() {
|
||||
let elem: Element = "<text xmlns='http://jabber.org/protocol/mood'>Yay!</text>".parse().unwrap();
|
||||
let elem: Element = "<text xmlns='http://jabber.org/protocol/mood'>Yay!</text>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let text = Text::try_from(elem).unwrap();
|
||||
assert_eq!(text.0, String::from("Yay!"));
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::date::DateTime;
|
||||
use crate::presence::PresencePayload;
|
||||
|
||||
generate_element!(
|
||||
/// Represents the query for messages before our join.
|
||||
|
@ -102,21 +102,25 @@ impl Muc {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use std::str::FromStr;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_muc_simple() {
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Muc::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_muc_invalid_child() {
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>".parse().unwrap();
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Muc::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -127,7 +131,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_muc_serialise() {
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let muc = Muc {
|
||||
password: None,
|
||||
history: None,
|
||||
|
@ -138,7 +144,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_muc_invalid_attribute() {
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Muc::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -153,7 +161,8 @@ mod tests {
|
|||
<x xmlns='http://jabber.org/protocol/muc'>
|
||||
<password>coucou</password>
|
||||
</x>"
|
||||
.parse().unwrap();
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let muc = Muc::try_from(elem).unwrap();
|
||||
assert_eq!(muc.password, Some("coucou".to_owned()));
|
||||
|
@ -168,7 +177,8 @@ mod tests {
|
|||
<x xmlns='http://jabber.org/protocol/muc'>
|
||||
<history maxstanzas='0'/>
|
||||
</x>"
|
||||
.parse().unwrap();
|
||||
.parse()
|
||||
.unwrap();
|
||||
let muc = Muc::try_from(elem).unwrap();
|
||||
let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0));
|
||||
assert_eq!(muc, muc2);
|
||||
|
@ -183,8 +193,12 @@ mod tests {
|
|||
<x xmlns='http://jabber.org/protocol/muc'>
|
||||
<history since='1970-01-01T00:00:00Z'/>
|
||||
</x>"
|
||||
.parse().unwrap();
|
||||
.parse()
|
||||
.unwrap();
|
||||
let muc = Muc::try_from(elem).unwrap();
|
||||
assert_eq!(muc.history.unwrap().since.unwrap(), DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap());
|
||||
assert_eq!(
|
||||
muc.history.unwrap().since.unwrap(),
|
||||
DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
183
src/muc/user.rs
183
src/muc/user.rs
|
@ -5,15 +5,11 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
generate_attribute_enum!(
|
||||
/// Lists all of the possible status codes used in MUC presences.
|
||||
|
@ -103,9 +99,11 @@ impl TryFrom<Element> for Actor {
|
|||
let nick = get_attr!(elem, "nick", optional);
|
||||
|
||||
match (jid, nick) {
|
||||
(Some(_), Some(_))
|
||||
| (None, None) =>
|
||||
return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")),
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
return Err(Error::ParseError(
|
||||
"Either 'jid' or 'nick' attribute is required.",
|
||||
))
|
||||
}
|
||||
(Some(jid), _) => Ok(Actor::Jid(jid)),
|
||||
(_, Some(nick)) => Ok(Actor::Nick(nick)),
|
||||
}
|
||||
|
@ -119,7 +117,8 @@ impl From<Actor> for Element {
|
|||
(match actor {
|
||||
Actor::Jid(jid) => elem.attr("jid", jid),
|
||||
Actor::Nick(nick) => elem.attr("nick", nick),
|
||||
}).build()
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +134,9 @@ generate_element!(
|
|||
|
||||
generate_elem_id!(
|
||||
/// A reason for inviting, declining, etc. a request.
|
||||
Reason, "reason", MUC_USER
|
||||
Reason,
|
||||
"reason",
|
||||
MUC_USER
|
||||
);
|
||||
|
||||
generate_attribute!(
|
||||
|
@ -237,14 +238,16 @@ generate_element!(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::error::Error as StdError;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "
|
||||
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
MucUser::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
@ -256,7 +259,9 @@ mod tests {
|
|||
<status code='102'/>
|
||||
<item affiliation='member' role='moderator'/>
|
||||
</x>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let muc_user = MucUser::try_from(elem).unwrap();
|
||||
assert_eq!(muc_user.status.len(), 2);
|
||||
assert_eq!(muc_user.status[0], Status::AffiliationChange);
|
||||
|
@ -272,7 +277,9 @@ mod tests {
|
|||
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||
<coucou/>
|
||||
</x>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MucUser::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -285,8 +292,13 @@ mod tests {
|
|||
fn test_serialise() {
|
||||
let elem: Element = "
|
||||
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
||||
".parse().unwrap();
|
||||
let muc = MucUser { status: vec!(), items: vec!() };
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let muc = MucUser {
|
||||
status: vec![],
|
||||
items: vec![],
|
||||
};
|
||||
let elem2 = muc.into();
|
||||
assert!(elem.compare_to(&elem2));
|
||||
}
|
||||
|
@ -295,7 +307,9 @@ mod tests {
|
|||
fn test_invalid_attribute() {
|
||||
let elem: Element = "
|
||||
<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = MucUser::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -308,7 +322,9 @@ mod tests {
|
|||
fn test_status_simple() {
|
||||
let elem: Element = "
|
||||
<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Status::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
@ -316,7 +332,9 @@ mod tests {
|
|||
fn test_status_invalid() {
|
||||
let elem: Element = "
|
||||
<status xmlns='http://jabber.org/protocol/muc#user'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Status::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -331,7 +349,9 @@ mod tests {
|
|||
<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
|
||||
<foo/>
|
||||
</status>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Status::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -344,7 +364,9 @@ mod tests {
|
|||
fn test_status_simple_code() {
|
||||
let elem: Element = "
|
||||
<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let status = Status::try_from(elem).unwrap();
|
||||
assert_eq!(status, Status::Kicked);
|
||||
}
|
||||
|
@ -353,7 +375,9 @@ mod tests {
|
|||
fn test_status_invalid_code() {
|
||||
let elem: Element = "
|
||||
<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Status::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -366,7 +390,9 @@ mod tests {
|
|||
fn test_status_invalid_code2() {
|
||||
let elem: Element = "
|
||||
<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Status::try_from(elem).unwrap_err();
|
||||
let error = match error {
|
||||
Error::ParseIntError(error) => error,
|
||||
|
@ -379,7 +405,9 @@ mod tests {
|
|||
fn test_actor_required_attributes() {
|
||||
let elem: Element = "
|
||||
<actor xmlns='http://jabber.org/protocol/muc#user'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Actor::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -394,7 +422,9 @@ mod tests {
|
|||
<actor xmlns='http://jabber.org/protocol/muc#user'
|
||||
jid='foo@bar/baz'
|
||||
nick='baz'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Actor::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -408,7 +438,9 @@ mod tests {
|
|||
let elem: Element = "
|
||||
<actor xmlns='http://jabber.org/protocol/muc#user'
|
||||
jid='foo@bar/baz'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let actor = Actor::try_from(elem).unwrap();
|
||||
let jid = match actor {
|
||||
Actor::Jid(jid) => jid,
|
||||
|
@ -421,7 +453,9 @@ mod tests {
|
|||
fn test_actor_nick() {
|
||||
let elem: Element = "
|
||||
<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let actor = Actor::try_from(elem).unwrap();
|
||||
let nick = match actor {
|
||||
Actor::Nick(nick) => nick,
|
||||
|
@ -434,7 +468,9 @@ mod tests {
|
|||
fn test_continue_simple() {
|
||||
let elem: Element = "
|
||||
<continue xmlns='http://jabber.org/protocol/muc#user'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Continue::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
@ -443,7 +479,9 @@ mod tests {
|
|||
let elem: Element = "
|
||||
<continue xmlns='http://jabber.org/protocol/muc#user'
|
||||
thread='foo'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let continue_ = Continue::try_from(elem).unwrap();
|
||||
assert_eq!(continue_.thread, Some("foo".to_owned()));
|
||||
}
|
||||
|
@ -454,7 +492,9 @@ mod tests {
|
|||
<continue xmlns='http://jabber.org/protocol/muc#user'>
|
||||
<foobar/>
|
||||
</continue>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let continue_ = Continue::try_from(elem).unwrap_err();
|
||||
let message = match continue_ {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -467,7 +507,8 @@ mod tests {
|
|||
fn test_reason_simple() {
|
||||
let elem: Element = "
|
||||
<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
|
||||
.parse().unwrap();
|
||||
.parse()
|
||||
.unwrap();
|
||||
let reason = Reason::try_from(elem).unwrap();
|
||||
assert_eq!(reason.0, "Reason".to_owned());
|
||||
}
|
||||
|
@ -476,7 +517,9 @@ mod tests {
|
|||
fn test_reason_invalid_attribute() {
|
||||
let elem: Element = "
|
||||
<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Reason::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -491,7 +534,9 @@ mod tests {
|
|||
<reason xmlns='http://jabber.org/protocol/muc#user'>
|
||||
<foobar/>
|
||||
</reason>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Reason::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -501,11 +546,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_invalid_attr(){
|
||||
fn test_item_invalid_attr() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
foo='bar'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Item::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -515,21 +562,25 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_affiliation_role_attr(){
|
||||
fn test_item_affiliation_role_attr() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'
|
||||
role='moderator'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Item::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_affiliation_role_invalid_attr(){
|
||||
fn test_item_affiliation_role_invalid_attr() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Item::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -539,13 +590,15 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_nick_attr(){
|
||||
fn test_item_nick_attr() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'
|
||||
role='moderator'
|
||||
nick='foobar'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let item = Item::try_from(elem).unwrap();
|
||||
match item {
|
||||
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
|
||||
|
@ -553,65 +606,79 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_affiliation_role_invalid_attr2(){
|
||||
fn test_item_affiliation_role_invalid_attr2() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
role='moderator'/>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Item::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned());
|
||||
assert_eq!(
|
||||
message,
|
||||
"Required attribute 'affiliation' missing.".to_owned()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_role_actor_child(){
|
||||
fn test_item_role_actor_child() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'
|
||||
role='moderator'>
|
||||
<actor nick='foobar'/>
|
||||
</item>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let item = Item::try_from(elem).unwrap();
|
||||
match item {
|
||||
Item { actor, .. } =>
|
||||
assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
|
||||
Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_role_continue_child(){
|
||||
fn test_item_role_continue_child() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'
|
||||
role='moderator'>
|
||||
<continue thread='foobar'/>
|
||||
</item>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let item = Item::try_from(elem).unwrap();
|
||||
let continue_1 = Continue { thread: Some("foobar".to_owned()) };
|
||||
let continue_1 = Continue {
|
||||
thread: Some("foobar".to_owned()),
|
||||
};
|
||||
match item {
|
||||
Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread),
|
||||
Item {
|
||||
continue_: Some(continue_2),
|
||||
..
|
||||
} => assert_eq!(continue_2.thread, continue_1.thread),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_role_reason_child(){
|
||||
fn test_item_role_reason_child() {
|
||||
let elem: Element = "
|
||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||
affiliation='member'
|
||||
role='moderator'>
|
||||
<reason>foobar</reason>
|
||||
</item>
|
||||
".parse().unwrap();
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let item = Item::try_from(elem).unwrap();
|
||||
match item {
|
||||
Item { reason, .. } =>
|
||||
assert_eq!(reason, Some(Reason("foobar".to_owned()))),
|
||||
Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
24
src/nick.rs
24
src/nick.rs
|
@ -6,15 +6,17 @@
|
|||
|
||||
generate_elem_id!(
|
||||
/// Represents a global, memorable, friendly or informal name chosen by a user.
|
||||
Nick, "nick", NICK
|
||||
Nick,
|
||||
"nick",
|
||||
NICK
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -30,7 +32,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>".parse().unwrap();
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let nick = Nick::try_from(elem).unwrap();
|
||||
assert_eq!(&nick.0, "Link Mauve");
|
||||
}
|
||||
|
@ -38,13 +42,17 @@ mod tests {
|
|||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem1 = Element::from(Nick(String::from("Link Mauve")));
|
||||
let elem2: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>".parse().unwrap();
|
||||
let elem2: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(elem1, elem2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>".parse().unwrap();
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Nick::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -55,7 +63,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Nick::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
12
src/ping.rs
12
src/ping.rs
|
@ -10,7 +10,9 @@ use crate::iq::IqGetPayload;
|
|||
generate_empty_element!(
|
||||
/// Represents a ping to the recipient, which must be answered with an
|
||||
/// empty `<iq/>` or with an error.
|
||||
Ping, "ping", PING
|
||||
Ping,
|
||||
"ping",
|
||||
PING
|
||||
);
|
||||
|
||||
impl IqGetPayload for Ping {}
|
||||
|
@ -18,9 +20,9 @@ impl IqGetPayload for Ping {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
|
@ -42,7 +44,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>".parse().unwrap();
|
||||
let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Ping::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
222
src/presence.rs
222
src/presence.rs
|
@ -5,11 +5,11 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter};
|
||||
use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements};
|
||||
|
||||
use jid::Jid;
|
||||
|
||||
|
@ -68,14 +68,15 @@ impl IntoElements for Show {
|
|||
}
|
||||
emitter.append_child(
|
||||
Element::builder("show")
|
||||
.append(match self {
|
||||
Show::None => unreachable!(),
|
||||
Show::Away => Some("away"),
|
||||
Show::Chat => Some("chat"),
|
||||
Show::Dnd => Some("dnd"),
|
||||
Show::Xa => Some("xa"),
|
||||
})
|
||||
.build())
|
||||
.append(match self {
|
||||
Show::None => unreachable!(),
|
||||
Show::Away => Some("away"),
|
||||
Show::Chat => Some("chat"),
|
||||
Show::Dnd => Some("dnd"),
|
||||
Show::Xa => Some("xa"),
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,24 +137,31 @@ impl FromStr for Type {
|
|||
"unsubscribe" => Type::Unsubscribe,
|
||||
"unsubscribed" => Type::Unsubscribed,
|
||||
|
||||
_ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")),
|
||||
_ => {
|
||||
return Err(Error::ParseError(
|
||||
"Invalid 'type' attribute on presence element.",
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAttributeValue for Type {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
Some(match self {
|
||||
Type::None => return None,
|
||||
Some(
|
||||
match self {
|
||||
Type::None => return None,
|
||||
|
||||
Type::Error => "error",
|
||||
Type::Probe => "probe",
|
||||
Type::Subscribe => "subscribe",
|
||||
Type::Subscribed => "subscribed",
|
||||
Type::Unavailable => "unavailable",
|
||||
Type::Unsubscribe => "unsubscribe",
|
||||
Type::Unsubscribed => "unsubscribed",
|
||||
}.to_owned())
|
||||
Type::Error => "error",
|
||||
Type::Probe => "probe",
|
||||
Type::Subscribe => "subscribe",
|
||||
Type::Subscribed => "subscribed",
|
||||
Type::Unavailable => "unavailable",
|
||||
Type::Unsubscribe => "unsubscribe",
|
||||
Type::Unsubscribed => "unsubscribed",
|
||||
}
|
||||
.to_owned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +205,7 @@ impl Presence {
|
|||
show: Show::None,
|
||||
statuses: BTreeMap::new(),
|
||||
priority: 0i8,
|
||||
payloads: vec!(),
|
||||
payloads: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,12 +274,14 @@ impl TryFrom<Element> for Presence {
|
|||
show: Show::None,
|
||||
statuses: BTreeMap::new(),
|
||||
priority: 0i8,
|
||||
payloads: vec!(),
|
||||
payloads: vec![],
|
||||
};
|
||||
for elem in root.children() {
|
||||
if elem.is("show", ns::DEFAULT_NS) {
|
||||
if show.is_some() {
|
||||
return Err(Error::ParseError("More than one show element in a presence."));
|
||||
return Err(Error::ParseError(
|
||||
"More than one show element in a presence.",
|
||||
));
|
||||
}
|
||||
check_no_attributes!(elem, "show");
|
||||
check_no_children!(elem, "show");
|
||||
|
@ -281,11 +291,15 @@ impl TryFrom<Element> for Presence {
|
|||
check_no_children!(elem, "status");
|
||||
let lang = get_attr!(elem, "xml:lang", default);
|
||||
if presence.statuses.insert(lang, elem.text()).is_some() {
|
||||
return Err(Error::ParseError("Status element present twice for the same xml:lang."));
|
||||
return Err(Error::ParseError(
|
||||
"Status element present twice for the same xml:lang.",
|
||||
));
|
||||
}
|
||||
} else if elem.is("priority", ns::DEFAULT_NS) {
|
||||
if priority.is_some() {
|
||||
return Err(Error::ParseError("More than one priority element in a presence."));
|
||||
return Err(Error::ParseError(
|
||||
"More than one priority element in a presence.",
|
||||
));
|
||||
}
|
||||
check_no_attributes!(elem, "priority");
|
||||
check_no_children!(elem, "priority");
|
||||
|
@ -307,24 +321,37 @@ impl TryFrom<Element> for Presence {
|
|||
impl From<Presence> for Element {
|
||||
fn from(presence: Presence) -> Element {
|
||||
Element::builder("presence")
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", presence.from)
|
||||
.attr("to", presence.to)
|
||||
.attr("id", presence.id)
|
||||
.attr("type", presence.type_)
|
||||
.append(presence.show)
|
||||
.append(presence.statuses.into_iter().map(|(lang, status)| {
|
||||
Element::builder("status")
|
||||
.attr("xml:lang", match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
})
|
||||
.append(status)
|
||||
.build()
|
||||
}).collect::<Vec<_>>())
|
||||
.append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) })
|
||||
.append(presence.payloads)
|
||||
.build()
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("from", presence.from)
|
||||
.attr("to", presence.to)
|
||||
.attr("id", presence.id)
|
||||
.attr("type", presence.type_)
|
||||
.append(presence.show)
|
||||
.append(
|
||||
presence
|
||||
.statuses
|
||||
.into_iter()
|
||||
.map(|(lang, status)| {
|
||||
Element::builder("status")
|
||||
.attr(
|
||||
"xml:lang",
|
||||
match lang.as_ref() {
|
||||
"" => None,
|
||||
lang => Some(lang),
|
||||
},
|
||||
)
|
||||
.append(status)
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.append(if presence.priority == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(format!("{}", presence.priority))
|
||||
})
|
||||
.append(presence.payloads)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +381,9 @@ mod tests {
|
|||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'/>".parse().unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'/>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
assert_eq!(presence.from, None);
|
||||
assert_eq!(presence.to, None);
|
||||
|
@ -366,9 +395,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_serialise() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client' type='unavailable'/>/>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client' type='unavailable'/>/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept' type='unavailable'/>/>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept' type='unavailable'/>/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::new(Type::Unavailable);
|
||||
let elem2 = presence.into();
|
||||
assert!(elem.compare_to(&elem2));
|
||||
|
@ -377,9 +410,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_show() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show>chat</show></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show>chat</show></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><show>chat</show></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><show>chat</show></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
assert_eq!(presence.payloads.len(), 0);
|
||||
assert_eq!(presence.show, Show::Chat);
|
||||
|
@ -388,9 +426,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_missing_show_value() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><show/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><show/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Presence::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -403,9 +445,14 @@ mod tests {
|
|||
fn test_invalid_show() {
|
||||
// "online" used to be a pretty common mistake.
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show>online</show></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><show>online</show></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><show>online</show></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><show>online</show></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Presence::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -417,9 +464,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_empty_status() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><status/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><status/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
assert_eq!(presence.payloads.len(), 0);
|
||||
assert_eq!(presence.statuses.len(), 1);
|
||||
|
@ -429,9 +480,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_status() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status>Here!</status></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status>Here!</status></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><status>Here!</status></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><status>Here!</status></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
assert_eq!(presence.payloads.len(), 0);
|
||||
assert_eq!(presence.statuses.len(), 1);
|
||||
|
@ -462,15 +518,23 @@ mod tests {
|
|||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Status element present twice for the same xml:lang.");
|
||||
assert_eq!(
|
||||
message,
|
||||
"Status element present twice for the same xml:lang."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_priority() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><priority>-1</priority></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><priority>-1</priority></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><priority>-1</priority></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><priority>-1</priority></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
assert_eq!(presence.payloads.len(), 0);
|
||||
assert_eq!(presence.priority, -1i8);
|
||||
|
@ -479,9 +543,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_invalid_priority() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><priority>128</priority></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><priority>128</priority></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><priority>128</priority></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><priority>128</priority></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Presence::try_from(elem).unwrap_err();
|
||||
match error {
|
||||
Error::ParseIntError(_) => (),
|
||||
|
@ -492,9 +561,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_unknown_child() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><test xmlns='invalid'/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><test xmlns='invalid'/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><test xmlns='invalid'/></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><test xmlns='invalid'/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let presence = Presence::try_from(elem).unwrap();
|
||||
let payload = &presence.payloads[0];
|
||||
assert!(payload.is("test", "invalid"));
|
||||
|
@ -503,9 +577,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_invalid_status_child() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status><coucou/></status></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status><coucou/></status></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><status><coucou/></status></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><status><coucou/></status></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Presence::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -517,9 +596,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status coucou=''/></presence>".parse().unwrap();
|
||||
let elem: Element = "<presence xmlns='jabber:client'><status coucou=''/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<presence xmlns='jabber:component:accept'><status coucou=''/></presence>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<presence xmlns='jabber:component:accept'><status coucou=''/></presence>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Presence::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -4,19 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
use crate::date::DateTime;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
|
||||
use crate::data_forms::DataForm;
|
||||
|
||||
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
|
||||
use crate::date::DateTime;
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
/// One PubSub item from a node.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -40,7 +35,9 @@ impl TryFrom<Element> for Item {
|
|||
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
||||
let payload = payloads.pop();
|
||||
if !payloads.is_empty() {
|
||||
return Err(Error::ParseError("More than a single payload in item element."));
|
||||
return Err(Error::ParseError(
|
||||
"More than a single payload in item element.",
|
||||
));
|
||||
}
|
||||
Ok(Item {
|
||||
payload,
|
||||
|
@ -53,11 +50,11 @@ impl TryFrom<Element> for Item {
|
|||
impl From<Item> for Element {
|
||||
fn from(item: Item) -> Element {
|
||||
Element::builder("item")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("id", item.id)
|
||||
.attr("publisher", item.publisher)
|
||||
.append(item.payload)
|
||||
.build()
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("id", item.id)
|
||||
.attr("publisher", item.publisher)
|
||||
.append(item.payload)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,21 +128,29 @@ pub enum PubSubEvent {
|
|||
|
||||
fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
|
||||
let mut is_retract = None;
|
||||
let mut items = vec!();
|
||||
let mut retracts = vec!();
|
||||
let mut items = vec![];
|
||||
let mut retracts = vec![];
|
||||
for child in elem.children() {
|
||||
if child.is("item", ns::PUBSUB_EVENT) {
|
||||
match is_retract {
|
||||
None => is_retract = Some(false),
|
||||
Some(false) => (),
|
||||
Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")),
|
||||
Some(true) => {
|
||||
return Err(Error::ParseError(
|
||||
"Mix of item and retract in items element.",
|
||||
))
|
||||
}
|
||||
}
|
||||
items.push(Item::try_from(child.clone())?);
|
||||
} else if child.is("retract", ns::PUBSUB_EVENT) {
|
||||
match is_retract {
|
||||
None => is_retract = Some(true),
|
||||
Some(true) => (),
|
||||
Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")),
|
||||
Some(false) => {
|
||||
return Err(Error::ParseError(
|
||||
"Mix of item and retract in items element.",
|
||||
))
|
||||
}
|
||||
}
|
||||
check_no_children!(child, "retract");
|
||||
check_no_unknown_attributes!(child, "retract", ["id"]);
|
||||
|
@ -157,7 +162,10 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
|
|||
}
|
||||
Ok(match is_retract {
|
||||
Some(false) => PubSubEvent::PublishedItems { node, items },
|
||||
Some(true) => PubSubEvent::RetractedItems { node, items: retracts },
|
||||
Some(true) => PubSubEvent::RetractedItems {
|
||||
node,
|
||||
items: retracts,
|
||||
},
|
||||
None => return Err(Error::ParseError("Missing children in items element.")),
|
||||
})
|
||||
}
|
||||
|
@ -176,7 +184,9 @@ impl TryFrom<Element> for PubSubEvent {
|
|||
let mut payloads = child.children().cloned().collect::<Vec<_>>();
|
||||
let item = payloads.pop();
|
||||
if !payloads.is_empty() {
|
||||
return Err(Error::ParseError("More than a single payload in configuration element."));
|
||||
return Err(Error::ParseError(
|
||||
"More than a single payload in configuration element.",
|
||||
));
|
||||
}
|
||||
let form = match item {
|
||||
None => None,
|
||||
|
@ -188,7 +198,9 @@ impl TryFrom<Element> for PubSubEvent {
|
|||
for item in child.children() {
|
||||
if item.is("redirect", ns::PUBSUB_EVENT) {
|
||||
if redirect.is_some() {
|
||||
return Err(Error::ParseError("More than one redirect in delete element."));
|
||||
return Err(Error::ParseError(
|
||||
"More than one redirect in delete element.",
|
||||
));
|
||||
}
|
||||
let uri = get_attr!(item, "uri", required);
|
||||
redirect = Some(uri);
|
||||
|
@ -222,77 +234,79 @@ impl TryFrom<Element> for PubSubEvent {
|
|||
impl From<PubSubEvent> for Element {
|
||||
fn from(event: PubSubEvent) -> Element {
|
||||
let payload = match event {
|
||||
PubSubEvent::Configuration { node, form } => {
|
||||
Element::builder("configuration")
|
||||
PubSubEvent::Configuration { node, form } => Element::builder("configuration")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(form)
|
||||
.build(),
|
||||
PubSubEvent::Delete { node, redirect } => Element::builder("purge")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(redirect.map(|redirect| {
|
||||
Element::builder("redirect")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(form)
|
||||
.attr("uri", redirect)
|
||||
.build()
|
||||
},
|
||||
PubSubEvent::Delete { node, redirect } => {
|
||||
Element::builder("purge")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(redirect.map(|redirect| {
|
||||
Element::builder("redirect")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("uri", redirect)
|
||||
.build()
|
||||
}))
|
||||
.build()
|
||||
},
|
||||
PubSubEvent::PublishedItems { node, items } => {
|
||||
Element::builder("items")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(items)
|
||||
.build()
|
||||
},
|
||||
PubSubEvent::RetractedItems { node, items } => {
|
||||
Element::builder("items")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(items.into_iter().map(|id| {
|
||||
Element::builder("retract")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("id", id)
|
||||
.build()
|
||||
}).collect::<Vec<_>>())
|
||||
.build()
|
||||
},
|
||||
PubSubEvent::Purge { node } => {
|
||||
Element::builder("purge")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.build()
|
||||
},
|
||||
PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => {
|
||||
Element::builder("subscription")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.attr("expiry", expiry)
|
||||
.attr("jid", jid)
|
||||
.attr("subid", subid)
|
||||
.attr("subscription", subscription)
|
||||
.build()
|
||||
},
|
||||
}))
|
||||
.build(),
|
||||
PubSubEvent::PublishedItems { node, items } => Element::builder("items")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(items)
|
||||
.build(),
|
||||
PubSubEvent::RetractedItems { node, items } => Element::builder("items")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.append(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
Element::builder("retract")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("id", id)
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.build(),
|
||||
PubSubEvent::Purge { node } => Element::builder("purge")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.build(),
|
||||
PubSubEvent::Subscription {
|
||||
node,
|
||||
expiry,
|
||||
jid,
|
||||
subid,
|
||||
subscription,
|
||||
} => Element::builder("subscription")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.attr("node", node)
|
||||
.attr("expiry", expiry)
|
||||
.attr("jid", jid)
|
||||
.attr("subid", subid)
|
||||
.attr("subscription", subscription)
|
||||
.build(),
|
||||
};
|
||||
Element::builder("event")
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.append(payload)
|
||||
.build()
|
||||
.ns(ns::PUBSUB_EVENT)
|
||||
.append(payload)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn missing_items() {
|
||||
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'/></event>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'/></event>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = PubSubEvent::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -309,9 +323,12 @@ mod tests {
|
|||
PubSubEvent::PublishedItems { node, items } => {
|
||||
assert_eq!(node, NodeName(String::from("coucou")));
|
||||
assert_eq!(items[0].id, Some(ItemId(String::from("test"))));
|
||||
assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap()));
|
||||
assert_eq!(
|
||||
items[0].publisher,
|
||||
Some(Jid::from_str("test@coucou").unwrap())
|
||||
);
|
||||
assert_eq!(items[0].payload, None);
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +346,7 @@ mod tests {
|
|||
Some(ref elem) => assert!(elem.is("foreign", "example:namespace")),
|
||||
_ => panic!(),
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +360,7 @@ mod tests {
|
|||
assert_eq!(node, NodeName(String::from("something")));
|
||||
assert_eq!(items[0], ItemId(String::from("coucou")));
|
||||
assert_eq!(items[1], ItemId(String::from("test")));
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -356,19 +373,22 @@ mod tests {
|
|||
PubSubEvent::Delete { node, redirect } => {
|
||||
assert_eq!(node, NodeName(String::from("coucou")));
|
||||
assert_eq!(redirect, Some(String::from("hello")));
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_purge() {
|
||||
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><purge node='coucou'/></event>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<event xmlns='http://jabber.org/protocol/pubsub#event'><purge node='coucou'/></event>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let event = PubSubEvent::try_from(elem).unwrap();
|
||||
match event {
|
||||
PubSubEvent::Purge { node } => {
|
||||
assert_eq!(node, NodeName(String::from("coucou")));
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -381,14 +401,17 @@ mod tests {
|
|||
PubSubEvent::Configuration { node, form: _ } => {
|
||||
assert_eq!(node, NodeName(String::from("coucou")));
|
||||
//assert_eq!(form.type_, Result_);
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><coucou node='test'/></event>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<event xmlns='http://jabber.org/protocol/pubsub#event'><coucou node='test'/></event>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = PubSubEvent::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -399,7 +422,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_attribute() {
|
||||
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = PubSubEvent::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -419,16 +444,29 @@ mod tests {
|
|||
subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3'
|
||||
subscription='subscribed'/>
|
||||
</event>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let event = PubSubEvent::try_from(elem.clone()).unwrap();
|
||||
match event.clone() {
|
||||
PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => {
|
||||
PubSubEvent::Subscription {
|
||||
node,
|
||||
expiry,
|
||||
jid,
|
||||
subid,
|
||||
subscription,
|
||||
} => {
|
||||
assert_eq!(node, NodeName(String::from("princely_musings")));
|
||||
assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))));
|
||||
assert_eq!(
|
||||
subid,
|
||||
Some(SubscriptionId(String::from(
|
||||
"ba49252aaa4f5d320c24d3766f0bdcade78c78d3"
|
||||
)))
|
||||
);
|
||||
assert_eq!(subscription, Some(Subscription::Subscribed));
|
||||
assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap()));
|
||||
assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap()));
|
||||
},
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
|
|
|
@ -4,19 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
use jid::Jid;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use crate::ns;
|
||||
|
||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
||||
use crate::data_forms::DataForm;
|
||||
|
||||
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
|
||||
use crate::error::Error;
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use crate::ns;
|
||||
use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
// TODO: a better solution would be to split this into a query and a result elements, like for
|
||||
// XEP-0030.
|
||||
|
@ -137,7 +132,9 @@ impl TryFrom<Element> for Item {
|
|||
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
||||
let payload = payloads.pop();
|
||||
if !payloads.is_empty() {
|
||||
return Err(Error::ParseError("More than a single payload in item element."));
|
||||
return Err(Error::ParseError(
|
||||
"More than a single payload in item element.",
|
||||
));
|
||||
}
|
||||
Ok(Item {
|
||||
payload,
|
||||
|
@ -149,10 +146,10 @@ impl TryFrom<Element> for Item {
|
|||
impl From<Item> for Element {
|
||||
fn from(item: Item) -> Element {
|
||||
Element::builder("item")
|
||||
.ns(ns::PUBSUB)
|
||||
.attr("id", item.id)
|
||||
.append(item.payload)
|
||||
.build()
|
||||
.ns(ns::PUBSUB)
|
||||
.attr("id", item.id)
|
||||
.append(item.payload)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +196,9 @@ generate_element!(
|
|||
|
||||
generate_attribute!(
|
||||
/// Whether a retract request should notify subscribers or not.
|
||||
Notify, "notify", bool
|
||||
Notify,
|
||||
"notify",
|
||||
bool
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -235,11 +234,15 @@ impl TryFrom<Element> for SubscribeOptions {
|
|||
for child in elem.children() {
|
||||
if child.is("required", ns::PUBSUB) {
|
||||
if required {
|
||||
return Err(Error::ParseError("More than one required element in subscribe-options."));
|
||||
return Err(Error::ParseError(
|
||||
"More than one required element in subscribe-options.",
|
||||
));
|
||||
}
|
||||
required = true;
|
||||
} else {
|
||||
return Err(Error::ParseError("Unknown child in subscribe-options element."));
|
||||
return Err(Error::ParseError(
|
||||
"Unknown child in subscribe-options element.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(SubscribeOptions { required })
|
||||
|
@ -251,12 +254,10 @@ impl From<SubscribeOptions> for Element {
|
|||
Element::builder("subscribe-options")
|
||||
.ns(ns::PUBSUB)
|
||||
.append(if subscribe_options.required {
|
||||
vec!(Element::builder("required")
|
||||
.ns(ns::PUBSUB)
|
||||
.build())
|
||||
} else {
|
||||
vec!()
|
||||
})
|
||||
vec![Element::builder("required").ns(ns::PUBSUB).build()]
|
||||
} else {
|
||||
vec![]
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +335,7 @@ pub enum PubSub {
|
|||
create: Create,
|
||||
|
||||
/// The configure request for the new node.
|
||||
configure: Option<Configure>
|
||||
configure: Option<Configure>,
|
||||
},
|
||||
|
||||
/// Request to publish items to a node, with optional options.
|
||||
|
@ -343,7 +344,7 @@ pub enum PubSub {
|
|||
publish: Publish,
|
||||
|
||||
/// The options related to this publish request.
|
||||
publish_options: Option<PublishOptions>
|
||||
publish_options: Option<PublishOptions>,
|
||||
},
|
||||
|
||||
/// A list of affiliations you have on a service, or on a node.
|
||||
|
@ -383,75 +384,114 @@ impl TryFrom<Element> for PubSub {
|
|||
for child in elem.children() {
|
||||
if child.is("create", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let create = Create::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Create { create, configure: None });
|
||||
payload = Some(PubSub::Create {
|
||||
create,
|
||||
configure: None,
|
||||
});
|
||||
} else if child.is("configure", ns::PUBSUB) {
|
||||
if let Some(PubSub::Create { create, configure }) = payload {
|
||||
if configure.is_some() {
|
||||
return Err(Error::ParseError("Configure is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Configure is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let configure = Some(Configure::try_from(child.clone())?);
|
||||
payload = Some(PubSub::Create { create, configure });
|
||||
} else {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
} else if child.is("publish", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let publish = Publish::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Publish { publish, publish_options: None });
|
||||
payload = Some(PubSub::Publish {
|
||||
publish,
|
||||
publish_options: None,
|
||||
});
|
||||
} else if child.is("publish-options", ns::PUBSUB) {
|
||||
if let Some(PubSub::Publish { publish, publish_options }) = payload {
|
||||
if let Some(PubSub::Publish {
|
||||
publish,
|
||||
publish_options,
|
||||
}) = payload
|
||||
{
|
||||
if publish_options.is_some() {
|
||||
return Err(Error::ParseError("Publish-options are already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Publish-options are already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let publish_options = Some(PublishOptions::try_from(child.clone())?);
|
||||
payload = Some(PubSub::Publish { publish, publish_options });
|
||||
payload = Some(PubSub::Publish {
|
||||
publish,
|
||||
publish_options,
|
||||
});
|
||||
} else {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
} else if child.is("affiliations", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let affiliations = Affiliations::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Affiliations(affiliations));
|
||||
} else if child.is("default", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let default = Default::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Default(default));
|
||||
} else if child.is("items", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let items = Items::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Items(items));
|
||||
} else if child.is("retract", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let retract = Retract::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Retract(retract));
|
||||
} else if child.is("subscription", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let subscription = SubscriptionElem::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Subscription(subscription));
|
||||
} else if child.is("subscriptions", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let subscriptions = Subscriptions::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Subscriptions(subscriptions));
|
||||
} else if child.is("unsubscribe", ns::PUBSUB) {
|
||||
if payload.is_some() {
|
||||
return Err(Error::ParseError("Payload is already defined in pubsub element."));
|
||||
return Err(Error::ParseError(
|
||||
"Payload is already defined in pubsub element.",
|
||||
));
|
||||
}
|
||||
let unsubscribe = Unsubscribe::try_from(child.clone())?;
|
||||
payload = Some(PubSub::Unsubscribe(unsubscribe));
|
||||
|
@ -468,28 +508,31 @@ impl From<PubSub> for Element {
|
|||
Element::builder("pubsub")
|
||||
.ns(ns::PUBSUB)
|
||||
.append(match pubsub {
|
||||
PubSub::Create { create, configure } => {
|
||||
let mut elems = vec!(Element::from(create));
|
||||
if let Some(configure) = configure {
|
||||
elems.push(Element::from(configure));
|
||||
}
|
||||
elems
|
||||
},
|
||||
PubSub::Publish { publish, publish_options } => {
|
||||
let mut elems = vec!(Element::from(publish));
|
||||
if let Some(publish_options) = publish_options {
|
||||
elems.push(Element::from(publish_options));
|
||||
}
|
||||
elems
|
||||
},
|
||||
PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)),
|
||||
PubSub::Default(default) => vec!(Element::from(default)),
|
||||
PubSub::Items(items) => vec!(Element::from(items)),
|
||||
PubSub::Retract(retract) => vec!(Element::from(retract)),
|
||||
PubSub::Subscription(subscription) => vec!(Element::from(subscription)),
|
||||
PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)),
|
||||
PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)),
|
||||
})
|
||||
PubSub::Create { create, configure } => {
|
||||
let mut elems = vec![Element::from(create)];
|
||||
if let Some(configure) = configure {
|
||||
elems.push(Element::from(configure));
|
||||
}
|
||||
elems
|
||||
}
|
||||
PubSub::Publish {
|
||||
publish,
|
||||
publish_options,
|
||||
} => {
|
||||
let mut elems = vec![Element::from(publish)];
|
||||
if let Some(publish_options) = publish_options {
|
||||
elems.push(Element::from(publish_options));
|
||||
}
|
||||
elems
|
||||
}
|
||||
PubSub::Affiliations(affiliations) => vec![Element::from(affiliations)],
|
||||
PubSub::Default(default) => vec![Element::from(default)],
|
||||
PubSub::Items(items) => vec![Element::from(items)],
|
||||
PubSub::Retract(retract) => vec![Element::from(retract)],
|
||||
PubSub::Subscription(subscription) => vec![Element::from(subscription)],
|
||||
PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)],
|
||||
PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)],
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +544,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn create() {
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/></pubsub>".parse().unwrap();
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/></pubsub>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let pubsub = PubSub::try_from(elem).unwrap();
|
||||
match pubsub.clone() {
|
||||
|
@ -515,7 +560,10 @@ mod tests {
|
|||
let elem2 = Element::from(pubsub);
|
||||
assert!(elem1.compare_to(&elem2));
|
||||
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='coucou'/></pubsub>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='coucou'/></pubsub>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let pubsub = PubSub::try_from(elem).unwrap();
|
||||
match pubsub.clone() {
|
||||
|
@ -532,7 +580,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn create_and_configure() {
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/><configure/></pubsub>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/><configure/></pubsub>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let pubsub = PubSub::try_from(elem).unwrap();
|
||||
match pubsub.clone() {
|
||||
|
@ -549,11 +600,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn publish() {
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><publish node='coucou'/></pubsub>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><publish node='coucou'/></pubsub>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let pubsub = PubSub::try_from(elem).unwrap();
|
||||
match pubsub.clone() {
|
||||
PubSub::Publish { publish, publish_options } => {
|
||||
PubSub::Publish {
|
||||
publish,
|
||||
publish_options,
|
||||
} => {
|
||||
assert_eq!(&publish.node.0, "coucou");
|
||||
assert!(publish_options.is_none());
|
||||
}
|
||||
|
@ -570,7 +627,10 @@ mod tests {
|
|||
let elem1 = elem.clone();
|
||||
let pubsub = PubSub::try_from(elem).unwrap();
|
||||
match pubsub.clone() {
|
||||
PubSub::Publish { publish, publish_options } => {
|
||||
PubSub::Publish {
|
||||
publish,
|
||||
publish_options,
|
||||
} => {
|
||||
assert_eq!(&publish.node.0, "coucou");
|
||||
assert!(publish_options.unwrap().form.is_none());
|
||||
}
|
||||
|
@ -583,7 +643,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn invalid_empty_pubsub() {
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'/>".parse().unwrap();
|
||||
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = PubSub::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -596,12 +658,17 @@ mod tests {
|
|||
fn publish_option() {
|
||||
let elem: Element = "<publish-options xmlns='http://jabber.org/protocol/pubsub'><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#publish-options</value></field></x></publish-options>".parse().unwrap();
|
||||
let publish_options = PublishOptions::try_from(elem).unwrap();
|
||||
assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options");
|
||||
assert_eq!(
|
||||
&publish_options.form.unwrap().form_type.unwrap(),
|
||||
"http://jabber.org/protocol/pubsub#publish-options"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscribe_options() {
|
||||
let elem1: Element = "<subscribe-options xmlns='http://jabber.org/protocol/pubsub'/>".parse().unwrap();
|
||||
let elem1: Element = "<subscribe-options xmlns='http://jabber.org/protocol/pubsub'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap();
|
||||
assert_eq!(subscribe_options1.required, false);
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ use crate::message::MessagePayload;
|
|||
generate_empty_element!(
|
||||
/// Requests that this message is acked by the final recipient once
|
||||
/// received.
|
||||
Request, "request", RECEIPTS
|
||||
Request,
|
||||
"request",
|
||||
RECEIPTS
|
||||
);
|
||||
|
||||
impl MessagePayload for Request {}
|
||||
|
@ -29,9 +31,9 @@ impl MessagePayload for Received {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -55,7 +57,9 @@ mod tests {
|
|||
let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap();
|
||||
Received::try_from(elem).unwrap();
|
||||
|
||||
let elem: Element = "<received xmlns='urn:xmpp:receipts' id='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<received xmlns='urn:xmpp:receipts' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
Received::try_from(elem).unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||
use jid::Jid;
|
||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
||||
|
||||
generate_elem_id!(
|
||||
/// Represents a group a contact is part of.
|
||||
Group, "group", ROSTER
|
||||
Group,
|
||||
"group",
|
||||
ROSTER
|
||||
);
|
||||
|
||||
generate_attribute!(
|
||||
|
@ -79,11 +81,11 @@ impl IqResultPayload for Roster {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use std::str::FromStr;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -122,7 +124,9 @@ mod tests {
|
|||
let roster2 = Roster::try_from(elem2).unwrap();
|
||||
assert_eq!(roster.items, roster2.items);
|
||||
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert_eq!(roster.ver, Some(String::from("ver9")));
|
||||
assert!(roster.items.is_empty());
|
||||
|
@ -141,14 +145,22 @@ mod tests {
|
|||
name='Benvolio'
|
||||
subscription='both'/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert_eq!(roster.ver, Some(String::from("ver11")));
|
||||
assert_eq!(roster.items.len(), 3);
|
||||
assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap());
|
||||
assert_eq!(
|
||||
roster.items[0].jid,
|
||||
Jid::from_str("romeo@example.net").unwrap()
|
||||
);
|
||||
assert_eq!(roster.items[0].name, Some(String::from("Romeo")));
|
||||
assert_eq!(roster.items[0].subscription, Subscription::Both);
|
||||
assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()));
|
||||
assert_eq!(
|
||||
roster.items[0].groups,
|
||||
vec!(Group::from_str("Friends").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -160,12 +172,17 @@ mod tests {
|
|||
<group>B</group>
|
||||
</item>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert!(roster.ver.is_none());
|
||||
assert_eq!(roster.items.len(), 1);
|
||||
assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap());
|
||||
assert_eq!(
|
||||
roster.items[0].jid,
|
||||
Jid::from_str("test@example.org").unwrap()
|
||||
);
|
||||
assert_eq!(roster.items[0].name, None);
|
||||
assert_eq!(roster.items[0].groups.len(), 2);
|
||||
assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
|
||||
|
@ -176,7 +193,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert!(roster.ver.is_none());
|
||||
assert_eq!(roster.items.len(), 1);
|
||||
|
@ -188,25 +208,38 @@ mod tests {
|
|||
<group>Servants</group>
|
||||
</item>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert!(roster.ver.is_none());
|
||||
assert_eq!(roster.items.len(), 1);
|
||||
assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
|
||||
assert_eq!(
|
||||
roster.items[0].jid,
|
||||
Jid::from_str("nurse@example.com").unwrap()
|
||||
);
|
||||
assert_eq!(roster.items[0].name, Some(String::from("Nurse")));
|
||||
assert_eq!(roster.items[0].groups.len(), 1);
|
||||
assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap());
|
||||
assert_eq!(
|
||||
roster.items[0].groups[0],
|
||||
Group::from_str("Servants").unwrap()
|
||||
);
|
||||
|
||||
let elem: Element = r#"
|
||||
<query xmlns='jabber:iq:roster'>
|
||||
<item jid='nurse@example.com'
|
||||
subscription='remove'/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
"#
|
||||
.parse()
|
||||
.unwrap();
|
||||
let roster = Roster::try_from(elem).unwrap();
|
||||
assert!(roster.ver.is_none());
|
||||
assert_eq!(roster.items.len(), 1);
|
||||
assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
|
||||
assert_eq!(
|
||||
roster.items[0].jid,
|
||||
Jid::from_str("nurse@example.com").unwrap()
|
||||
);
|
||||
assert!(roster.items[0].name.is_none());
|
||||
assert!(roster.items[0].groups.is_empty());
|
||||
assert_eq!(roster.items[0].subscription, Subscription::Remove);
|
||||
|
@ -214,7 +247,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Roster::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -222,7 +257,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Unknown child in query element.");
|
||||
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Roster::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -233,7 +270,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_item() {
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>".parse().unwrap();
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Roster::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -251,7 +290,10 @@ mod tests {
|
|||
assert_eq!(error.description(), "Invalid JID, I guess?");
|
||||
*/
|
||||
|
||||
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = Roster::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
94
src/rsm.rs
94
src/rsm.rs
|
@ -74,12 +74,30 @@ impl TryFrom<Element> for SetQuery {
|
|||
impl From<SetQuery> for Element {
|
||||
fn from(set: SetQuery) -> Element {
|
||||
Element::builder("set")
|
||||
.ns(ns::RSM)
|
||||
.append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()))
|
||||
.append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build()))
|
||||
.append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build()))
|
||||
.append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()))
|
||||
.build()
|
||||
.ns(ns::RSM)
|
||||
.append(set.max.map(|max| {
|
||||
Element::builder("max")
|
||||
.ns(ns::RSM)
|
||||
.append(format!("{}", max))
|
||||
.build()
|
||||
}))
|
||||
.append(
|
||||
set.after
|
||||
.map(|after| Element::builder("after").ns(ns::RSM).append(after).build()),
|
||||
)
|
||||
.append(set.before.map(|before| {
|
||||
Element::builder("before")
|
||||
.ns(ns::RSM)
|
||||
.append(before)
|
||||
.build()
|
||||
}))
|
||||
.append(set.index.map(|index| {
|
||||
Element::builder("index")
|
||||
.ns(ns::RSM)
|
||||
.append(format!("{}", index))
|
||||
.build()
|
||||
}))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,18 +156,27 @@ impl TryFrom<Element> for SetResult {
|
|||
|
||||
impl From<SetResult> for Element {
|
||||
fn from(set: SetResult) -> Element {
|
||||
let first = set.first.clone()
|
||||
.map(|first| Element::builder("first")
|
||||
.ns(ns::RSM)
|
||||
.attr("index", set.first_index)
|
||||
.append(first)
|
||||
.build());
|
||||
Element::builder("set")
|
||||
let first = set.first.clone().map(|first| {
|
||||
Element::builder("first")
|
||||
.ns(ns::RSM)
|
||||
.attr("index", set.first_index)
|
||||
.append(first)
|
||||
.append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build()))
|
||||
.append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()))
|
||||
.build()
|
||||
});
|
||||
Element::builder("set")
|
||||
.ns(ns::RSM)
|
||||
.append(first)
|
||||
.append(
|
||||
set.last
|
||||
.map(|last| Element::builder("last").ns(ns::RSM).append(last).build()),
|
||||
)
|
||||
.append(set.count.map(|count| {
|
||||
Element::builder("count")
|
||||
.ns(ns::RSM)
|
||||
.append(format!("{}", count))
|
||||
.build()
|
||||
}))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,14 +201,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let set = SetQuery::try_from(elem).unwrap();
|
||||
assert_eq!(set.max, None);
|
||||
assert_eq!(set.after, None);
|
||||
assert_eq!(set.before, None);
|
||||
assert_eq!(set.index, None);
|
||||
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let set = SetResult::try_from(elem).unwrap();
|
||||
match set.first {
|
||||
Some(_) => panic!(),
|
||||
|
@ -193,7 +224,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_unknown() {
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = SetQuery::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -201,7 +234,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "This is not a RSM set element.");
|
||||
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
|
||||
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = SetResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -212,7 +247,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = SetQuery::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -220,7 +257,9 @@ mod tests {
|
|||
};
|
||||
assert_eq!(message, "Unknown child in set element.");
|
||||
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = SetResult::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -231,7 +270,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let rsm = SetQuery {
|
||||
max: None,
|
||||
after: None,
|
||||
|
@ -241,7 +282,9 @@ mod tests {
|
|||
let elem2 = rsm.into();
|
||||
assert_eq!(elem, elem2);
|
||||
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let rsm = SetResult {
|
||||
first: None,
|
||||
first_index: None,
|
||||
|
@ -254,7 +297,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_first_index() {
|
||||
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let elem1 = elem.clone();
|
||||
let set = SetResult::try_from(elem).unwrap();
|
||||
assert_eq!(set.first, Some(String::from("coucou")));
|
||||
|
|
65
src/sasl.rs
65
src/sasl.rs
|
@ -6,10 +6,10 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use crate::ns;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
use crate::helpers::Base64;
|
||||
|
||||
|
@ -84,7 +84,9 @@ generate_element!(
|
|||
generate_empty_element!(
|
||||
/// Sent by the client at any point after [auth](struct.Auth.html) if it
|
||||
/// wants to cancel the current authentication process.
|
||||
Abort, "abort", SASL
|
||||
Abort,
|
||||
"abort",
|
||||
SASL
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -166,11 +168,15 @@ impl TryFrom<Element> for Failure {
|
|||
check_no_children!(child, "text");
|
||||
let lang = get_attr!(child, "xml:lang", default);
|
||||
if texts.insert(lang, child.text()).is_some() {
|
||||
return Err(Error::ParseError("Text element present twice for the same xml:lang in failure element."));
|
||||
return Err(Error::ParseError(
|
||||
"Text element present twice for the same xml:lang in failure element.",
|
||||
));
|
||||
}
|
||||
} else if child.has_ns(ns::SASL) {
|
||||
if defined_condition.is_some() {
|
||||
return Err(Error::ParseError("Failure must not have more than one defined-condition."));
|
||||
return Err(Error::ParseError(
|
||||
"Failure must not have more than one defined-condition.",
|
||||
));
|
||||
}
|
||||
check_no_attributes!(child, "defined-condition");
|
||||
check_no_children!(child, "defined-condition");
|
||||
|
@ -184,7 +190,8 @@ impl TryFrom<Element> for Failure {
|
|||
return Err(Error::ParseError("Unknown element in Failure."));
|
||||
}
|
||||
}
|
||||
let defined_condition = defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?;
|
||||
let defined_condition =
|
||||
defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?;
|
||||
|
||||
Ok(Failure {
|
||||
defined_condition: defined_condition,
|
||||
|
@ -196,24 +203,30 @@ impl TryFrom<Element> for Failure {
|
|||
impl From<Failure> for Element {
|
||||
fn from(failure: Failure) -> Element {
|
||||
Element::builder("failure")
|
||||
.ns(ns::SASL)
|
||||
.append(failure.defined_condition)
|
||||
.append(failure.texts.into_iter().map(|(lang, text)| {
|
||||
Element::builder("text")
|
||||
.ns(ns::SASL)
|
||||
.attr("xml:lang", lang)
|
||||
.append(text)
|
||||
.build()
|
||||
}).collect::<Vec<_>>())
|
||||
.build()
|
||||
.ns(ns::SASL)
|
||||
.append(failure.defined_condition)
|
||||
.append(
|
||||
failure
|
||||
.texts
|
||||
.into_iter()
|
||||
.map(|(lang, text)| {
|
||||
Element::builder("text")
|
||||
.ns(ns::SASL)
|
||||
.attr("xml:lang", lang)
|
||||
.append(text)
|
||||
.build()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -243,7 +256,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'/>".parse().unwrap();
|
||||
let elem: Element = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let auth = Auth::try_from(elem).unwrap();
|
||||
assert_eq!(auth.mechanism, Mechanism::Plain);
|
||||
assert!(auth.data.is_empty());
|
||||
|
@ -251,7 +266,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn section_6_5_1() {
|
||||
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><aborted/></failure>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><aborted/></failure>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let failure = Failure::try_from(elem).unwrap();
|
||||
assert_eq!(failure.defined_condition, DefinedCondition::Aborted);
|
||||
assert!(failure.texts.is_empty());
|
||||
|
@ -262,9 +280,14 @@ mod tests {
|
|||
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
|
||||
<account-disabled/>
|
||||
<text xml:lang='en'>Call 212-555-1212 for assistance.</text>
|
||||
</failure>".parse().unwrap();
|
||||
</failure>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let failure = Failure::try_from(elem).unwrap();
|
||||
assert_eq!(failure.defined_condition, DefinedCondition::AccountDisabled);
|
||||
assert_eq!(failure.texts["en"], String::from("Call 212-555-1212 for assistance."));
|
||||
assert_eq!(
|
||||
failure.texts["en"],
|
||||
String::from("Call 212-555-1212 for assistance.")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
30
src/sm.rs
30
src/sm.rs
|
@ -24,7 +24,9 @@ impl A {
|
|||
|
||||
generate_attribute!(
|
||||
/// Whether to allow resumption of a previous stream.
|
||||
ResumeAttr, "resume", bool
|
||||
ResumeAttr,
|
||||
"resume",
|
||||
bool
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -103,7 +105,9 @@ generate_element!(
|
|||
|
||||
generate_empty_element!(
|
||||
/// Requests the currently received stanzas by the other party.
|
||||
R, "r", SM
|
||||
R,
|
||||
"r",
|
||||
SM
|
||||
);
|
||||
|
||||
generate_element!(
|
||||
|
@ -135,14 +139,16 @@ generate_element!(
|
|||
// TODO: add support for optional and required.
|
||||
generate_empty_element!(
|
||||
/// Represents availability of Stream Management in `<stream:features/>`.
|
||||
StreamManagement, "sm", SM
|
||||
StreamManagement,
|
||||
"sm",
|
||||
SM
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -189,12 +195,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn resume() {
|
||||
let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>".parse().unwrap();
|
||||
let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let enable = Enable::try_from(elem).unwrap();
|
||||
assert_eq!(enable.max, None);
|
||||
assert_eq!(enable.resume, ResumeAttr::True);
|
||||
|
||||
let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>".parse().unwrap();
|
||||
let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let enabled = Enabled::try_from(elem).unwrap();
|
||||
let previd = enabled.id.unwrap();
|
||||
assert_eq!(enabled.resume, ResumeAttr::True);
|
||||
|
@ -202,12 +212,16 @@ mod tests {
|
|||
assert_eq!(enabled.max, Some(600));
|
||||
assert_eq!(enabled.location, None);
|
||||
|
||||
let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let resume = Resume::try_from(elem).unwrap();
|
||||
assert_eq!(resume.h, 5);
|
||||
assert_eq!(resume.previd, previd);
|
||||
|
||||
let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let resumed = Resumed::try_from(elem).unwrap();
|
||||
assert_eq!(resumed.h, 5);
|
||||
assert_eq!(resumed.previd, previd);
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use try_from::TryFrom;
|
||||
use std::collections::BTreeMap;
|
||||
use try_from::TryFrom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use crate::message::MessagePayload;
|
||||
use crate::presence::PresencePayload;
|
||||
use crate::error::Error;
|
||||
use jid::Jid;
|
||||
use crate::message::MessagePayload;
|
||||
use crate::ns;
|
||||
use crate::presence::PresencePayload;
|
||||
use jid::Jid;
|
||||
|
||||
generate_attribute!(
|
||||
/// The type of the error.
|
||||
|
@ -234,23 +234,30 @@ impl TryFrom<Element> for StanzaError {
|
|||
check_no_children!(child, "text");
|
||||
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."));
|
||||
return Err(Error::ParseError(
|
||||
"Text element present twice for the same xml:lang.",
|
||||
));
|
||||
}
|
||||
} else if child.has_ns(ns::XMPP_STANZAS) {
|
||||
if defined_condition.is_some() {
|
||||
return Err(Error::ParseError("Error must not have more than one defined-condition."));
|
||||
return Err(Error::ParseError(
|
||||
"Error must not have more than one defined-condition.",
|
||||
));
|
||||
}
|
||||
check_no_children!(child, "defined-condition");
|
||||
let condition = DefinedCondition::try_from(child.clone())?;
|
||||
defined_condition = Some(condition);
|
||||
} else {
|
||||
if other.is_some() {
|
||||
return Err(Error::ParseError("Error must not have more than one other element."));
|
||||
return Err(Error::ParseError(
|
||||
"Error must not have more than one other element.",
|
||||
));
|
||||
}
|
||||
other = Some(child.clone());
|
||||
}
|
||||
}
|
||||
let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?;
|
||||
let defined_condition =
|
||||
defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?;
|
||||
|
||||
Ok(StanzaError {
|
||||
type_: type_,
|
||||
|
@ -265,17 +272,17 @@ impl TryFrom<Element> for StanzaError {
|
|||
impl From<StanzaError> for Element {
|
||||
fn from(err: StanzaError) -> Element {
|
||||
let mut root = Element::builder("error")
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("type", err.type_)
|
||||
.attr("by", err.by)
|
||||
.append(err.defined_condition)
|
||||
.build();
|
||||
.ns(ns::DEFAULT_NS)
|
||||
.attr("type", err.type_)
|
||||
.attr("by", err.by)
|
||||
.append(err.defined_condition)
|
||||
.build();
|
||||
for (lang, text) in err.texts {
|
||||
let elem = Element::builder("text")
|
||||
.ns(ns::XMPP_STANZAS)
|
||||
.attr("xml:lang", lang)
|
||||
.append(text)
|
||||
.build();
|
||||
.ns(ns::XMPP_STANZAS)
|
||||
.attr("xml:lang", lang)
|
||||
.append(text)
|
||||
.build();
|
||||
root.append_child(elem);
|
||||
}
|
||||
if let Some(other) = err.other {
|
||||
|
@ -313,7 +320,10 @@ mod tests {
|
|||
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'><undefined-condition xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>".parse().unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap();
|
||||
assert_eq!(error.type_, ErrorType::Cancel);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition);
|
||||
assert_eq!(
|
||||
error.defined_condition,
|
||||
DefinedCondition::UndefinedCondition
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -330,9 +340,13 @@ mod tests {
|
|||
assert_eq!(message, "Required attribute 'type' missing.");
|
||||
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<error xmlns='jabber:client' type='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<error xmlns='jabber:client' type='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<error xmlns='jabber:component:accept' type='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<error xmlns='jabber:component:accept' type='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -344,9 +358,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_invalid_condition() {
|
||||
#[cfg(not(feature = "component"))]
|
||||
let elem: Element = "<error xmlns='jabber:client' type='cancel'/>".parse().unwrap();
|
||||
let elem: Element = "<error xmlns='jabber:client' type='cancel'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
#[cfg(feature = "component")]
|
||||
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'/>".parse().unwrap();
|
||||
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
|
|
@ -37,10 +37,10 @@ impl MessagePayload for OriginId {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::error::Error;
|
||||
use minidom::Element;
|
||||
use std::str::FromStr;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -58,19 +58,25 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let stanza_id = StanzaId::try_from(elem).unwrap();
|
||||
assert_eq!(stanza_id.id, String::from("coucou"));
|
||||
assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap());
|
||||
|
||||
let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let origin_id = OriginId::try_from(elem).unwrap();
|
||||
assert_eq!(origin_id.id, String::from("coucou"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_child() {
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>".parse().unwrap();
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = StanzaId::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -92,7 +98,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_invalid_by() {
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let error = StanzaId::try_from(elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
|
@ -103,8 +111,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_serialise() {
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
|
||||
let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() };
|
||||
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let stanza_id = StanzaId {
|
||||
id: String::from("coucou"),
|
||||
by: Jid::from_str("coucou@coucou").unwrap(),
|
||||
};
|
||||
let elem2 = stanza_id.into();
|
||||
assert_eq!(elem, elem2);
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ impl Stream {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
|
|
@ -11,7 +11,9 @@ generate_empty_element!(
|
|||
///
|
||||
/// It should only be used in an `<iq type='get'/>`, as it can only
|
||||
/// represent the request, and not a result.
|
||||
VersionQuery, "query", VERSION
|
||||
VersionQuery,
|
||||
"query",
|
||||
VERSION
|
||||
);
|
||||
|
||||
impl IqGetPayload for VersionQuery {}
|
||||
|
@ -39,9 +41,9 @@ impl IqResultPayload for VersionResult {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use crate::compare_elements::NamespaceAwareCompare;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -59,7 +61,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let elem: Element = "<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>".parse().unwrap();
|
||||
let elem: Element =
|
||||
"<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let version = VersionResult::try_from(elem).unwrap();
|
||||
assert_eq!(version.name, String::from("xmpp-rs"));
|
||||
assert_eq!(version.version, String::from("0.3.0"));
|
||||
|
@ -74,7 +79,10 @@ mod tests {
|
|||
os: None,
|
||||
};
|
||||
let elem1 = Element::from(version);
|
||||
let elem2: Element = "<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>".parse().unwrap();
|
||||
let elem2: Element =
|
||||
"<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
println!("{:?}", elem1);
|
||||
assert!(elem1.compare_to(&elem2));
|
||||
}
|
||||
|
|
|
@ -72,8 +72,8 @@ impl Open {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use try_from::TryFrom;
|
||||
use minidom::Element;
|
||||
use try_from::TryFrom;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[test]
|
||||
|
@ -89,7 +89,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>".parse().unwrap();
|
||||
let elem: Element = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let open = Open::try_from(elem).unwrap();
|
||||
assert_eq!(open.from, None);
|
||||
assert_eq!(open.to, None);
|
||||
|
|
Loading…
Reference in New Issue
Block a user