Run cargo fmt.
This commit is contained in:
parent
d517b8e32e
commit
efd7bd5f2f
51 changed files with 2276 additions and 1324 deletions
|
|
@ -8,7 +8,9 @@ use crate::message::MessagePayload;
|
||||||
|
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Requests the attention of the recipient.
|
/// Requests the attention of the recipient.
|
||||||
Attention, "attention", ATTENTION
|
Attention,
|
||||||
|
"attention",
|
||||||
|
ATTENTION
|
||||||
);
|
);
|
||||||
|
|
||||||
impl MessagePayload for Attention {}
|
impl MessagePayload for Attention {}
|
||||||
|
|
@ -16,9 +18,9 @@ impl MessagePayload for Attention {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_size() {
|
fn test_size() {
|
||||||
|
|
@ -33,7 +35,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Attention::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -44,7 +48,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
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 error = Attention::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
31
src/bind.rs
31
src/bind.rs
|
|
@ -4,16 +4,14 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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 std::str::FromStr;
|
||||||
use try_from::TryFrom;
|
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
|
/// 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.
|
/// can obtain a full JID and start exchanging on the XMPP network.
|
||||||
///
|
///
|
||||||
|
|
@ -76,19 +74,12 @@ impl From<Bind> for Element {
|
||||||
Element::builder("bind")
|
Element::builder("bind")
|
||||||
.ns(ns::BIND)
|
.ns(ns::BIND)
|
||||||
.append(match bind {
|
.append(match bind {
|
||||||
Bind::None => vec!(),
|
Bind::None => vec![],
|
||||||
Bind::Resource(resource) => vec!(
|
Bind::Resource(resource) => vec![Element::builder("resource")
|
||||||
Element::builder("resource")
|
|
||||||
.ns(ns::BIND)
|
.ns(ns::BIND)
|
||||||
.append(resource)
|
.append(resource)
|
||||||
.build()
|
.build()],
|
||||||
),
|
Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()],
|
||||||
Bind::Jid(jid) => vec!(
|
|
||||||
Element::builder("jid")
|
|
||||||
.ns(ns::BIND)
|
|
||||||
.append(jid)
|
|
||||||
.build()
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +103,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let bind = Bind::try_from(elem).unwrap();
|
||||||
assert_eq!(bind, Bind::None);
|
assert_eq!(bind, Bind::None);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,19 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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 jid::Jid;
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
use crate::error::Error;
|
|
||||||
|
|
||||||
use crate::ns;
|
|
||||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
|
||||||
|
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// The element requesting the blocklist, the result iq will contain a
|
/// The element requesting the blocklist, the result iq will contain a
|
||||||
/// [BlocklistResult].
|
/// [BlocklistResult].
|
||||||
BlocklistRequest, "blocklist", BLOCKING
|
BlocklistRequest,
|
||||||
|
"blocklist",
|
||||||
|
BLOCKING
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqGetPayload for BlocklistRequest {}
|
impl IqGetPayload for BlocklistRequest {}
|
||||||
|
|
@ -67,7 +66,8 @@ macro_rules! generate_blocking_element {
|
||||||
generate_blocking_element!(
|
generate_blocking_element!(
|
||||||
/// The element containing the current blocklist, as a reply from
|
/// The element containing the current blocklist, as a reply from
|
||||||
/// [BlocklistRequest].
|
/// [BlocklistRequest].
|
||||||
BlocklistResult, "blocklist"
|
BlocklistResult,
|
||||||
|
"blocklist"
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqResultPayload for BlocklistResult {}
|
impl IqResultPayload for BlocklistResult {}
|
||||||
|
|
@ -75,7 +75,8 @@ impl IqResultPayload for BlocklistResult {}
|
||||||
// TODO: Prevent zero elements from being allowed.
|
// TODO: Prevent zero elements from being allowed.
|
||||||
generate_blocking_element!(
|
generate_blocking_element!(
|
||||||
/// A query to block one or more JIDs.
|
/// A query to block one or more JIDs.
|
||||||
Block, "block"
|
Block,
|
||||||
|
"block"
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqSetPayload for Block {}
|
impl IqSetPayload for Block {}
|
||||||
|
|
@ -84,14 +85,17 @@ generate_blocking_element!(
|
||||||
/// A query to unblock one or more JIDs, or all of them.
|
/// A query to unblock one or more JIDs, or all of them.
|
||||||
///
|
///
|
||||||
/// Warning: not putting any JID there means clearing out the blocklist.
|
/// Warning: not putting any JID there means clearing out the blocklist.
|
||||||
Unblock, "unblock"
|
Unblock,
|
||||||
|
"unblock"
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqSetPayload for Unblock {}
|
impl IqSetPayload for Unblock {}
|
||||||
|
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// The application-specific error condition when a message is blocked.
|
/// The application-specific error condition when a message is blocked.
|
||||||
Blocked, "blocked", BLOCKING_ERRORS
|
Blocked,
|
||||||
|
"blocked",
|
||||||
|
BLOCKING_ERRORS
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -138,7 +142,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_items() {
|
fn test_items() {
|
||||||
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
|
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 {
|
Jid {
|
||||||
node: Some(String::from("coucou")),
|
node: Some(String::from("coucou")),
|
||||||
domain: String::from("coucou"),
|
domain: String::from("coucou"),
|
||||||
|
|
@ -149,7 +153,7 @@ mod tests {
|
||||||
domain: String::from("domain"),
|
domain: String::from("domain"),
|
||||||
resource: None,
|
resource: None,
|
||||||
},
|
},
|
||||||
);
|
];
|
||||||
|
|
||||||
let request_elem = elem.clone();
|
let request_elem = elem.clone();
|
||||||
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
||||||
|
|
@ -174,7 +178,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 request_elem = elem.clone();
|
||||||
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
|
|
@ -191,7 +197,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Unknown attribute in blocklist element.");
|
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 error = Block::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -199,7 +207,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Unknown attribute in block element.");
|
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 error = Unblock::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ use jid::Jid;
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// Whether a conference bookmark should be joined automatically.
|
/// Whether a conference bookmark should be joined automatically.
|
||||||
Autojoin, "autojoin", bool
|
Autojoin,
|
||||||
|
"autojoin",
|
||||||
|
bool
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -70,9 +72,9 @@ impl Storage {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -111,7 +113,10 @@ mod tests {
|
||||||
assert_eq!(storage.urls[0].url, "https://example.org/");
|
assert_eq!(storage.urls[0].url, "https://example.org/");
|
||||||
assert_eq!(storage.conferences.len(), 1);
|
assert_eq!(storage.conferences.len(), 1);
|
||||||
assert_eq!(storage.conferences[0].autojoin, Autojoin::True);
|
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].name, "Test MUC");
|
||||||
assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou");
|
assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou");
|
||||||
assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret");
|
assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret");
|
||||||
|
|
|
||||||
67
src/caps.rs
67
src/caps.rs
|
|
@ -4,23 +4,20 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::data_forms::DataForm;
|
||||||
use crate::hashes::{Hash, Algo};
|
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
|
||||||
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::hashes::{Algo, Hash};
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use crate::presence::PresencePayload;
|
||||||
use base64;
|
use base64;
|
||||||
|
use blake2::VarBlake2b;
|
||||||
|
use digest::{Digest, Input, VariableOutput};
|
||||||
|
use minidom::Element;
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
use sha2::{Sha256, Sha512};
|
use sha2::{Sha256, Sha512};
|
||||||
use sha3::{Sha3_256, Sha3_512};
|
use sha3::{Sha3_256, Sha3_512};
|
||||||
use blake2::VarBlake2b;
|
use try_from::TryFrom;
|
||||||
use digest::{Digest, VariableOutput, Input};
|
|
||||||
|
|
||||||
/// Represents a capability hash for a given client.
|
/// Represents a capability hash for a given client.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -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> {
|
fn compute_items<T, F: Fn(&T) -> Vec<u8>>(things: &[T], encode: F) -> Vec<u8> {
|
||||||
let mut string: Vec<u8> = vec!();
|
let mut string: Vec<u8> = vec![];
|
||||||
let mut accumulator: Vec<Vec<u8>> = vec!();
|
let mut accumulator: Vec<Vec<u8>> = vec![];
|
||||||
for thing in things {
|
for thing in things {
|
||||||
let bytes = encode(thing);
|
let bytes = encode(thing);
|
||||||
accumulator.push(bytes);
|
accumulator.push(bytes);
|
||||||
|
|
@ -114,7 +111,7 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
|
||||||
|
|
||||||
fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
||||||
compute_items(extensions, |extension| {
|
compute_items(extensions, |extension| {
|
||||||
let mut bytes = vec!();
|
let mut bytes = vec![];
|
||||||
// TODO: maybe handle the error case?
|
// TODO: maybe handle the error case?
|
||||||
if let Some(ref form_type) = extension.form_type {
|
if let Some(ref form_type) = extension.form_type {
|
||||||
bytes.extend_from_slice(form_type.as_bytes());
|
bytes.extend_from_slice(form_type.as_bytes());
|
||||||
|
|
@ -125,8 +122,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bytes.append(&mut compute_item(&field.var));
|
bytes.append(&mut compute_item(&field.var));
|
||||||
bytes.append(&mut compute_items(&field.values,
|
bytes.append(&mut compute_items(&field.values, |value| {
|
||||||
|value| compute_item(value)));
|
compute_item(value)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
bytes
|
bytes
|
||||||
})
|
})
|
||||||
|
|
@ -143,7 +141,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
|
||||||
let features_string = compute_features(&disco.features);
|
let features_string = compute_features(&disco.features);
|
||||||
let extensions_string = compute_extensions(&disco.extensions);
|
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(identities_string);
|
||||||
final_string.extend(features_string);
|
final_string.extend(features_string);
|
||||||
final_string.extend(extensions_string);
|
final_string.extend(extensions_string);
|
||||||
|
|
@ -164,33 +162,33 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result<Hash, String> {
|
||||||
Algo::Sha_1 => {
|
Algo::Sha_1 => {
|
||||||
let hash = Sha1::digest(data);
|
let hash = Sha1::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha_256 => {
|
Algo::Sha_256 => {
|
||||||
let hash = Sha256::digest(data);
|
let hash = Sha256::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha_512 => {
|
Algo::Sha_512 => {
|
||||||
let hash = Sha512::digest(data);
|
let hash = Sha512::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha3_256 => {
|
Algo::Sha3_256 => {
|
||||||
let hash = Sha3_256::digest(data);
|
let hash = Sha3_256::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha3_512 => {
|
Algo::Sha3_512 => {
|
||||||
let hash = Sha3_512::digest(data);
|
let hash = Sha3_512::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Blake2b_256 => {
|
Algo::Blake2b_256 => {
|
||||||
let mut hasher = VarBlake2b::new(32).unwrap();
|
let mut hasher = VarBlake2b::new(32).unwrap();
|
||||||
hasher.input(data);
|
hasher.input(data);
|
||||||
hasher.vec_result()
|
hasher.vec_result()
|
||||||
},
|
}
|
||||||
Algo::Blake2b_512 => {
|
Algo::Blake2b_512 => {
|
||||||
let mut hasher = VarBlake2b::new(64).unwrap();
|
let mut hasher = VarBlake2b::new(64).unwrap();
|
||||||
hasher.input(data);
|
hasher.input(data);
|
||||||
hasher.vec_result()
|
hasher.vec_result()
|
||||||
},
|
}
|
||||||
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
|
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
|
||||||
},
|
},
|
||||||
algo: algo,
|
algo: algo,
|
||||||
|
|
@ -229,7 +227,10 @@ mod tests {
|
||||||
let caps = Caps::try_from(elem).unwrap();
|
let caps = Caps::try_from(elem).unwrap();
|
||||||
assert_eq!(caps.node, String::from("coucou"));
|
assert_eq!(caps.node, String::from("coucou"));
|
||||||
assert_eq!(caps.hash.algo, Algo::Sha_256);
|
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]
|
#[test]
|
||||||
|
|
@ -262,7 +263,9 @@ mod tests {
|
||||||
<feature var='http://jabber.org/protocol/disco#items'/>
|
<feature var='http://jabber.org/protocol/disco#items'/>
|
||||||
<feature var='http://jabber.org/protocol/muc'/>
|
<feature var='http://jabber.org/protocol/muc'/>
|
||||||
</query>
|
</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 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());
|
let mut expected = Vec::with_capacity(data.len());
|
||||||
|
|
@ -272,7 +275,10 @@ mod tests {
|
||||||
assert_eq!(caps, expected);
|
assert_eq!(caps, expected);
|
||||||
|
|
||||||
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
|
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]
|
#[test]
|
||||||
|
|
@ -308,7 +314,9 @@ mod tests {
|
||||||
</field>
|
</field>
|
||||||
</x>
|
</x>
|
||||||
</query>
|
</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 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());
|
let mut expected = Vec::with_capacity(data.len());
|
||||||
expected.extend_from_slice(data);
|
expected.extend_from_slice(data);
|
||||||
|
|
@ -317,6 +325,9 @@ mod tests {
|
||||||
assert_eq!(caps, expected);
|
assert_eq!(caps, expected);
|
||||||
|
|
||||||
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_size() {
|
fn test_size() {
|
||||||
|
|
@ -44,13 +44,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
ChatState::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = ChatState::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -61,7 +65,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = ChatState::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -72,7 +78,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
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 error = ChatState::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use minidom::{Node, Element};
|
use minidom::{Element, Node};
|
||||||
|
|
||||||
pub trait NamespaceAwareCompare {
|
pub trait NamespaceAwareCompare {
|
||||||
/// Namespace-aware comparison for tests
|
/// Namespace-aware comparison for tests
|
||||||
|
|
@ -14,10 +14,10 @@ pub trait NamespaceAwareCompare {
|
||||||
impl NamespaceAwareCompare for Node {
|
impl NamespaceAwareCompare for Node {
|
||||||
fn compare_to(&self, other: &Self) -> bool {
|
fn compare_to(&self, other: &Self) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(&Node::Element(ref elem1), &Node::Element(ref elem2)) =>
|
(&Node::Element(ref elem1), &Node::Element(ref elem2)) => {
|
||||||
Element::compare_to(elem1, elem2),
|
Element::compare_to(elem1, elem2)
|
||||||
(&Node::Text(ref text1), &Node::Text(ref text2)) =>
|
}
|
||||||
text1 == text2,
|
(&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,20 +25,21 @@ impl NamespaceAwareCompare for Node {
|
||||||
|
|
||||||
impl NamespaceAwareCompare for Element {
|
impl NamespaceAwareCompare for Element {
|
||||||
fn compare_to(&self, other: &Self) -> bool {
|
fn compare_to(&self, other: &Self) -> bool {
|
||||||
if self.name() == other.name() &&
|
if self.name() == other.name() && self.ns() == other.ns() && self.attrs().eq(other.attrs())
|
||||||
self.ns() == other.ns() &&
|
|
||||||
self.attrs().eq(other.attrs())
|
|
||||||
{
|
{
|
||||||
let child_elems = self.children().count();
|
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));
|
.all(|text| text.chars().all(char::is_whitespace));
|
||||||
if child_elems > 0 && text_is_whitespace {
|
if child_elems > 0 && text_is_whitespace {
|
||||||
// Ignore all the whitespace text nodes
|
// Ignore all the whitespace text nodes
|
||||||
self.children().zip(other.children())
|
self.children()
|
||||||
|
.zip(other.children())
|
||||||
.all(|(node1, node2)| node1.compare_to(node2))
|
.all(|(node1, node2)| node1.compare_to(node2))
|
||||||
} else {
|
} else {
|
||||||
// Compare with text nodes
|
// Compare with text nodes
|
||||||
self.nodes().zip(other.nodes())
|
self.nodes()
|
||||||
|
.zip(other.nodes())
|
||||||
.all(|(node1, node2)| node1.compare_to(node2))
|
.all(|(node1, node2)| node1.compare_to(node2))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::helpers::PlainText;
|
use crate::helpers::PlainText;
|
||||||
use sha1::Sha1;
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
|
use sha1::Sha1;
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// The main authentication mechanism for components.
|
/// The main authentication mechanism for components.
|
||||||
|
|
@ -25,9 +25,7 @@ generate_element!(
|
||||||
impl Handshake {
|
impl Handshake {
|
||||||
/// Creates a successful reply from a server.
|
/// Creates a successful reply from a server.
|
||||||
pub fn new() -> Handshake {
|
pub fn new() -> Handshake {
|
||||||
Handshake {
|
Handshake { data: None }
|
||||||
data: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an authentication request from the component.
|
/// Creates an authentication request from the component.
|
||||||
|
|
@ -44,8 +42,8 @@ impl Handshake {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -61,11 +59,15 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let handshake = Handshake::try_from(elem).unwrap();
|
||||||
assert_eq!(handshake.data, None);
|
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();
|
let handshake = Handshake::try_from(elem).unwrap();
|
||||||
assert_eq!(handshake.data, Some(String::from("Coucou")));
|
assert_eq!(handshake.data, Some(String::from("Coucou")));
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +78,9 @@ mod tests {
|
||||||
assert_eq!(handshake.data, None);
|
assert_eq!(handshake.data, None);
|
||||||
|
|
||||||
let handshake = Handshake::from_password_and_stream_id("123456", "sid");
|
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
|
// 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/.
|
// 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::error::Error;
|
||||||
use crate::ns;
|
|
||||||
|
|
||||||
use crate::media_element::MediaElement;
|
use crate::media_element::MediaElement;
|
||||||
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Represents one of the possible values for a list- field.
|
/// Represents one of the possible values for a list- field.
|
||||||
|
|
@ -98,8 +95,7 @@ pub struct Field {
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
fn is_list(&self) -> bool {
|
fn is_list(&self) -> bool {
|
||||||
self.type_ == FieldType::ListSingle ||
|
self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti
|
||||||
self.type_ == FieldType::ListMulti
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,9 +110,9 @@ impl TryFrom<Element> for Field {
|
||||||
type_: get_attr!(elem, "type", default),
|
type_: get_attr!(elem, "type", default),
|
||||||
label: get_attr!(elem, "label", optional),
|
label: get_attr!(elem, "label", optional),
|
||||||
required: false,
|
required: false,
|
||||||
options: vec!(),
|
options: vec![],
|
||||||
values: vec!(),
|
values: vec![],
|
||||||
media: vec!(),
|
media: vec![],
|
||||||
};
|
};
|
||||||
for element in elem.children() {
|
for element in elem.children() {
|
||||||
if element.is("value", ns::DATA_FORMS) {
|
if element.is("value", ns::DATA_FORMS) {
|
||||||
|
|
@ -140,7 +136,9 @@ impl TryFrom<Element> for Field {
|
||||||
let media_element = MediaElement::try_from(element.clone())?;
|
let media_element = MediaElement::try_from(element.clone())?;
|
||||||
field.media.push(media_element);
|
field.media.push(media_element);
|
||||||
} else {
|
} 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)
|
Ok(field)
|
||||||
|
|
@ -154,11 +152,24 @@ impl From<Field> for Element {
|
||||||
.attr("var", field.var)
|
.attr("var", field.var)
|
||||||
.attr("type", field.type_)
|
.attr("type", field.type_)
|
||||||
.attr("label", field.label)
|
.attr("label", field.label)
|
||||||
.append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None })
|
.append(if field.required {
|
||||||
|
Some(Element::builder("required").ns(ns::DATA_FORMS).build())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
.append(field.options)
|
.append(field.options)
|
||||||
.append(field.values.into_iter().map(|value| {
|
.append(
|
||||||
Element::builder("value").ns(ns::DATA_FORMS).append(value).build()
|
field
|
||||||
}).collect::<Vec<_>>())
|
.values
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| {
|
||||||
|
Element::builder("value")
|
||||||
|
.ns(ns::DATA_FORMS)
|
||||||
|
.append(value)
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
.append(field.media)
|
.append(field.media)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -215,7 +226,7 @@ impl TryFrom<Element> for DataForm {
|
||||||
form_type: None,
|
form_type: None,
|
||||||
title: None,
|
title: None,
|
||||||
instructions: None,
|
instructions: None,
|
||||||
fields: vec!(),
|
fields: vec![],
|
||||||
};
|
};
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if child.is("title", ns::DATA_FORMS) {
|
if child.is("title", ns::DATA_FORMS) {
|
||||||
|
|
@ -227,7 +238,9 @@ impl TryFrom<Element> for DataForm {
|
||||||
form.title = Some(child.text());
|
form.title = Some(child.text());
|
||||||
} else if child.is("instructions", ns::DATA_FORMS) {
|
} else if child.is("instructions", ns::DATA_FORMS) {
|
||||||
if form.instructions.is_some() {
|
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_children!(child, "instructions");
|
||||||
check_no_attributes!(child, "instructions");
|
check_no_attributes!(child, "instructions");
|
||||||
|
|
@ -257,8 +270,15 @@ impl From<DataForm> for Element {
|
||||||
Element::builder("x")
|
Element::builder("x")
|
||||||
.ns(ns::DATA_FORMS)
|
.ns(ns::DATA_FORMS)
|
||||||
.attr("type", form.type_)
|
.attr("type", form.type_)
|
||||||
.append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)))
|
.append(
|
||||||
.append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text)))
|
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)
|
.append(form.fields)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +338,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_wrong_child() {
|
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 error = DataForm::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -329,12 +351,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn option() {
|
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();
|
let option = Option_::try_from(elem).unwrap();
|
||||||
assert_eq!(&option.label.unwrap(), "Coucou !");
|
assert_eq!(&option.label.unwrap(), "Coucou !");
|
||||||
assert_eq!(&option.value, "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 error = Option_::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -348,6 +375,9 @@ mod tests {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => 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
|
// 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/.
|
// 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 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
|
/// Implements the DateTime profile of XEP-0082, which represents a
|
||||||
/// non-recurring moment in time, with an accuracy of seconds or fraction of
|
/// non-recurring moment in time, with an accuracy of seconds or fraction of
|
||||||
|
|
@ -114,7 +112,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
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();
|
let attr = date.into_attribute_value();
|
||||||
assert_eq!(attr, Some(String::from("2017-05-21T20:19:55+01:00")));
|
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
|
// 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/.
|
// 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::message::MessagePayload;
|
||||||
use crate::presence::PresencePayload;
|
use crate::presence::PresencePayload;
|
||||||
use crate::date::DateTime;
|
|
||||||
|
|
||||||
use jid::Jid;
|
use jid::Jid;
|
||||||
|
|
||||||
use crate::helpers::PlainText;
|
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Notes when and by whom a message got stored for later delivery.
|
/// Notes when and by whom a message got stored for later delivery.
|
||||||
Delay, "delay", DELAY,
|
Delay, "delay", DELAY,
|
||||||
|
|
@ -34,10 +32,10 @@ impl PresencePayload for Delay {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -53,16 +51,24 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let delay = Delay::try_from(elem).unwrap();
|
||||||
assert_eq!(delay.from, Some(Jid::from_str("capulet.com").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);
|
assert_eq!(delay.data, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown() {
|
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 error = Delay::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -73,7 +79,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Delay::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -84,7 +92,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
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 {
|
let delay = Delay {
|
||||||
from: None,
|
from: None,
|
||||||
stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(),
|
stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(),
|
||||||
|
|
|
||||||
105
src/disco.rs
105
src/disco.rs
|
|
@ -4,16 +4,13 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::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!(
|
generate_element!(
|
||||||
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` 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> {
|
fn try_from(elem: Element) -> Result<Identity, Error> {
|
||||||
check_self!(elem, "identity", DISCO_INFO, "disco#info identity");
|
check_self!(elem, "identity", DISCO_INFO, "disco#info identity");
|
||||||
check_no_children!(elem, "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);
|
let category = get_attr!(elem, "category", required);
|
||||||
if category == "" {
|
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);
|
let type_ = get_attr!(elem, "type", required);
|
||||||
if type_ == "" {
|
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 {
|
Ok(Identity {
|
||||||
|
|
@ -122,9 +127,9 @@ impl TryFrom<Element> for DiscoInfoResult {
|
||||||
|
|
||||||
let mut result = DiscoInfoResult {
|
let mut result = DiscoInfoResult {
|
||||||
node: get_attr!(elem, "node", optional),
|
node: get_attr!(elem, "node", optional),
|
||||||
identities: vec!(),
|
identities: vec![],
|
||||||
features: vec!(),
|
features: vec![],
|
||||||
extensions: vec!(),
|
extensions: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
|
|
@ -137,7 +142,9 @@ impl TryFrom<Element> for DiscoInfoResult {
|
||||||
} else if child.is("x", ns::DATA_FORMS) {
|
} else if child.is("x", ns::DATA_FORMS) {
|
||||||
let data_form = DataForm::try_from(child.clone())?;
|
let data_form = DataForm::try_from(child.clone())?;
|
||||||
if data_form.type_ != DataFormType::Result_ {
|
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() {
|
if data_form.form_type.is_none() {
|
||||||
return Err(Error::ParseError("Data form found without a FORM_TYPE."));
|
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() {
|
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() {
|
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() }) {
|
if !result.features.contains(&Feature {
|
||||||
return Err(Error::ParseError("disco#info feature not present in disco#info."));
|
var: ns::DISCO_INFO.to_owned(),
|
||||||
|
}) {
|
||||||
|
return Err(Error::ParseError(
|
||||||
|
"disco#info feature not present in disco#info.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|
@ -295,7 +310,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -306,7 +324,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_identity() {
|
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 error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -314,13 +335,19 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'category' missing.");
|
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 error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => 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 elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap();
|
||||||
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
|
|
@ -341,7 +368,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_feature() {
|
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 error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -352,13 +382,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_result() {
|
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 error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => 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 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();
|
let error = DiscoInfoResult::try_from(elem).unwrap_err();
|
||||||
|
|
@ -379,23 +414,31 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_items() {
|
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();
|
let query = DiscoItemsQuery::try_from(elem).unwrap();
|
||||||
assert!(query.node.is_none());
|
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();
|
let query = DiscoItemsQuery::try_from(elem).unwrap();
|
||||||
assert_eq!(query.node, Some(String::from("coucou")));
|
assert_eq!(query.node, Some(String::from("coucou")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_items_result() {
|
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();
|
let query = DiscoItemsResult::try_from(elem).unwrap();
|
||||||
assert!(query.node.is_none());
|
assert!(query.node.is_none());
|
||||||
assert!(query.items.is_empty());
|
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();
|
let query = DiscoItemsResult::try_from(elem).unwrap();
|
||||||
assert_eq!(query.node, Some(String::from("coucou")));
|
assert_eq!(query.node, Some(String::from("coucou")));
|
||||||
assert!(query.items.is_empty());
|
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
|
// 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/.
|
// 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::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::ns;
|
||||||
|
use crate::presence::PresencePayload;
|
||||||
use base64;
|
use base64;
|
||||||
|
use blake2::VarBlake2b;
|
||||||
|
use digest::{Digest, Input, VariableOutput};
|
||||||
use sha2::{Sha256, Sha512};
|
use sha2::{Sha256, Sha512};
|
||||||
use sha3::{Sha3_256, Sha3_512};
|
use sha3::{Sha3_256, Sha3_512};
|
||||||
use blake2::VarBlake2b;
|
|
||||||
use digest::{Digest, VariableOutput, Input};
|
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Represents a set of capability hashes, all of them must correspond to
|
/// 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> {
|
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 string: Vec<u8> = vec![];
|
||||||
let mut accumulator: Vec<Vec<u8>> = vec!();
|
let mut accumulator: Vec<Vec<u8>> = vec![];
|
||||||
for thing in things {
|
for thing in things {
|
||||||
let bytes = encode(thing);
|
let bytes = encode(thing);
|
||||||
accumulator.push(bytes);
|
accumulator.push(bytes);
|
||||||
|
|
@ -60,8 +58,12 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
|
||||||
compute_items(identities, 0x1c, |identity| {
|
compute_items(identities, 0x1c, |identity| {
|
||||||
let mut bytes = compute_item(&identity.category);
|
let mut bytes = compute_item(&identity.category);
|
||||||
bytes.append(&mut compute_item(&identity.type_));
|
bytes.append(&mut compute_item(&identity.type_));
|
||||||
bytes.append(&mut compute_item(&identity.lang.clone().unwrap_or_default()));
|
bytes.append(&mut compute_item(
|
||||||
bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default()));
|
&identity.lang.clone().unwrap_or_default(),
|
||||||
|
));
|
||||||
|
bytes.append(&mut compute_item(
|
||||||
|
&identity.name.clone().unwrap_or_default(),
|
||||||
|
));
|
||||||
bytes.push(0x1e);
|
bytes.push(0x1e);
|
||||||
bytes
|
bytes
|
||||||
})
|
})
|
||||||
|
|
@ -71,8 +73,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
|
||||||
compute_items(extensions, 0x1c, |extension| {
|
compute_items(extensions, 0x1c, |extension| {
|
||||||
compute_items(&extension.fields, 0x1d, |field| {
|
compute_items(&extension.fields, 0x1d, |field| {
|
||||||
let mut bytes = compute_item(&field.var);
|
let mut bytes = compute_item(&field.var);
|
||||||
bytes.append(&mut compute_items(&field.values, 0x1e,
|
bytes.append(&mut compute_items(&field.values, 0x1e, |value| {
|
||||||
|value| compute_item(value)));
|
compute_item(value)
|
||||||
|
}));
|
||||||
bytes
|
bytes
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -86,7 +89,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
|
||||||
let identities_string = compute_identities(&disco.identities);
|
let identities_string = compute_identities(&disco.identities);
|
||||||
let extensions_string = compute_extensions(&disco.extensions);
|
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(features_string);
|
||||||
final_string.extend(identities_string);
|
final_string.extend(identities_string);
|
||||||
final_string.extend(extensions_string);
|
final_string.extend(extensions_string);
|
||||||
|
|
@ -107,29 +110,29 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, String> {
|
||||||
Algo::Sha_256 => {
|
Algo::Sha_256 => {
|
||||||
let hash = Sha256::digest(data);
|
let hash = Sha256::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha_512 => {
|
Algo::Sha_512 => {
|
||||||
let hash = Sha512::digest(data);
|
let hash = Sha512::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha3_256 => {
|
Algo::Sha3_256 => {
|
||||||
let hash = Sha3_256::digest(data);
|
let hash = Sha3_256::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Sha3_512 => {
|
Algo::Sha3_512 => {
|
||||||
let hash = Sha3_512::digest(data);
|
let hash = Sha3_512::digest(data);
|
||||||
get_hash_vec(hash.as_slice())
|
get_hash_vec(hash.as_slice())
|
||||||
},
|
}
|
||||||
Algo::Blake2b_256 => {
|
Algo::Blake2b_256 => {
|
||||||
let mut hasher = VarBlake2b::new(32).unwrap();
|
let mut hasher = VarBlake2b::new(32).unwrap();
|
||||||
hasher.input(data);
|
hasher.input(data);
|
||||||
hasher.vec_result()
|
hasher.vec_result()
|
||||||
},
|
}
|
||||||
Algo::Blake2b_512 => {
|
Algo::Blake2b_512 => {
|
||||||
let mut hasher = VarBlake2b::new(64).unwrap();
|
let mut hasher = VarBlake2b::new(64).unwrap();
|
||||||
hasher.input(data);
|
hasher.input(data);
|
||||||
hasher.vec_result()
|
hasher.vec_result()
|
||||||
},
|
}
|
||||||
Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")),
|
Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")),
|
||||||
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
|
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.
|
/// ecaps2 hash.
|
||||||
pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
|
pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
|
||||||
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -170,9 +178,15 @@ mod tests {
|
||||||
let ecaps2 = ECaps2::try_from(elem).unwrap();
|
let ecaps2 = ECaps2::try_from(elem).unwrap();
|
||||||
assert_eq!(ecaps2.hashes.len(), 2);
|
assert_eq!(ecaps2.hashes.len(), 2);
|
||||||
assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256);
|
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].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]
|
#[test]
|
||||||
|
|
@ -217,50 +231,52 @@ mod tests {
|
||||||
<feature var="jabber:iq:roster"/>
|
<feature var="jabber:iq:roster"/>
|
||||||
<feature var="jabber:iq:last"/>
|
<feature var="jabber:iq:last"/>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
.parse()
|
||||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
.unwrap();
|
||||||
108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
|
let expected = vec![
|
||||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104,
|
114, 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109,
|
||||||
97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58,
|
115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116,
|
||||||
111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
|
101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||||
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
|
||||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||||
108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31,
|
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35,
|
||||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
105, 116, 101, 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104,
|
||||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116,
|
||||||
114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47,
|
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
||||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||||
111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
|
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105,
|
||||||
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
|
||||||
114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111,
|
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116,
|
||||||
102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
|
31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31,
|
||||||
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97,
|
||||||
58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
98, 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114,
|
||||||
58, 112, 114, 105, 118, 97, 99, 121, 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,
|
||||||
58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98,
|
120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110,
|
||||||
98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97,
|
103, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
|
||||||
98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111,
|
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99,
|
||||||
110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31,
|
108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98,
|
||||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
|
117, 115, 77, 111, 100, 31, 30, 28, 28,
|
||||||
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 disco = DiscoInfoResult::try_from(elem).unwrap();
|
||||||
let ecaps2 = compute_disco(&disco);
|
let ecaps2 = compute_disco(&disco);
|
||||||
assert_eq!(ecaps2.len(), 0x1d9);
|
assert_eq!(ecaps2.len(), 0x1d9);
|
||||||
assert_eq!(ecaps2, expected);
|
assert_eq!(ecaps2, expected);
|
||||||
|
|
||||||
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
|
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();
|
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]
|
#[test]
|
||||||
|
|
@ -329,121 +345,111 @@ mod tests {
|
||||||
</field>
|
</field>
|
||||||
</x>
|
</x>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100,
|
.parse()
|
||||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
.unwrap();
|
||||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97,
|
let expected = vec![
|
||||||
99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47,
|
103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||||
111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116,
|
108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106,
|
||||||
121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58,
|
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116,
|
||||||
111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114,
|
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
|
||||||
101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98,
|
116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
|
||||||
98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,
|
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||||
111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31,
|
114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115,
|
||||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111,
|
112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31,
|
||||||
109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106,
|
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||||
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111,
|
||||||
111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102,
|
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||||
111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101,
|
||||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||||
100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116,
|
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,
|
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
|
||||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108,
|
116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31,
|
||||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102,
|
114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116,
|
||||||
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,
|
||||||
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121,
|
||||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111,
|
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||||
99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47,
|
||||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47,
|
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||||
103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31,
|
108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111,
|
||||||
114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111,
|
||||||
46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47,
|
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||||
105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101,
|
||||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111,
|
114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||||
99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58,
|
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
|
||||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
|
||||||
111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111,
|
99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108,
|
||||||
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
|
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||||
99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116,
|
108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46,
|
||||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109,
|
||||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104,
|
101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119,
|
||||||
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111,
|
||||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47,
|
110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115,
|
||||||
112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116,
|
45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97,
|
||||||
114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47,
|
98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98,
|
||||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114,
|
||||||
111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116,
|
58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||||
116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111,
|
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||||
111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101,
|
58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97,
|
||||||
115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47,
|
110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105,
|
||||||
119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101,
|
110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106,
|
||||||
120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45,
|
97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114,
|
||||||
48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101,
|
58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58,
|
||||||
116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31,
|
105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105,
|
||||||
106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97,
|
113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101,
|
||||||
114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111,
|
114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97,
|
||||||
119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98,
|
||||||
100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58,
|
||||||
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101,
|
97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116,
|
||||||
114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114,
|
105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
|
||||||
58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98,
|
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31,
|
||||||
101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31,
|
117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105,
|
||||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31,
|
101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
|
||||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106,
|
99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
|
||||||
97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99,
|
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89,
|
||||||
121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111,
|
80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114,
|
||||||
115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111,
|
||||||
116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115,
|
||||||
58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101,
|
105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107,
|
||||||
114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114,
|
97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114,
|
||||||
58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101,
|
115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49,
|
||||||
114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109,
|
49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54,
|
||||||
112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97,
|
98, 50, 41, 31, 30, 29, 28,
|
||||||
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 disco = DiscoInfoResult::try_from(elem).unwrap();
|
||||||
let ecaps2 = compute_disco(&disco);
|
let ecaps2 = compute_disco(&disco);
|
||||||
assert_eq!(ecaps2.len(), 0x543);
|
assert_eq!(ecaps2.len(), 0x543);
|
||||||
assert_eq!(ecaps2, expected);
|
assert_eq!(ecaps2, expected);
|
||||||
|
|
||||||
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
|
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();
|
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]
|
#[test]
|
||||||
fn test_blake2b_512() {
|
fn test_blake2b_512() {
|
||||||
let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap();
|
let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap();
|
||||||
let known_hash: Vec<u8> = vec!(
|
let known_hash: Vec<u8> = vec![
|
||||||
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9,
|
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12,
|
||||||
0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1,
|
0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F,
|
||||||
0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95,
|
0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52,
|
||||||
0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
|
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);
|
assert_eq!(hash.hash, known_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/eme.rs
25
src/eme.rs
|
|
@ -24,9 +24,9 @@ impl MessagePayload for ExplicitMessageEncryption {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -42,7 +42,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let encryption = ExplicitMessageEncryption::try_from(elem).unwrap();
|
||||||
assert_eq!(encryption.namespace, "urn:xmpp:otr:0");
|
assert_eq!(encryption.namespace, "urn:xmpp:otr:0");
|
||||||
assert_eq!(encryption.name, None);
|
assert_eq!(encryption.name, None);
|
||||||
|
|
@ -55,7 +57,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown() {
|
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 error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -66,7 +70,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -77,8 +83,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>".parse().unwrap();
|
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>"
|
||||||
let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None };
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
let eme = ExplicitMessageEncryption {
|
||||||
|
namespace: String::from("coucou"),
|
||||||
|
name: None,
|
||||||
|
};
|
||||||
let elem2 = eme.into();
|
let elem2 = eme.into();
|
||||||
assert_eq!(elem, elem2);
|
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
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use base64;
|
||||||
|
use chrono;
|
||||||
|
use jid;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::num;
|
|
||||||
use std::string;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net;
|
use std::net;
|
||||||
|
use std::num;
|
||||||
use base64;
|
use std::string;
|
||||||
use jid;
|
|
||||||
use chrono;
|
|
||||||
|
|
||||||
/// Contains one of the potential errors triggered while parsing an
|
/// Contains one of the potential errors triggered while parsing an
|
||||||
/// [Element](../struct.Element.html) into a specialised struct.
|
/// [Element](../struct.Element.html) into a specialised struct.
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ generate_element!(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -50,7 +50,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Forwarded::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -62,7 +64,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'/>".parse().unwrap();
|
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();
|
let elem2 = forwarded.into();
|
||||||
assert_eq!(elem, elem2);
|
assert_eq!(elem, elem2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,11 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::error::Error;
|
||||||
|
|
||||||
use crate::helpers::Base64;
|
use crate::helpers::Base64;
|
||||||
use base64;
|
use base64;
|
||||||
|
use minidom::IntoAttributeValue;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// List of the algorithms we support, or Unknown.
|
/// List of the algorithms we support, or Unknown.
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
|
@ -114,10 +111,7 @@ generate_element!(
|
||||||
impl Hash {
|
impl Hash {
|
||||||
/// Creates a [Hash] element with the given algo and data.
|
/// Creates a [Hash] element with the given algo and data.
|
||||||
pub fn new(algo: Algo, hash: Vec<u8>) -> Hash {
|
pub fn new(algo: Algo, hash: Vec<u8>) -> Hash {
|
||||||
Hash {
|
Hash { algo, hash }
|
||||||
algo,
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [new](#method.new) but takes base64-encoded data before decoding
|
/// Like [new](#method.new) but takes base64-encoded data before decoding
|
||||||
|
|
@ -130,8 +124,8 @@ impl Hash {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[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 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();
|
let hash = Hash::try_from(elem).unwrap();
|
||||||
assert_eq!(hash.algo, Algo::Sha_256);
|
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]
|
#[test]
|
||||||
fn test_unknown() {
|
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 error = Hash::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -168,7 +167,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Hash::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use base64;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use base64;
|
||||||
|
|
||||||
/// Codec for plain text content.
|
/// Codec for plain text content.
|
||||||
pub struct PlainText;
|
pub struct PlainText;
|
||||||
|
|
@ -19,9 +19,7 @@ impl PlainText {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(string: &Option<String>) -> Option<String> {
|
pub fn encode(string: &Option<String>) -> Option<String> {
|
||||||
string.as_ref().map(|text| {
|
string.as_ref().map(|text| text.to_owned())
|
||||||
text.to_owned()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
35
src/ibb.rs
35
src/ibb.rs
|
|
@ -8,8 +8,9 @@ use crate::helpers::Base64;
|
||||||
use crate::iq::IqSetPayload;
|
use crate::iq::IqSetPayload;
|
||||||
|
|
||||||
generate_id!(
|
generate_id!(
|
||||||
/// An identifier matching a stream.
|
/// An identifier matching a stream.
|
||||||
StreamId);
|
StreamId
|
||||||
|
);
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// Which stanza type to use to exchange data.
|
/// Which stanza type to use to exchange data.
|
||||||
|
|
@ -71,10 +72,10 @@ impl IqSetPayload for Close {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -98,26 +99,36 @@ mod tests {
|
||||||
fn test_simple() {
|
fn test_simple() {
|
||||||
let sid = StreamId(String::from("coucou"));
|
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();
|
let open = Open::try_from(elem).unwrap();
|
||||||
assert_eq!(open.block_size, 3);
|
assert_eq!(open.block_size, 3);
|
||||||
assert_eq!(open.sid, sid);
|
assert_eq!(open.sid, sid);
|
||||||
assert_eq!(open.stanza, Stanza::Iq);
|
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();
|
let data = Data::try_from(elem).unwrap();
|
||||||
assert_eq!(data.seq, 0);
|
assert_eq!(data.seq, 0);
|
||||||
assert_eq!(data.sid, sid);
|
assert_eq!(data.sid, sid);
|
||||||
assert_eq!(data.data, vec!(0, 0, 0));
|
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();
|
let close = Close::try_from(elem).unwrap();
|
||||||
assert_eq!(close.sid, sid);
|
assert_eq!(close.sid, sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = Open::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -125,7 +136,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'block-size' missing.");
|
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 error = Open::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
|
|
@ -133,7 +146,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message.description(), "invalid digit found in string");
|
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 error = Open::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(error) => error,
|
Error::ParseError(error) => error,
|
||||||
|
|
|
||||||
87
src/ibr.rs
87
src/ibr.rs
|
|
@ -4,18 +4,14 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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 std::collections::HashMap;
|
||||||
use try_from::TryFrom;
|
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.
|
/// Query for registering against a service.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Query {
|
pub struct Query {
|
||||||
|
|
@ -31,7 +27,6 @@ pub struct Query {
|
||||||
|
|
||||||
/// A data form the user must fill before being allowed to register.
|
/// A data form the user must fill before being allowed to register.
|
||||||
pub form: Option<DataForm>,
|
pub form: Option<DataForm>,
|
||||||
|
|
||||||
// Not yet implemented.
|
// Not yet implemented.
|
||||||
//pub oob: Option<Oob>,
|
//pub oob: Option<Oob>,
|
||||||
}
|
}
|
||||||
|
|
@ -55,9 +50,26 @@ impl TryFrom<Element> for Query {
|
||||||
let namespace = child.ns().unwrap();
|
let namespace = child.ns().unwrap();
|
||||||
if namespace == ns::REGISTER {
|
if namespace == ns::REGISTER {
|
||||||
let name = child.name();
|
let name = child.name();
|
||||||
let fields = vec!["address", "city", "date", "email", "first", "instructions",
|
let fields = vec![
|
||||||
"key", "last", "misc", "name", "nick", "password", "phone",
|
"address",
|
||||||
"state", "text", "url", "username", "zip"];
|
"city",
|
||||||
|
"date",
|
||||||
|
"email",
|
||||||
|
"first",
|
||||||
|
"instructions",
|
||||||
|
"key",
|
||||||
|
"last",
|
||||||
|
"misc",
|
||||||
|
"name",
|
||||||
|
"nick",
|
||||||
|
"password",
|
||||||
|
"phone",
|
||||||
|
"state",
|
||||||
|
"text",
|
||||||
|
"url",
|
||||||
|
"username",
|
||||||
|
"zip",
|
||||||
|
];
|
||||||
if fields.binary_search(&name).is_ok() {
|
if fields.binary_search(&name).is_ok() {
|
||||||
query.fields.insert(name.to_owned(), child.text());
|
query.fields.insert(name.to_owned(), child.text());
|
||||||
} else if name == "registered" {
|
} else if name == "registered" {
|
||||||
|
|
@ -81,11 +93,23 @@ impl From<Query> for Element {
|
||||||
fn from(query: Query) -> Element {
|
fn from(query: Query) -> Element {
|
||||||
Element::builder("query")
|
Element::builder("query")
|
||||||
.ns(ns::REGISTER)
|
.ns(ns::REGISTER)
|
||||||
.append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None })
|
.append(if query.registered {
|
||||||
.append(query.fields.into_iter().map(|(name, value)| {
|
Some(Element::builder("registered").ns(ns::REGISTER))
|
||||||
Element::builder(name).ns(ns::REGISTER).append(value)
|
} else {
|
||||||
}).collect::<Vec<_>>())
|
None
|
||||||
.append(if query.remove { Some(Element::builder("remove").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)
|
.append(query.form)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +150,9 @@ mod tests {
|
||||||
<password/>
|
<password/>
|
||||||
<email/>
|
<email/>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let query = Query::try_from(elem).unwrap();
|
let query = Query::try_from(elem).unwrap();
|
||||||
assert_eq!(query.registered, false);
|
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 ");
|
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>
|
</field>
|
||||||
</x>
|
</x>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let elem1 = elem.clone();
|
let elem1 = elem.clone();
|
||||||
let query = Query::try_from(elem).unwrap();
|
let query = Query::try_from(elem).unwrap();
|
||||||
assert_eq!(query.registered, false);
|
assert_eq!(query.registered, false);
|
||||||
assert!(!query.fields["instructions"].is_empty());
|
assert!(!query.fields["instructions"].is_empty());
|
||||||
let form = query.form.clone().unwrap();
|
let form = query.form.clone().unwrap();
|
||||||
assert!(!form.instructions.unwrap().is_empty());
|
assert!(!form.instructions.unwrap().is_empty());
|
||||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
|
assert!(form
|
||||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
|
.fields
|
||||||
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
|
.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();
|
let elem2 = query.into();
|
||||||
assert!(elem1.compare_to(&elem2));
|
assert!(elem1.compare_to(&elem2));
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +245,9 @@ mod tests {
|
||||||
</field>
|
</field>
|
||||||
</x>
|
</x>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let elem1 = elem.clone();
|
let elem1 = elem.clone();
|
||||||
let query = Query::try_from(elem).unwrap();
|
let query = Query::try_from(elem).unwrap();
|
||||||
assert_eq!(query.registered, false);
|
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
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::presence::PresencePayload;
|
|
||||||
use crate::date::DateTime;
|
use crate::date::DateTime;
|
||||||
|
use crate::presence::PresencePayload;
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Represents the last time the user interacted with their system.
|
/// Represents the last time the user interacted with their system.
|
||||||
|
|
@ -21,11 +21,11 @@ impl PresencePayload for Idle {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use std::str::FromStr;
|
use minidom::Element;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_size() {
|
fn test_size() {
|
||||||
|
|
@ -34,13 +34,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
Idle::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -63,7 +67,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_date() {
|
fn test_invalid_date() {
|
||||||
// There is no thirteenth month.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -72,7 +78,9 @@ mod tests {
|
||||||
assert_eq!(message.description(), "input is out of range");
|
assert_eq!(message.description(), "input is out of range");
|
||||||
|
|
||||||
// Timezone ≥24:00 aren’t allowed.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -81,7 +89,9 @@ mod tests {
|
||||||
assert_eq!(message.description(), "input is out of range");
|
assert_eq!(message.description(), "input is out of range");
|
||||||
|
|
||||||
// Timezone without the : separator aren’t allowed.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -90,7 +100,9 @@ mod tests {
|
||||||
assert_eq!(message.description(), "input contains invalid characters");
|
assert_eq!(message.description(), "input contains invalid characters");
|
||||||
|
|
||||||
// No seconds, error message could be improved.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -99,7 +111,9 @@ mod tests {
|
||||||
assert_eq!(message.description(), "input contains invalid characters");
|
assert_eq!(message.description(), "input contains invalid characters");
|
||||||
|
|
||||||
// TODO: maybe we’ll want to support this one, as per XEP-0082 §4.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -108,7 +122,9 @@ mod tests {
|
||||||
assert_eq!(message.description(), "input contains invalid characters");
|
assert_eq!(message.description(), "input contains invalid characters");
|
||||||
|
|
||||||
// No timezone.
|
// 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 error = Idle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ChronoParseError(string) => string,
|
Error::ChronoParseError(string) => string,
|
||||||
|
|
@ -119,8 +135,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
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'/>"
|
||||||
let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() };
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
let idle = Idle {
|
||||||
|
since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap(),
|
||||||
|
};
|
||||||
let elem2 = idle.into();
|
let elem2 = idle.into();
|
||||||
assert_eq!(elem, elem2);
|
assert_eq!(elem, elem2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
91
src/iq.rs
91
src/iq.rs
|
|
@ -5,18 +5,13 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::Element;
|
||||||
use minidom::IntoAttributeValue;
|
use minidom::IntoAttributeValue;
|
||||||
|
use try_from::TryFrom;
|
||||||
use jid::Jid;
|
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
|
|
||||||
use crate::ns;
|
|
||||||
|
|
||||||
use crate::stanza_error::StanzaError;
|
|
||||||
|
|
||||||
/// Should be implemented on every known payload of an `<iq type='get'/>`.
|
/// Should be implemented on every known payload of an `<iq type='get'/>`.
|
||||||
pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
|
pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
|
||||||
|
|
@ -45,12 +40,15 @@ pub enum IqType {
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue for &'a IqType {
|
impl<'a> IntoAttributeValue for &'a IqType {
|
||||||
fn into_attribute_value(self) -> Option<String> {
|
fn into_attribute_value(self) -> Option<String> {
|
||||||
Some(match *self {
|
Some(
|
||||||
|
match *self {
|
||||||
IqType::Get(_) => "get",
|
IqType::Get(_) => "get",
|
||||||
IqType::Set(_) => "set",
|
IqType::Set(_) => "set",
|
||||||
IqType::Result(_) => "result",
|
IqType::Result(_) => "result",
|
||||||
IqType::Error(_) => "error",
|
IqType::Error(_) => "error",
|
||||||
}.to_owned())
|
}
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,9 +206,7 @@ impl From<Iq> for Element {
|
||||||
.attr("type", &iq.payload)
|
.attr("type", &iq.payload)
|
||||||
.build();
|
.build();
|
||||||
let elem = match iq.payload {
|
let elem = match iq.payload {
|
||||||
IqType::Get(elem)
|
IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem,
|
||||||
| IqType::Set(elem)
|
|
||||||
| IqType::Result(Some(elem)) => elem,
|
|
||||||
IqType::Error(error) => error.into(),
|
IqType::Error(error) => error.into(),
|
||||||
IqType::Result(None) => return stanza,
|
IqType::Result(None) => return stanza,
|
||||||
};
|
};
|
||||||
|
|
@ -222,9 +218,9 @@ impl From<Iq> for Element {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::stanza_error::{ErrorType, DefinedCondition};
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
use crate::disco::DiscoInfoQuery;
|
use crate::disco::DiscoInfoQuery;
|
||||||
|
use crate::stanza_error::{DefinedCondition, ErrorType};
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -259,11 +255,15 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='get'>
|
let elem: Element = "<iq xmlns='jabber:client' type='get'>
|
||||||
<foo xmlns='bar'/>
|
<foo xmlns='bar'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
|
||||||
<foo xmlns='bar'/>
|
<foo xmlns='bar'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
|
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
|
|
@ -271,7 +271,7 @@ mod tests {
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(iq.id, None);
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Get(element) => element.compare_to(&query),
|
IqType::Get(element) => element.compare_to(&query),
|
||||||
_ => false
|
_ => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,11 +280,15 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='set'>
|
let elem: Element = "<iq xmlns='jabber:client' type='set'>
|
||||||
<vCard xmlns='vcard-temp'/>
|
<vCard xmlns='vcard-temp'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
|
||||||
<vCard xmlns='vcard-temp'/>
|
<vCard xmlns='vcard-temp'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
|
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
|
|
@ -292,7 +296,7 @@ mod tests {
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(iq.id, None);
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Set(element) => element.compare_to(&vcard),
|
IqType::Set(element) => element.compare_to(&vcard),
|
||||||
_ => false
|
_ => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +305,9 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[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();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
|
|
@ -317,13 +323,19 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'>
|
let elem: Element = "<iq xmlns='jabber:client' type='result'>
|
||||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
|
||||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let iq = Iq::try_from(elem).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.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(iq.id, None);
|
||||||
|
|
@ -341,14 +353,18 @@ mod tests {
|
||||||
<error type='cancel'>
|
<error type='cancel'>
|
||||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
</error>
|
</error>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
|
||||||
<ping xmlns='urn:xmpp:ping'/>
|
<ping xmlns='urn:xmpp:ping'/>
|
||||||
<error type='cancel'>
|
<error type='cancel'>
|
||||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
</error>
|
</error>
|
||||||
</iq>".parse().unwrap();
|
</iq>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
|
|
@ -357,10 +373,13 @@ mod tests {
|
||||||
IqType::Error(error) => {
|
IqType::Error(error) => {
|
||||||
assert_eq!(error.type_, ErrorType::Cancel);
|
assert_eq!(error.type_, ErrorType::Cancel);
|
||||||
assert_eq!(error.by, None);
|
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.texts.len(), 0);
|
||||||
assert_eq!(error.other, None);
|
assert_eq!(error.other, None);
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -368,9 +387,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_children_invalid() {
|
fn test_children_invalid() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = Iq::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -384,7 +407,9 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[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 {
|
let iq2 = Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,13 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::error::Error;
|
||||||
use crate::ns;
|
|
||||||
use crate::iq::IqSetPayload;
|
use crate::iq::IqSetPayload;
|
||||||
|
use crate::ns;
|
||||||
|
use jid::Jid;
|
||||||
|
use minidom::Element;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// The action attribute.
|
/// The action attribute.
|
||||||
|
|
@ -351,7 +349,8 @@ impl From<Reason> for Element {
|
||||||
Reason::Timeout => "timeout",
|
Reason::Timeout => "timeout",
|
||||||
Reason::UnsupportedApplications => "unsupported-applications",
|
Reason::UnsupportedApplications => "unsupported-applications",
|
||||||
Reason::UnsupportedTransports => "unsupported-transports",
|
Reason::UnsupportedTransports => "unsupported-transports",
|
||||||
}).build()
|
})
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,19 +378,25 @@ impl TryFrom<Element> for ReasonElement {
|
||||||
match child.name() {
|
match child.name() {
|
||||||
"text" => {
|
"text" => {
|
||||||
if text.is_some() {
|
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());
|
text = Some(child.text());
|
||||||
},
|
}
|
||||||
name => {
|
name => {
|
||||||
if reason.is_some() {
|
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()?);
|
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 {
|
Ok(ReasonElement {
|
||||||
reason: reason,
|
reason: reason,
|
||||||
text: text,
|
text: text,
|
||||||
|
|
@ -491,9 +496,9 @@ impl TryFrom<Element> for Jingle {
|
||||||
initiator: get_attr!(root, "initiator", optional),
|
initiator: get_attr!(root, "initiator", optional),
|
||||||
responder: get_attr!(root, "responder", optional),
|
responder: get_attr!(root, "responder", optional),
|
||||||
sid: get_attr!(root, "sid", required),
|
sid: get_attr!(root, "sid", required),
|
||||||
contents: vec!(),
|
contents: vec![],
|
||||||
reason: None,
|
reason: None,
|
||||||
other: vec!(),
|
other: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for child in root.children().cloned() {
|
for child in root.children().cloned() {
|
||||||
|
|
@ -502,7 +507,9 @@ impl TryFrom<Element> for Jingle {
|
||||||
jingle.contents.push(content);
|
jingle.contents.push(content);
|
||||||
} else if child.is("reason", ns::JINGLE) {
|
} else if child.is("reason", ns::JINGLE) {
|
||||||
if jingle.reason.is_some() {
|
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)?;
|
let reason = ReasonElement::try_from(child)?;
|
||||||
jingle.reason = Some(reason);
|
jingle.reason = Some(reason);
|
||||||
|
|
@ -565,7 +572,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let jingle = Jingle::try_from(elem).unwrap();
|
||||||
assert_eq!(jingle.action, Action::SessionInitiate);
|
assert_eq!(jingle.action, Action::SessionInitiate);
|
||||||
assert_eq!(jingle.sid, SessionId(String::from("coucou")));
|
assert_eq!(jingle.sid, SessionId(String::from("coucou")));
|
||||||
|
|
@ -581,7 +591,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'action' missing.");
|
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 error = Jingle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -589,7 +601,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'sid' missing.");
|
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 error = Jingle::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
158
src/jingle_ft.rs
158
src/jingle_ft.rs
|
|
@ -4,19 +4,15 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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 crate::date::DateTime;
|
||||||
|
|
||||||
use minidom::Element;
|
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::hashes::Hash;
|
||||||
|
use crate::jingle::{ContentId, Creator};
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Represents a range in a file.
|
/// Represents a range in a file.
|
||||||
|
|
@ -153,7 +149,7 @@ impl TryFrom<Element> for File {
|
||||||
descs: BTreeMap::new(),
|
descs: BTreeMap::new(),
|
||||||
size: None,
|
size: None,
|
||||||
range: None,
|
range: None,
|
||||||
hashes: vec!(),
|
hashes: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
|
|
@ -164,7 +160,9 @@ impl TryFrom<Element> for File {
|
||||||
file.date = Some(child.text().parse()?);
|
file.date = Some(child.text().parse()?);
|
||||||
} else if child.is("media-type", ns::JINGLE_FT) {
|
} else if child.is("media-type", ns::JINGLE_FT) {
|
||||||
if file.media_type.is_some() {
|
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());
|
file.media_type = Some(child.text());
|
||||||
} else if child.is("name", ns::JINGLE_FT) {
|
} 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 lang = get_attr!(child, "xml:lang", default);
|
||||||
let desc = Desc(child.text());
|
let desc = Desc(child.text());
|
||||||
if file.descs.insert(lang, desc).is_some() {
|
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) {
|
} else if child.is("size", ns::JINGLE_FT) {
|
||||||
if file.size.is_some() {
|
if file.size.is_some() {
|
||||||
|
|
@ -201,38 +201,47 @@ impl TryFrom<Element> for File {
|
||||||
|
|
||||||
impl From<File> for Element {
|
impl From<File> for Element {
|
||||||
fn from(file: File) -> Element {
|
fn from(file: File) -> Element {
|
||||||
let mut root = Element::builder("file")
|
let mut root = Element::builder("file").ns(ns::JINGLE_FT);
|
||||||
.ns(ns::JINGLE_FT);
|
|
||||||
if let Some(date) = file.date {
|
if let Some(date) = file.date {
|
||||||
root = root.append(Element::builder("date")
|
root = root.append(
|
||||||
|
Element::builder("date")
|
||||||
.ns(ns::JINGLE_FT)
|
.ns(ns::JINGLE_FT)
|
||||||
.append(date)
|
.append(date)
|
||||||
.build());
|
.build(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(media_type) = file.media_type {
|
if let Some(media_type) = file.media_type {
|
||||||
root = root.append(Element::builder("media-type")
|
root = root.append(
|
||||||
|
Element::builder("media-type")
|
||||||
.ns(ns::JINGLE_FT)
|
.ns(ns::JINGLE_FT)
|
||||||
.append(media_type)
|
.append(media_type)
|
||||||
.build());
|
.build(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(name) = file.name {
|
if let Some(name) = file.name {
|
||||||
root = root.append(Element::builder("name")
|
root = root.append(
|
||||||
|
Element::builder("name")
|
||||||
.ns(ns::JINGLE_FT)
|
.ns(ns::JINGLE_FT)
|
||||||
.append(name)
|
.append(name)
|
||||||
.build());
|
.build(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (lang, desc) in file.descs.into_iter() {
|
for (lang, desc) in file.descs.into_iter() {
|
||||||
root = root.append(Element::builder("desc")
|
root = root.append(
|
||||||
|
Element::builder("desc")
|
||||||
.ns(ns::JINGLE_FT)
|
.ns(ns::JINGLE_FT)
|
||||||
.attr("xml:lang", lang)
|
.attr("xml:lang", lang)
|
||||||
.append(desc.0)
|
.append(desc.0)
|
||||||
.build());
|
.build(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(size) = file.size {
|
if let Some(size) = file.size {
|
||||||
root = root.append(Element::builder("size")
|
root = root.append(
|
||||||
|
Element::builder("size")
|
||||||
.ns(ns::JINGLE_FT)
|
.ns(ns::JINGLE_FT)
|
||||||
.append(format!("{}", size))
|
.append(format!("{}", size))
|
||||||
.build());
|
.build(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(range) = file.range {
|
if let Some(range) = file.range {
|
||||||
root = root.append(range);
|
root = root.append(range);
|
||||||
|
|
@ -260,12 +269,16 @@ impl TryFrom<Element> for Description {
|
||||||
let mut file = None;
|
let mut file = None;
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if file.is_some() {
|
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())?);
|
file = Some(File::try_from(child.clone())?);
|
||||||
}
|
}
|
||||||
if file.is_none() {
|
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 {
|
Ok(Description {
|
||||||
file: file.unwrap(),
|
file: file.unwrap(),
|
||||||
|
|
@ -304,12 +317,16 @@ impl TryFrom<Element> for Checksum {
|
||||||
let mut file = None;
|
let mut file = None;
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if file.is_some() {
|
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())?);
|
file = Some(File::try_from(child.clone())?);
|
||||||
}
|
}
|
||||||
if file.is_none() {
|
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 {
|
Ok(Checksum {
|
||||||
name: get_attr!(elem, "name", required),
|
name: get_attr!(elem, "name", required),
|
||||||
|
|
@ -381,16 +398,24 @@ mod tests {
|
||||||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||||
</file>
|
</file>
|
||||||
</description>
|
</description>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let desc = Description::try_from(elem).unwrap();
|
let desc = Description::try_from(elem).unwrap();
|
||||||
assert_eq!(desc.file.media_type, Some(String::from("text/plain")));
|
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.name, Some(String::from("test.txt")));
|
||||||
assert_eq!(desc.file.descs, BTreeMap::new());
|
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.size, Some(6144u64));
|
||||||
assert_eq!(desc.file.range, None);
|
assert_eq!(desc.file.range, None);
|
||||||
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
|
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]
|
#[test]
|
||||||
|
|
@ -402,7 +427,9 @@ mod tests {
|
||||||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||||
</file>
|
</file>
|
||||||
</description>
|
</description>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let desc = Description::try_from(elem).unwrap();
|
let desc = Description::try_from(elem).unwrap();
|
||||||
assert_eq!(desc.file.media_type, None);
|
assert_eq!(desc.file.media_type, None);
|
||||||
assert_eq!(desc.file.name, None);
|
assert_eq!(desc.file.name, None);
|
||||||
|
|
@ -411,7 +438,10 @@ mod tests {
|
||||||
assert_eq!(desc.file.size, None);
|
assert_eq!(desc.file.size, None);
|
||||||
assert_eq!(desc.file.range, None);
|
assert_eq!(desc.file.range, None);
|
||||||
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
|
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]
|
#[test]
|
||||||
|
|
@ -426,11 +456,19 @@ mod tests {
|
||||||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||||
</file>
|
</file>
|
||||||
</description>
|
</description>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let desc = Description::try_from(elem).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["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#"
|
let elem: Element = r#"
|
||||||
<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
|
<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
|
||||||
|
|
@ -442,7 +480,9 @@ mod tests {
|
||||||
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
|
||||||
</file>
|
</file>
|
||||||
</description>
|
</description>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Description::try_from(elem).unwrap_err();
|
let error = Description::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -478,7 +518,10 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Unknown attribute in received element.");
|
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 error = Received::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -498,16 +541,31 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_checksum() {
|
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 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();
|
let checksum = Checksum::try_from(elem).unwrap();
|
||||||
assert_eq!(checksum.name, ContentId(String::from("coucou")));
|
assert_eq!(checksum.name, ContentId(String::from("coucou")));
|
||||||
assert_eq!(checksum.creator, Creator::Initiator);
|
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 elem2 = Element::from(checksum);
|
||||||
let checksum2 = Checksum::try_from(elem2).unwrap();
|
let checksum2 = Checksum::try_from(elem2).unwrap();
|
||||||
assert_eq!(checksum2.name, ContentId(String::from("coucou")));
|
assert_eq!(checksum2.name, ContentId(String::from("coucou")));
|
||||||
assert_eq!(checksum2.creator, Creator::Initiator);
|
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 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();
|
let error = Checksum::try_from(elem).unwrap_err();
|
||||||
|
|
@ -544,14 +602,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_range() {
|
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();
|
let range = Range::try_from(elem).unwrap();
|
||||||
assert_eq!(range.offset, 0);
|
assert_eq!(range.offset, 0);
|
||||||
assert_eq!(range.length, None);
|
assert_eq!(range.length, None);
|
||||||
assert_eq!(range.hashes, vec!());
|
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 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();
|
let range = Range::try_from(elem).unwrap();
|
||||||
assert_eq!(range.offset, 2048);
|
assert_eq!(range.offset, 2048);
|
||||||
assert_eq!(range.length, Some(1024));
|
assert_eq!(range.length, Some(1024));
|
||||||
|
|
@ -562,7 +628,9 @@ mod tests {
|
||||||
assert_eq!(range2.length, Some(1024));
|
assert_eq!(range2.length, Some(1024));
|
||||||
assert_eq!(range2.hashes, hashes);
|
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 error = Range::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ attributes: [
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -43,7 +43,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let transport = Transport::try_from(elem).unwrap();
|
||||||
assert_eq!(transport.block_size, 3);
|
assert_eq!(transport.block_size, 3);
|
||||||
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
||||||
|
|
@ -52,7 +55,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = Transport::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -60,15 +65,23 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'block-size' missing.");
|
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 error = Transport::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
_ => panic!(),
|
_ => 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 error = Transport::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
|
|
@ -76,7 +89,10 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message.description(), "invalid digit found in string");
|
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 error = Transport::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,11 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::error::Error;
|
||||||
|
|
||||||
use crate::jingle::SessionId;
|
use crate::jingle::SessionId;
|
||||||
|
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
/// Defines a protocol for broadcasting Jingle requests to all of the clients
|
/// Defines a protocol for broadcasting Jingle requests to all of the clients
|
||||||
/// of a user.
|
/// of a user.
|
||||||
|
|
@ -72,9 +68,11 @@ impl TryFrom<Element> for JingleMI {
|
||||||
}
|
}
|
||||||
JingleMI::Propose {
|
JingleMI::Propose {
|
||||||
sid: get_sid(elem)?,
|
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)?),
|
"retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?),
|
||||||
"accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
|
"accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
|
||||||
"proceed" => JingleMI::Proceed(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 {
|
impl From<JingleMI> for Element {
|
||||||
fn from(jingle_mi: JingleMI) -> Element {
|
fn from(jingle_mi: JingleMI) -> Element {
|
||||||
match jingle_mi {
|
match jingle_mi {
|
||||||
JingleMI::Propose { sid, description } => {
|
JingleMI::Propose { sid, description } => Element::builder("propose")
|
||||||
Element::builder("propose")
|
|
||||||
.ns(ns::JINGLE_MESSAGE)
|
.ns(ns::JINGLE_MESSAGE)
|
||||||
.attr("id", sid)
|
.attr("id", sid)
|
||||||
.append(description)
|
.append(description),
|
||||||
},
|
JingleMI::Retract(sid) => Element::builder("retract")
|
||||||
JingleMI::Retract(sid) => {
|
|
||||||
Element::builder("retract")
|
|
||||||
.ns(ns::JINGLE_MESSAGE)
|
.ns(ns::JINGLE_MESSAGE)
|
||||||
.attr("id", sid)
|
.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),
|
||||||
}
|
}
|
||||||
JingleMI::Accept(sid) => {
|
.build()
|
||||||
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]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
JingleMI::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = JingleMI::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,12 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::error::Error;
|
||||||
|
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use jid::Jid;
|
||||||
|
use minidom::Element;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// The type of the connection being proposed by this candidate.
|
/// The type of the connection being proposed by this candidate.
|
||||||
|
|
@ -187,37 +184,50 @@ impl TryFrom<Element> for Transport {
|
||||||
let mut payload = None;
|
let mut payload = None;
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
payload = Some(if child.is("candidate", ns::JINGLE_S5B) {
|
payload = Some(if child.is("candidate", ns::JINGLE_S5B) {
|
||||||
let mut candidates = match payload {
|
let mut candidates =
|
||||||
|
match payload {
|
||||||
Some(TransportPayload::Candidates(candidates)) => candidates,
|
Some(TransportPayload::Candidates(candidates)) => candidates,
|
||||||
Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")),
|
Some(_) => return Err(Error::ParseError(
|
||||||
None => vec!(),
|
"Non-candidate child already present in JingleS5B transport element.",
|
||||||
|
)),
|
||||||
|
None => vec![],
|
||||||
};
|
};
|
||||||
candidates.push(Candidate::try_from(child.clone())?);
|
candidates.push(Candidate::try_from(child.clone())?);
|
||||||
TransportPayload::Candidates(candidates)
|
TransportPayload::Candidates(candidates)
|
||||||
} else if child.is("activated", ns::JINGLE_S5B) {
|
} else if child.is("activated", ns::JINGLE_S5B) {
|
||||||
if payload.is_some() {
|
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);
|
let cid = get_attr!(child, "cid", required);
|
||||||
TransportPayload::Activated(cid)
|
TransportPayload::Activated(cid)
|
||||||
} else if child.is("candidate-error", ns::JINGLE_S5B) {
|
} else if child.is("candidate-error", ns::JINGLE_S5B) {
|
||||||
if payload.is_some() {
|
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
|
TransportPayload::CandidateError
|
||||||
} else if child.is("candidate-used", ns::JINGLE_S5B) {
|
} else if child.is("candidate-used", ns::JINGLE_S5B) {
|
||||||
if payload.is_some() {
|
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);
|
let cid = get_attr!(child, "cid", required);
|
||||||
TransportPayload::CandidateUsed(cid)
|
TransportPayload::CandidateUsed(cid)
|
||||||
} else if child.is("proxy-error", ns::JINGLE_S5B) {
|
} else if child.is("proxy-error", ns::JINGLE_S5B) {
|
||||||
if payload.is_some() {
|
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
|
TransportPayload::ProxyError
|
||||||
} else {
|
} 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);
|
let payload = payload.unwrap_or(TransportPayload::None);
|
||||||
|
|
@ -238,34 +248,25 @@ impl From<Transport> for Element {
|
||||||
.attr("dstaddr", transport.dstaddr)
|
.attr("dstaddr", transport.dstaddr)
|
||||||
.attr("mode", transport.mode)
|
.attr("mode", transport.mode)
|
||||||
.append(match transport.payload {
|
.append(match transport.payload {
|
||||||
TransportPayload::Candidates(candidates) => {
|
TransportPayload::Candidates(candidates) => candidates
|
||||||
candidates.into_iter()
|
.into_iter()
|
||||||
.map(Element::from)
|
.map(Element::from)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>(),
|
||||||
},
|
TransportPayload::Activated(cid) => vec![Element::builder("activated")
|
||||||
TransportPayload::Activated(cid) => {
|
|
||||||
vec!(Element::builder("activated")
|
|
||||||
.ns(ns::JINGLE_S5B)
|
.ns(ns::JINGLE_S5B)
|
||||||
.attr("cid", cid)
|
.attr("cid", cid)
|
||||||
.build())
|
.build()],
|
||||||
},
|
TransportPayload::CandidateError => vec![Element::builder("candidate-error")
|
||||||
TransportPayload::CandidateError => {
|
|
||||||
vec!(Element::builder("candidate-error")
|
|
||||||
.ns(ns::JINGLE_S5B)
|
.ns(ns::JINGLE_S5B)
|
||||||
.build())
|
.build()],
|
||||||
},
|
TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used")
|
||||||
TransportPayload::CandidateUsed(cid) => {
|
|
||||||
vec!(Element::builder("candidate-used")
|
|
||||||
.ns(ns::JINGLE_S5B)
|
.ns(ns::JINGLE_S5B)
|
||||||
.attr("cid", cid)
|
.attr("cid", cid)
|
||||||
.build())
|
.build()],
|
||||||
},
|
|
||||||
TransportPayload::ProxyError => {
|
TransportPayload::ProxyError => {
|
||||||
vec!(Element::builder("proxy-error")
|
vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()]
|
||||||
.ns(ns::JINGLE_S5B)
|
}
|
||||||
.build())
|
TransportPayload::None => vec![],
|
||||||
},
|
|
||||||
TransportPayload::None => vec!(),
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -274,8 +275,8 @@ impl From<Transport> for Element {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::str::FromStr;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -303,7 +304,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let transport = Transport::try_from(elem).unwrap();
|
||||||
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
assert_eq!(transport.sid, StreamId(String::from("coucou")));
|
||||||
assert_eq!(transport.dstaddr, None);
|
assert_eq!(transport.dstaddr, None);
|
||||||
|
|
@ -334,14 +337,14 @@ mod tests {
|
||||||
sid: StreamId(String::from("coucou")),
|
sid: StreamId(String::from("coucou")),
|
||||||
dstaddr: None,
|
dstaddr: None,
|
||||||
mode: Mode::Tcp,
|
mode: Mode::Tcp,
|
||||||
payload: TransportPayload::Candidates(vec!(Candidate {
|
payload: TransportPayload::Candidates(vec![Candidate {
|
||||||
cid: CandidateId(String::from("coucou")),
|
cid: CandidateId(String::from("coucou")),
|
||||||
host: IpAddr::from_str("127.0.0.1").unwrap(),
|
host: IpAddr::from_str("127.0.0.1").unwrap(),
|
||||||
jid: Jid::from_str("coucou@coucou").unwrap(),
|
jid: Jid::from_str("coucou@coucou").unwrap(),
|
||||||
port: None,
|
port: None,
|
||||||
priority: 0u32,
|
priority: 0u32,
|
||||||
type_: Type::Direct,
|
type_: Type::Direct,
|
||||||
})),
|
}]),
|
||||||
};
|
};
|
||||||
let elem2: Element = transport.into();
|
let elem2: Element = transport.into();
|
||||||
assert!(elem.compare_to(&elem2));
|
assert!(elem.compare_to(&elem2));
|
||||||
|
|
|
||||||
18
src/lib.rs
18
src/lib.rs
|
|
@ -24,15 +24,15 @@
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
extern crate minidom;
|
|
||||||
extern crate jid;
|
|
||||||
extern crate base64;
|
extern crate base64;
|
||||||
|
extern crate blake2;
|
||||||
|
extern crate chrono;
|
||||||
extern crate digest;
|
extern crate digest;
|
||||||
|
extern crate jid;
|
||||||
|
extern crate minidom;
|
||||||
extern crate sha1;
|
extern crate sha1;
|
||||||
extern crate sha2;
|
extern crate sha2;
|
||||||
extern crate sha3;
|
extern crate sha3;
|
||||||
extern crate blake2;
|
|
||||||
extern crate chrono;
|
|
||||||
extern crate try_from;
|
extern crate try_from;
|
||||||
|
|
||||||
pub use minidom::Element;
|
pub use minidom::Element;
|
||||||
|
|
@ -52,20 +52,20 @@ mod macros;
|
||||||
/// Namespace-aware comparison for tests
|
/// Namespace-aware comparison for tests
|
||||||
mod compare_elements;
|
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
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod message;
|
pub mod message;
|
||||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod presence;
|
pub mod presence;
|
||||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod iq;
|
pub mod sasl;
|
||||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod stanza_error;
|
pub mod stanza_error;
|
||||||
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
|
||||||
pub mod stream;
|
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
|
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
|
||||||
pub mod roster;
|
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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
macro_rules! get_attr {
|
macro_rules! get_attr {
|
||||||
($elem:ident, $attr:tt, $type:tt) => (
|
($elem:ident, $attr:tt, $type:tt) => {
|
||||||
get_attr!($elem, $attr, $type, value, value.parse()?)
|
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) {
|
match $elem.attr($attr) {
|
||||||
Some("") => None,
|
Some("") => None,
|
||||||
Some($value) => Some($func),
|
Some($value) => Some($func),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
|
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => {
|
||||||
match $elem.attr($attr) {
|
match $elem.attr($attr) {
|
||||||
Some($value) => Some($func),
|
Some($value) => Some($func),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
|
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => {
|
||||||
match $elem.attr($attr) {
|
match $elem.attr($attr) {
|
||||||
Some($value) => $func,
|
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) {
|
match $elem.attr($attr) {
|
||||||
Some($value) => $func,
|
Some($value) => $func,
|
||||||
None => ::std::default::Default::default(),
|
None => ::std::default::Default::default(),
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_attribute {
|
macro_rules! generate_attribute {
|
||||||
|
|
@ -211,38 +217,54 @@ macro_rules! generate_attribute_enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! check_self {
|
macro_rules! check_self {
|
||||||
($elem:ident, $name:tt, $ns:ident) => (
|
($elem:ident, $name:tt, $ns:ident) => {
|
||||||
check_self!($elem, $name, $ns, $name);
|
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) {
|
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 {
|
macro_rules! check_ns_only {
|
||||||
($elem:ident, $name:tt, $ns:ident) => (
|
($elem:ident, $name:tt, $ns:ident) => {
|
||||||
if !$elem.has_ns(crate::ns::$ns) {
|
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 {
|
macro_rules! check_no_children {
|
||||||
($elem:ident, $name:tt) => (
|
($elem:ident, $name:tt) => {
|
||||||
for _ in $elem.children() {
|
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 {
|
macro_rules! check_no_attributes {
|
||||||
($elem:ident, $name:tt) => (
|
($elem:ident, $name:tt) => {
|
||||||
for _ in $elem.attrs() {
|
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 {
|
macro_rules! check_no_unknown_attributes {
|
||||||
|
|
@ -351,76 +373,95 @@ macro_rules! start_decl {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! start_parse_elem {
|
macro_rules! start_parse_elem {
|
||||||
($temp:ident: Vec) => (
|
($temp:ident: Vec) => {
|
||||||
let mut $temp = Vec::new();
|
let mut $temp = Vec::new();
|
||||||
);
|
};
|
||||||
($temp:ident: Option) => (
|
($temp:ident: Option) => {
|
||||||
let mut $temp = None;
|
let mut $temp = None;
|
||||||
);
|
};
|
||||||
($temp:ident: Required) => (
|
($temp:ident: Required) => {
|
||||||
let mut $temp = None;
|
let mut $temp = None;
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! do_parse {
|
macro_rules! do_parse {
|
||||||
($elem:ident, Element) => (
|
($elem:ident, Element) => {
|
||||||
$elem.clone()
|
$elem.clone()
|
||||||
);
|
};
|
||||||
($elem:ident, String) => (
|
($elem:ident, String) => {
|
||||||
$elem.text()
|
$elem.text()
|
||||||
);
|
};
|
||||||
($elem:ident, $constructor:ident) => (
|
($elem:ident, $constructor:ident) => {
|
||||||
$constructor::try_from($elem.clone())?
|
$constructor::try_from($elem.clone())?
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! do_parse_elem {
|
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.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() {
|
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 = 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() {
|
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 = Some(do_parse!($elem, $constructor));
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! finish_parse_elem {
|
macro_rules! finish_parse_elem {
|
||||||
($temp:ident: Vec = $name:tt, $parent_name:tt) => (
|
($temp:ident: Vec = $name:tt, $parent_name:tt) => {
|
||||||
$temp
|
$temp
|
||||||
);
|
};
|
||||||
($temp:ident: Option = $name:tt, $parent_name:tt) => (
|
($temp:ident: Option = $name:tt, $parent_name:tt) => {
|
||||||
$temp
|
$temp
|
||||||
);
|
};
|
||||||
($temp:ident: Required = $name:tt, $parent_name:tt) => (
|
($temp:ident: Required = $name:tt, $parent_name:tt) => {
|
||||||
$temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))?
|
$temp.ok_or(crate::error::Error::ParseError(concat!(
|
||||||
);
|
"Missing child ",
|
||||||
|
$name,
|
||||||
|
" in ",
|
||||||
|
$parent_name,
|
||||||
|
" element."
|
||||||
|
)))?
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_serialiser {
|
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)
|
::minidom::Element::builder($name)
|
||||||
.ns(crate::ns::$ns)
|
.ns(crate::ns::$ns)
|
||||||
.append($parent.$elem)
|
.append($parent.$elem)
|
||||||
.build()
|
.build()
|
||||||
);
|
};
|
||||||
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
|
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
|
||||||
$parent.$elem.map(|elem|
|
$parent.$elem.map(|elem| {
|
||||||
::minidom::Element::builder($name)
|
::minidom::Element::builder($name)
|
||||||
.ns(crate::ns::$ns)
|
.ns(crate::ns::$ns)
|
||||||
.append(elem)
|
.append(elem)
|
||||||
.build())
|
.build()
|
||||||
);
|
})
|
||||||
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
|
};
|
||||||
|
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||||
$parent.$elem
|
$parent.$elem
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_element {
|
macro_rules! generate_element {
|
||||||
|
|
|
||||||
100
src/mam.rs
100
src/mam.rs
|
|
@ -4,21 +4,17 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::data_forms::DataForm;
|
||||||
use crate::rsm::{SetQuery, SetResult};
|
use crate::error::Error;
|
||||||
use crate::forwarding::Forwarded;
|
use crate::forwarding::Forwarded;
|
||||||
use crate::pubsub::NodeName;
|
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||||
|
use crate::message::MessagePayload;
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use crate::pubsub::NodeName;
|
||||||
|
use crate::rsm::{SetQuery, SetResult};
|
||||||
|
use jid::Jid;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_id!(
|
generate_id!(
|
||||||
/// An identifier matching a result message to the query requesting it.
|
/// An identifier matching a result message to the query requesting it.
|
||||||
|
|
@ -70,7 +66,9 @@ impl MessagePayload for Result_ {}
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// True when the end of a MAM query has been reached.
|
/// True when the end of a MAM query has been reached.
|
||||||
Complete, "complete", bool
|
Complete,
|
||||||
|
"complete",
|
||||||
|
bool
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -133,8 +131,8 @@ impl TryFrom<Element> for Prefs {
|
||||||
fn try_from(elem: Element) -> Result<Prefs, Error> {
|
fn try_from(elem: Element) -> Result<Prefs, Error> {
|
||||||
check_self!(elem, "prefs", MAM);
|
check_self!(elem, "prefs", MAM);
|
||||||
check_no_unknown_attributes!(elem, "prefs", ["default"]);
|
check_no_unknown_attributes!(elem, "prefs", ["default"]);
|
||||||
let mut always = vec!();
|
let mut always = vec![];
|
||||||
let mut never = vec!();
|
let mut never = vec![];
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if child.is("always", ns::MAM) {
|
if child.is("always", ns::MAM) {
|
||||||
for jid_elem in child.children() {
|
for jid_elem in child.children() {
|
||||||
|
|
@ -155,7 +153,11 @@ impl TryFrom<Element> for Prefs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let default_ = get_attr!(elem, "default", required);
|
let default_ = get_attr!(elem, "default", required);
|
||||||
Ok(Prefs { default_, always, never })
|
Ok(Prefs {
|
||||||
|
default_,
|
||||||
|
always,
|
||||||
|
never,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,15 +165,16 @@ fn serialise_jid_list(name: &str, jids: Vec<Jid>) -> Option<Element> {
|
||||||
if jids.is_empty() {
|
if jids.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Element::builder(name)
|
Some(
|
||||||
|
Element::builder(name)
|
||||||
.ns(ns::MAM)
|
.ns(ns::MAM)
|
||||||
.append(jids.into_iter()
|
.append(
|
||||||
.map(|jid| Element::builder("jid")
|
jids.into_iter()
|
||||||
.ns(ns::MAM)
|
.map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build())
|
||||||
.append(jid)
|
.collect::<Vec<_>>(),
|
||||||
.build())
|
)
|
||||||
.collect::<Vec<_>>())
|
.build(),
|
||||||
.build())
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +236,9 @@ mod tests {
|
||||||
</message>
|
</message>
|
||||||
</forwarded>
|
</forwarded>
|
||||||
</result>
|
</result>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = r#"
|
let elem: Element = r#"
|
||||||
<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
|
<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
|
||||||
|
|
@ -257,7 +262,9 @@ mod tests {
|
||||||
<last>09af3-cc343-b409f</last>
|
<last>09af3-cc343-b409f</last>
|
||||||
</set>
|
</set>
|
||||||
</fin>
|
</fin>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Fin::try_from(elem).unwrap();
|
Fin::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +281,9 @@ mod tests {
|
||||||
</field>
|
</field>
|
||||||
</x>
|
</x>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Query::try_from(elem).unwrap();
|
Query::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,13 +303,17 @@ mod tests {
|
||||||
<max>10</max>
|
<max>10</max>
|
||||||
</set>
|
</set>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Query::try_from(elem).unwrap();
|
Query::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prefs_get() {
|
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();
|
let prefs = Prefs::try_from(elem).unwrap();
|
||||||
assert_eq!(prefs.always, vec!());
|
assert_eq!(prefs.always, vec!());
|
||||||
assert_eq!(prefs.never, vec!());
|
assert_eq!(prefs.never, vec!());
|
||||||
|
|
@ -310,7 +323,9 @@ mod tests {
|
||||||
<always/>
|
<always/>
|
||||||
<never/>
|
<never/>
|
||||||
</prefs>
|
</prefs>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let prefs = Prefs::try_from(elem).unwrap();
|
let prefs = Prefs::try_from(elem).unwrap();
|
||||||
assert_eq!(prefs.always, vec!());
|
assert_eq!(prefs.always, vec!());
|
||||||
assert_eq!(prefs.never, vec!());
|
assert_eq!(prefs.never, vec!());
|
||||||
|
|
@ -327,10 +342,18 @@ mod tests {
|
||||||
<jid>montague@montague.lit</jid>
|
<jid>montague@montague.lit</jid>
|
||||||
</never>
|
</never>
|
||||||
</prefs>
|
</prefs>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let prefs = Prefs::try_from(elem).unwrap();
|
let prefs = Prefs::try_from(elem).unwrap();
|
||||||
assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap()));
|
assert_eq!(
|
||||||
assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap()));
|
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());
|
let elem2 = Element::from(prefs.clone());
|
||||||
println!("{:?}", elem2);
|
println!("{:?}", elem2);
|
||||||
|
|
@ -342,7 +365,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Query::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -354,7 +379,12 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
|
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();
|
let elem2 = replace.into();
|
||||||
assert_eq!(elem, elem2);
|
assert_eq!(elem, elem2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,11 @@ generate_element!(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::data_forms::DataForm;
|
use crate::data_forms::DataForm;
|
||||||
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -76,7 +76,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_width_height() {
|
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();
|
let media = MediaElement::try_from(elem).unwrap();
|
||||||
assert_eq!(media.width.unwrap(), 32);
|
assert_eq!(media.width.unwrap(), 32);
|
||||||
assert_eq!(media.height.unwrap(), 32);
|
assert_eq!(media.height.unwrap(), 32);
|
||||||
|
|
@ -93,15 +95,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_width_height() {
|
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 = MediaElement::try_from(elem).unwrap_err();
|
||||||
let error = match error {
|
let error = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
_ => panic!(),
|
_ => 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 = MediaElement::try_from(elem).unwrap_err();
|
||||||
let error = match error {
|
let error = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
|
|
@ -109,15 +118,22 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(error.description(), "invalid digit found in string");
|
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 = MediaElement::try_from(elem).unwrap_err();
|
||||||
let error = match error {
|
let error = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
_ => panic!(),
|
_ => 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 = MediaElement::try_from(elem).unwrap_err();
|
||||||
let error = match error {
|
let error = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
|
|
@ -128,7 +144,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown_child() {
|
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 error = MediaElement::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -139,7 +157,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_uri() {
|
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 error = MediaElement::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -147,7 +168,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'type' missing.");
|
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 error = MediaElement::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -169,17 +192,28 @@ mod tests {
|
||||||
<uri type='audio/mpeg'>
|
<uri type='audio/mpeg'>
|
||||||
http://victim.example.com/challenges/speech.mp3?F3A6292C
|
http://victim.example.com/challenges/speech.mp3?F3A6292C
|
||||||
</uri>
|
</uri>
|
||||||
</media>"#.parse().unwrap();
|
</media>"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let media = MediaElement::try_from(elem).unwrap();
|
let media = MediaElement::try_from(elem).unwrap();
|
||||||
assert!(media.width.is_none());
|
assert!(media.width.is_none());
|
||||||
assert!(media.height.is_none());
|
assert!(media.height.is_none());
|
||||||
assert_eq!(media.uris.len(), 3);
|
assert_eq!(media.uris.len(), 3);
|
||||||
assert_eq!(media.uris[0].type_, "audio/x-wav");
|
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].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].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]
|
#[test]
|
||||||
|
|
@ -200,15 +234,23 @@ mod tests {
|
||||||
</media>
|
</media>
|
||||||
</field>
|
</field>
|
||||||
[ ... ]
|
[ ... ]
|
||||||
</x>"#.parse().unwrap();
|
</x>"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let form = DataForm::try_from(elem).unwrap();
|
let form = DataForm::try_from(elem).unwrap();
|
||||||
assert_eq!(form.fields.len(), 1);
|
assert_eq!(form.fields.len(), 1);
|
||||||
assert_eq!(form.fields[0].var, "ocr");
|
assert_eq!(form.fields[0].var, "ocr");
|
||||||
assert_eq!(form.fields[0].media[0].width, Some(290));
|
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].height, Some(80));
|
||||||
assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
|
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].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"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
102
src/message.rs
102
src/message.rs
|
|
@ -4,16 +4,12 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::error::Error;
|
||||||
|
|
||||||
use crate::ns;
|
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/>`.
|
/// Should be implemented on every known payload of a `<message/>`.
|
||||||
pub trait MessagePayload: TryFrom<Element> + Into<Element> {}
|
pub trait MessagePayload: TryFrom<Element> + Into<Element> {}
|
||||||
|
|
@ -44,18 +40,24 @@ type Lang = String;
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// Represents one `<body/>` element, that is the free form text content of
|
/// Represents one `<body/>` element, that is the free form text content of
|
||||||
/// a message.
|
/// a message.
|
||||||
Body, "body", DEFAULT_NS
|
Body,
|
||||||
|
"body",
|
||||||
|
DEFAULT_NS
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// Defines the subject of a room, or of an email-like normal message.
|
/// Defines the subject of a room, or of an email-like normal message.
|
||||||
Subject, "subject", DEFAULT_NS
|
Subject,
|
||||||
|
"subject",
|
||||||
|
DEFAULT_NS
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// A thread identifier, so that other people can specify to which message
|
/// A thread identifier, so that other people can specify to which message
|
||||||
/// they are replying.
|
/// they are replying.
|
||||||
Thread, "thread", DEFAULT_NS
|
Thread,
|
||||||
|
"thread",
|
||||||
|
DEFAULT_NS
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The main structure representing the `<message/>` stanza.
|
/// The main structure representing the `<message/>` stanza.
|
||||||
|
|
@ -102,11 +104,14 @@ impl Message {
|
||||||
bodies: BTreeMap::new(),
|
bodies: BTreeMap::new(),
|
||||||
subjects: BTreeMap::new(),
|
subjects: BTreeMap::new(),
|
||||||
thread: None,
|
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() {
|
if map.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -156,21 +161,25 @@ impl TryFrom<Element> for Message {
|
||||||
let mut bodies = BTreeMap::new();
|
let mut bodies = BTreeMap::new();
|
||||||
let mut subjects = BTreeMap::new();
|
let mut subjects = BTreeMap::new();
|
||||||
let mut thread = None;
|
let mut thread = None;
|
||||||
let mut payloads = vec!();
|
let mut payloads = vec![];
|
||||||
for elem in root.children() {
|
for elem in root.children() {
|
||||||
if elem.is("body", ns::DEFAULT_NS) {
|
if elem.is("body", ns::DEFAULT_NS) {
|
||||||
check_no_children!(elem, "body");
|
check_no_children!(elem, "body");
|
||||||
let lang = get_attr!(elem, "xml:lang", default);
|
let lang = get_attr!(elem, "xml:lang", default);
|
||||||
let body = Body(elem.text());
|
let body = Body(elem.text());
|
||||||
if bodies.insert(lang, body).is_some() {
|
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) {
|
} else if elem.is("subject", ns::DEFAULT_NS) {
|
||||||
check_no_children!(elem, "subject");
|
check_no_children!(elem, "subject");
|
||||||
let lang = get_attr!(elem, "xml:lang", default);
|
let lang = get_attr!(elem, "xml:lang", default);
|
||||||
let subject = Subject(elem.text());
|
let subject = Subject(elem.text());
|
||||||
if subjects.insert(lang, subject).is_some() {
|
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) {
|
} else if elem.is("thread", ns::DEFAULT_NS) {
|
||||||
if thread.is_some() {
|
if thread.is_some() {
|
||||||
|
|
@ -203,26 +212,40 @@ impl From<Message> for Element {
|
||||||
.attr("to", message.to)
|
.attr("to", message.to)
|
||||||
.attr("id", message.id)
|
.attr("id", message.id)
|
||||||
.attr("type", message.type_)
|
.attr("type", message.type_)
|
||||||
.append(message.subjects.into_iter()
|
.append(
|
||||||
|
message
|
||||||
|
.subjects
|
||||||
|
.into_iter()
|
||||||
.map(|(lang, subject)| {
|
.map(|(lang, subject)| {
|
||||||
let mut subject = Element::from(subject);
|
let mut subject = Element::from(subject);
|
||||||
subject.set_attr("xml:lang", match lang.as_ref() {
|
subject.set_attr(
|
||||||
|
"xml:lang",
|
||||||
|
match lang.as_ref() {
|
||||||
"" => None,
|
"" => None,
|
||||||
lang => Some(lang),
|
lang => Some(lang),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
subject
|
subject
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>(),
|
||||||
.append(message.bodies.into_iter()
|
)
|
||||||
|
.append(
|
||||||
|
message
|
||||||
|
.bodies
|
||||||
|
.into_iter()
|
||||||
.map(|(lang, body)| {
|
.map(|(lang, body)| {
|
||||||
let mut body = Element::from(body);
|
let mut body = Element::from(body);
|
||||||
body.set_attr("xml:lang", match lang.as_ref() {
|
body.set_attr(
|
||||||
|
"xml:lang",
|
||||||
|
match lang.as_ref() {
|
||||||
"" => None,
|
"" => None,
|
||||||
lang => Some(lang),
|
lang => Some(lang),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
body
|
body
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
.append(message.payloads)
|
.append(message.payloads)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -231,8 +254,8 @@ impl From<Message> for Element {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::str::FromStr;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -259,7 +282,9 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[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();
|
let message = Message::try_from(elem).unwrap();
|
||||||
assert_eq!(message.from, None);
|
assert_eq!(message.from, None);
|
||||||
assert_eq!(message.to, None);
|
assert_eq!(message.to, None);
|
||||||
|
|
@ -273,7 +298,9 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[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);
|
let mut message = Message::new(None);
|
||||||
message.type_ = MessageType::Normal;
|
message.type_ = MessageType::Normal;
|
||||||
let elem2 = message.into();
|
let elem2 = message.into();
|
||||||
|
|
@ -291,7 +318,7 @@ mod tests {
|
||||||
assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap());
|
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!(lang, "");
|
||||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +334,9 @@ mod tests {
|
||||||
#[cfg(feature = "component")]
|
#[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 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()));
|
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();
|
let elem2 = message.into();
|
||||||
assert!(elem.compare_to(&elem2));
|
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 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 elem1 = elem.clone();
|
||||||
let message = Message::try_from(elem).unwrap();
|
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!(lang, "");
|
||||||
assert_eq!(subject, &Subject::from_str("Hello world!").unwrap());
|
assert_eq!(subject, &Subject::from_str("Hello world!").unwrap());
|
||||||
}
|
}
|
||||||
|
|
@ -342,28 +374,28 @@ mod tests {
|
||||||
|
|
||||||
// Tests basic feature.
|
// 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!(lang, "fr");
|
||||||
assert_eq!(body, &Body::from_str("Salut le monde !").unwrap());
|
assert_eq!(body, &Body::from_str("Salut le monde !").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests order.
|
// 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!(lang, "de");
|
||||||
assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap());
|
assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests fallback.
|
// 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!(lang, "");
|
||||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests fallback.
|
// 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!(lang, "");
|
||||||
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ impl MessagePayload for Replace {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -39,13 +39,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
Replace::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
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 error = Replace::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -56,7 +60,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = Replace::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -67,7 +73,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_id() {
|
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 error = Replace::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -78,8 +86,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
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'/>"
|
||||||
let replace = Replace { id: String::from("coucou") };
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
let replace = Replace {
|
||||||
|
id: String::from("coucou"),
|
||||||
|
};
|
||||||
let elem2 = replace.into();
|
let elem2 = replace.into();
|
||||||
assert_eq!(elem, elem2);
|
assert_eq!(elem, elem2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/mood.rs
14
src/mood.rs
|
|
@ -263,14 +263,16 @@ generate_element_enum!(
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// Free-form text description of the mood.
|
/// Free-form text description of the mood.
|
||||||
Text, "text", MOOD
|
Text,
|
||||||
|
"text",
|
||||||
|
MOOD
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -288,14 +290,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let mood = MoodEnum::try_from(elem).unwrap();
|
||||||
assert_eq!(mood, MoodEnum::Happy);
|
assert_eq!(mood, MoodEnum::Happy);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_text() {
|
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();
|
let text = Text::try_from(elem).unwrap();
|
||||||
assert_eq!(text.0, String::from("Yay!"));
|
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
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::presence::PresencePayload;
|
|
||||||
use crate::date::DateTime;
|
use crate::date::DateTime;
|
||||||
|
use crate::presence::PresencePayload;
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
/// Represents the query for messages before our join.
|
/// Represents the query for messages before our join.
|
||||||
|
|
@ -102,21 +102,25 @@ impl Muc {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_muc_simple() {
|
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();
|
Muc::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_muc_invalid_child() {
|
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 error = Muc::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -127,7 +131,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_muc_serialise() {
|
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 {
|
let muc = Muc {
|
||||||
password: None,
|
password: None,
|
||||||
history: None,
|
history: None,
|
||||||
|
|
@ -138,7 +144,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_muc_invalid_attribute() {
|
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 error = Muc::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -153,7 +161,8 @@ mod tests {
|
||||||
<x xmlns='http://jabber.org/protocol/muc'>
|
<x xmlns='http://jabber.org/protocol/muc'>
|
||||||
<password>coucou</password>
|
<password>coucou</password>
|
||||||
</x>"
|
</x>"
|
||||||
.parse().unwrap();
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let elem1 = elem.clone();
|
let elem1 = elem.clone();
|
||||||
let muc = Muc::try_from(elem).unwrap();
|
let muc = Muc::try_from(elem).unwrap();
|
||||||
assert_eq!(muc.password, Some("coucou".to_owned()));
|
assert_eq!(muc.password, Some("coucou".to_owned()));
|
||||||
|
|
@ -168,7 +177,8 @@ mod tests {
|
||||||
<x xmlns='http://jabber.org/protocol/muc'>
|
<x xmlns='http://jabber.org/protocol/muc'>
|
||||||
<history maxstanzas='0'/>
|
<history maxstanzas='0'/>
|
||||||
</x>"
|
</x>"
|
||||||
.parse().unwrap();
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let muc = Muc::try_from(elem).unwrap();
|
let muc = Muc::try_from(elem).unwrap();
|
||||||
let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0));
|
let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0));
|
||||||
assert_eq!(muc, muc2);
|
assert_eq!(muc, muc2);
|
||||||
|
|
@ -183,8 +193,12 @@ mod tests {
|
||||||
<x xmlns='http://jabber.org/protocol/muc'>
|
<x xmlns='http://jabber.org/protocol/muc'>
|
||||||
<history since='1970-01-01T00:00:00Z'/>
|
<history since='1970-01-01T00:00:00Z'/>
|
||||||
</x>"
|
</x>"
|
||||||
.parse().unwrap();
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let muc = Muc::try_from(elem).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
|
// 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/.
|
// 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::error::Error;
|
||||||
|
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use jid::Jid;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
generate_attribute_enum!(
|
generate_attribute_enum!(
|
||||||
/// Lists all of the possible status codes used in MUC presences.
|
/// 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);
|
let nick = get_attr!(elem, "nick", optional);
|
||||||
|
|
||||||
match (jid, nick) {
|
match (jid, nick) {
|
||||||
(Some(_), Some(_))
|
(Some(_), Some(_)) | (None, None) => {
|
||||||
| (None, None) =>
|
return Err(Error::ParseError(
|
||||||
return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")),
|
"Either 'jid' or 'nick' attribute is required.",
|
||||||
|
))
|
||||||
|
}
|
||||||
(Some(jid), _) => Ok(Actor::Jid(jid)),
|
(Some(jid), _) => Ok(Actor::Jid(jid)),
|
||||||
(_, Some(nick)) => Ok(Actor::Nick(nick)),
|
(_, Some(nick)) => Ok(Actor::Nick(nick)),
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +117,8 @@ impl From<Actor> for Element {
|
||||||
(match actor {
|
(match actor {
|
||||||
Actor::Jid(jid) => elem.attr("jid", jid),
|
Actor::Jid(jid) => elem.attr("jid", jid),
|
||||||
Actor::Nick(nick) => elem.attr("nick", nick),
|
Actor::Nick(nick) => elem.attr("nick", nick),
|
||||||
}).build()
|
})
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +134,9 @@ generate_element!(
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// A reason for inviting, declining, etc. a request.
|
/// A reason for inviting, declining, etc. a request.
|
||||||
Reason, "reason", MUC_USER
|
Reason,
|
||||||
|
"reason",
|
||||||
|
MUC_USER
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
|
|
@ -237,14 +238,16 @@ generate_element!(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::error::Error as StdError;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
fn test_simple() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
MucUser::try_from(elem).unwrap();
|
MucUser::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,7 +259,9 @@ mod tests {
|
||||||
<status code='102'/>
|
<status code='102'/>
|
||||||
<item affiliation='member' role='moderator'/>
|
<item affiliation='member' role='moderator'/>
|
||||||
</x>
|
</x>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let muc_user = MucUser::try_from(elem).unwrap();
|
let muc_user = MucUser::try_from(elem).unwrap();
|
||||||
assert_eq!(muc_user.status.len(), 2);
|
assert_eq!(muc_user.status.len(), 2);
|
||||||
assert_eq!(muc_user.status[0], Status::AffiliationChange);
|
assert_eq!(muc_user.status[0], Status::AffiliationChange);
|
||||||
|
|
@ -272,7 +277,9 @@ mod tests {
|
||||||
<x xmlns='http://jabber.org/protocol/muc#user'>
|
<x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
<coucou/>
|
<coucou/>
|
||||||
</x>
|
</x>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = MucUser::try_from(elem).unwrap_err();
|
let error = MucUser::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -285,8 +292,13 @@ mod tests {
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<x xmlns='http://jabber.org/protocol/muc#user'/>
|
<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();
|
let elem2 = muc.into();
|
||||||
assert!(elem.compare_to(&elem2));
|
assert!(elem.compare_to(&elem2));
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +307,9 @@ mod tests {
|
||||||
fn test_invalid_attribute() {
|
fn test_invalid_attribute() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
|
<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = MucUser::try_from(elem).unwrap_err();
|
let error = MucUser::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -308,7 +322,9 @@ mod tests {
|
||||||
fn test_status_simple() {
|
fn test_status_simple() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
|
<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Status::try_from(elem).unwrap();
|
Status::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,7 +332,9 @@ mod tests {
|
||||||
fn test_status_invalid() {
|
fn test_status_invalid() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user'/>
|
<status xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -331,7 +349,9 @@ mod tests {
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
|
<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
|
||||||
<foo/>
|
<foo/>
|
||||||
</status>
|
</status>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -344,7 +364,9 @@ mod tests {
|
||||||
fn test_status_simple_code() {
|
fn test_status_simple_code() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>
|
<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let status = Status::try_from(elem).unwrap();
|
let status = Status::try_from(elem).unwrap();
|
||||||
assert_eq!(status, Status::Kicked);
|
assert_eq!(status, Status::Kicked);
|
||||||
}
|
}
|
||||||
|
|
@ -353,7 +375,9 @@ mod tests {
|
||||||
fn test_status_invalid_code() {
|
fn test_status_invalid_code() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>
|
<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -366,7 +390,9 @@ mod tests {
|
||||||
fn test_status_invalid_code2() {
|
fn test_status_invalid_code2() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
|
<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Status::try_from(elem).unwrap_err();
|
let error = Status::try_from(elem).unwrap_err();
|
||||||
let error = match error {
|
let error = match error {
|
||||||
Error::ParseIntError(error) => error,
|
Error::ParseIntError(error) => error,
|
||||||
|
|
@ -379,7 +405,9 @@ mod tests {
|
||||||
fn test_actor_required_attributes() {
|
fn test_actor_required_attributes() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<actor xmlns='http://jabber.org/protocol/muc#user'/>
|
<actor xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Actor::try_from(elem).unwrap_err();
|
let error = Actor::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -394,7 +422,9 @@ mod tests {
|
||||||
<actor xmlns='http://jabber.org/protocol/muc#user'
|
<actor xmlns='http://jabber.org/protocol/muc#user'
|
||||||
jid='foo@bar/baz'
|
jid='foo@bar/baz'
|
||||||
nick='baz'/>
|
nick='baz'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Actor::try_from(elem).unwrap_err();
|
let error = Actor::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -408,7 +438,9 @@ mod tests {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<actor xmlns='http://jabber.org/protocol/muc#user'
|
<actor xmlns='http://jabber.org/protocol/muc#user'
|
||||||
jid='foo@bar/baz'/>
|
jid='foo@bar/baz'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let actor = Actor::try_from(elem).unwrap();
|
let actor = Actor::try_from(elem).unwrap();
|
||||||
let jid = match actor {
|
let jid = match actor {
|
||||||
Actor::Jid(jid) => jid,
|
Actor::Jid(jid) => jid,
|
||||||
|
|
@ -421,7 +453,9 @@ mod tests {
|
||||||
fn test_actor_nick() {
|
fn test_actor_nick() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
|
<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let actor = Actor::try_from(elem).unwrap();
|
let actor = Actor::try_from(elem).unwrap();
|
||||||
let nick = match actor {
|
let nick = match actor {
|
||||||
Actor::Nick(nick) => nick,
|
Actor::Nick(nick) => nick,
|
||||||
|
|
@ -434,7 +468,9 @@ mod tests {
|
||||||
fn test_continue_simple() {
|
fn test_continue_simple() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<continue xmlns='http://jabber.org/protocol/muc#user'/>
|
<continue xmlns='http://jabber.org/protocol/muc#user'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Continue::try_from(elem).unwrap();
|
Continue::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,7 +479,9 @@ mod tests {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<continue xmlns='http://jabber.org/protocol/muc#user'
|
<continue xmlns='http://jabber.org/protocol/muc#user'
|
||||||
thread='foo'/>
|
thread='foo'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let continue_ = Continue::try_from(elem).unwrap();
|
let continue_ = Continue::try_from(elem).unwrap();
|
||||||
assert_eq!(continue_.thread, Some("foo".to_owned()));
|
assert_eq!(continue_.thread, Some("foo".to_owned()));
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +492,9 @@ mod tests {
|
||||||
<continue xmlns='http://jabber.org/protocol/muc#user'>
|
<continue xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
<foobar/>
|
<foobar/>
|
||||||
</continue>
|
</continue>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let continue_ = Continue::try_from(elem).unwrap_err();
|
let continue_ = Continue::try_from(elem).unwrap_err();
|
||||||
let message = match continue_ {
|
let message = match continue_ {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -467,7 +507,8 @@ mod tests {
|
||||||
fn test_reason_simple() {
|
fn test_reason_simple() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
|
<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
|
||||||
.parse().unwrap();
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let reason = Reason::try_from(elem).unwrap();
|
let reason = Reason::try_from(elem).unwrap();
|
||||||
assert_eq!(reason.0, "Reason".to_owned());
|
assert_eq!(reason.0, "Reason".to_owned());
|
||||||
}
|
}
|
||||||
|
|
@ -476,7 +517,9 @@ mod tests {
|
||||||
fn test_reason_invalid_attribute() {
|
fn test_reason_invalid_attribute() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
|
<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Reason::try_from(elem).unwrap_err();
|
let error = Reason::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -491,7 +534,9 @@ mod tests {
|
||||||
<reason xmlns='http://jabber.org/protocol/muc#user'>
|
<reason xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
<foobar/>
|
<foobar/>
|
||||||
</reason>
|
</reason>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Reason::try_from(elem).unwrap_err();
|
let error = Reason::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -501,11 +546,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_invalid_attr(){
|
fn test_item_invalid_attr() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
foo='bar'/>
|
foo='bar'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Item::try_from(elem).unwrap_err();
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -515,21 +562,25 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_affiliation_role_attr(){
|
fn test_item_affiliation_role_attr() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'
|
affiliation='member'
|
||||||
role='moderator'/>
|
role='moderator'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
Item::try_from(elem).unwrap();
|
Item::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_affiliation_role_invalid_attr(){
|
fn test_item_affiliation_role_invalid_attr() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'/>
|
affiliation='member'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Item::try_from(elem).unwrap_err();
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -539,13 +590,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_nick_attr(){
|
fn test_item_nick_attr() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'
|
affiliation='member'
|
||||||
role='moderator'
|
role='moderator'
|
||||||
nick='foobar'/>
|
nick='foobar'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let item = Item::try_from(elem).unwrap();
|
let item = Item::try_from(elem).unwrap();
|
||||||
match item {
|
match item {
|
||||||
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
|
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
|
||||||
|
|
@ -553,65 +606,79 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_affiliation_role_invalid_attr2(){
|
fn test_item_affiliation_role_invalid_attr2() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
role='moderator'/>
|
role='moderator'/>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let error = Item::try_from(elem).unwrap_err();
|
let error = Item::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned());
|
assert_eq!(
|
||||||
|
message,
|
||||||
|
"Required attribute 'affiliation' missing.".to_owned()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_role_actor_child(){
|
fn test_item_role_actor_child() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'
|
affiliation='member'
|
||||||
role='moderator'>
|
role='moderator'>
|
||||||
<actor nick='foobar'/>
|
<actor nick='foobar'/>
|
||||||
</item>
|
</item>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let item = Item::try_from(elem).unwrap();
|
let item = Item::try_from(elem).unwrap();
|
||||||
match item {
|
match item {
|
||||||
Item { actor, .. } =>
|
Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
|
||||||
assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_role_continue_child(){
|
fn test_item_role_continue_child() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'
|
affiliation='member'
|
||||||
role='moderator'>
|
role='moderator'>
|
||||||
<continue thread='foobar'/>
|
<continue thread='foobar'/>
|
||||||
</item>
|
</item>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let item = Item::try_from(elem).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 {
|
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!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_item_role_reason_child(){
|
fn test_item_role_reason_child() {
|
||||||
let elem: Element = "
|
let elem: Element = "
|
||||||
<item xmlns='http://jabber.org/protocol/muc#user'
|
<item xmlns='http://jabber.org/protocol/muc#user'
|
||||||
affiliation='member'
|
affiliation='member'
|
||||||
role='moderator'>
|
role='moderator'>
|
||||||
<reason>foobar</reason>
|
<reason>foobar</reason>
|
||||||
</item>
|
</item>
|
||||||
".parse().unwrap();
|
"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let item = Item::try_from(elem).unwrap();
|
let item = Item::try_from(elem).unwrap();
|
||||||
match item {
|
match item {
|
||||||
Item { reason, .. } =>
|
Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),
|
||||||
assert_eq!(reason, Some(Reason("foobar".to_owned()))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/nick.rs
24
src/nick.rs
|
|
@ -6,15 +6,17 @@
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// Represents a global, memorable, friendly or informal name chosen by a user.
|
/// Represents a global, memorable, friendly or informal name chosen by a user.
|
||||||
Nick, "nick", NICK
|
Nick,
|
||||||
|
"nick",
|
||||||
|
NICK
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -30,7 +32,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let nick = Nick::try_from(elem).unwrap();
|
||||||
assert_eq!(&nick.0, "Link Mauve");
|
assert_eq!(&nick.0, "Link Mauve");
|
||||||
}
|
}
|
||||||
|
|
@ -38,13 +42,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
let elem1 = Element::from(Nick(String::from("Link Mauve")));
|
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);
|
assert_eq!(elem1, elem2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = Nick::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -55,7 +63,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
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 error = Nick::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
12
src/ping.rs
12
src/ping.rs
|
|
@ -10,7 +10,9 @@ use crate::iq::IqGetPayload;
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Represents a ping to the recipient, which must be answered with an
|
/// Represents a ping to the recipient, which must be answered with an
|
||||||
/// empty `<iq/>` or with an error.
|
/// empty `<iq/>` or with an error.
|
||||||
Ping, "ping", PING
|
Ping,
|
||||||
|
"ping",
|
||||||
|
PING
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqGetPayload for Ping {}
|
impl IqGetPayload for Ping {}
|
||||||
|
|
@ -18,9 +20,9 @@ impl IqGetPayload for Ping {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_size() {
|
fn test_size() {
|
||||||
|
|
@ -42,7 +44,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = Ping::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
166
src/presence.rs
166
src/presence.rs
|
|
@ -5,11 +5,11 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::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;
|
use jid::Jid;
|
||||||
|
|
||||||
|
|
@ -75,7 +75,8 @@ impl IntoElements for Show {
|
||||||
Show::Dnd => Some("dnd"),
|
Show::Dnd => Some("dnd"),
|
||||||
Show::Xa => Some("xa"),
|
Show::Xa => Some("xa"),
|
||||||
})
|
})
|
||||||
.build())
|
.build(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,14 +137,19 @@ impl FromStr for Type {
|
||||||
"unsubscribe" => Type::Unsubscribe,
|
"unsubscribe" => Type::Unsubscribe,
|
||||||
"unsubscribed" => Type::Unsubscribed,
|
"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 {
|
impl IntoAttributeValue for Type {
|
||||||
fn into_attribute_value(self) -> Option<String> {
|
fn into_attribute_value(self) -> Option<String> {
|
||||||
Some(match self {
|
Some(
|
||||||
|
match self {
|
||||||
Type::None => return None,
|
Type::None => return None,
|
||||||
|
|
||||||
Type::Error => "error",
|
Type::Error => "error",
|
||||||
|
|
@ -153,7 +159,9 @@ impl IntoAttributeValue for Type {
|
||||||
Type::Unavailable => "unavailable",
|
Type::Unavailable => "unavailable",
|
||||||
Type::Unsubscribe => "unsubscribe",
|
Type::Unsubscribe => "unsubscribe",
|
||||||
Type::Unsubscribed => "unsubscribed",
|
Type::Unsubscribed => "unsubscribed",
|
||||||
}.to_owned())
|
}
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +205,7 @@ impl Presence {
|
||||||
show: Show::None,
|
show: Show::None,
|
||||||
statuses: BTreeMap::new(),
|
statuses: BTreeMap::new(),
|
||||||
priority: 0i8,
|
priority: 0i8,
|
||||||
payloads: vec!(),
|
payloads: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,12 +274,14 @@ impl TryFrom<Element> for Presence {
|
||||||
show: Show::None,
|
show: Show::None,
|
||||||
statuses: BTreeMap::new(),
|
statuses: BTreeMap::new(),
|
||||||
priority: 0i8,
|
priority: 0i8,
|
||||||
payloads: vec!(),
|
payloads: vec![],
|
||||||
};
|
};
|
||||||
for elem in root.children() {
|
for elem in root.children() {
|
||||||
if elem.is("show", ns::DEFAULT_NS) {
|
if elem.is("show", ns::DEFAULT_NS) {
|
||||||
if show.is_some() {
|
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_attributes!(elem, "show");
|
||||||
check_no_children!(elem, "show");
|
check_no_children!(elem, "show");
|
||||||
|
|
@ -281,11 +291,15 @@ impl TryFrom<Element> for Presence {
|
||||||
check_no_children!(elem, "status");
|
check_no_children!(elem, "status");
|
||||||
let lang = get_attr!(elem, "xml:lang", default);
|
let lang = get_attr!(elem, "xml:lang", default);
|
||||||
if presence.statuses.insert(lang, elem.text()).is_some() {
|
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) {
|
} else if elem.is("priority", ns::DEFAULT_NS) {
|
||||||
if priority.is_some() {
|
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_attributes!(elem, "priority");
|
||||||
check_no_children!(elem, "priority");
|
check_no_children!(elem, "priority");
|
||||||
|
|
@ -313,16 +327,29 @@ impl From<Presence> for Element {
|
||||||
.attr("id", presence.id)
|
.attr("id", presence.id)
|
||||||
.attr("type", presence.type_)
|
.attr("type", presence.type_)
|
||||||
.append(presence.show)
|
.append(presence.show)
|
||||||
.append(presence.statuses.into_iter().map(|(lang, status)| {
|
.append(
|
||||||
|
presence
|
||||||
|
.statuses
|
||||||
|
.into_iter()
|
||||||
|
.map(|(lang, status)| {
|
||||||
Element::builder("status")
|
Element::builder("status")
|
||||||
.attr("xml:lang", match lang.as_ref() {
|
.attr(
|
||||||
|
"xml:lang",
|
||||||
|
match lang.as_ref() {
|
||||||
"" => None,
|
"" => None,
|
||||||
lang => Some(lang),
|
lang => Some(lang),
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.append(status)
|
.append(status)
|
||||||
.build()
|
.build()
|
||||||
}).collect::<Vec<_>>())
|
})
|
||||||
.append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) })
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.append(if presence.priority == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format!("{}", presence.priority))
|
||||||
|
})
|
||||||
.append(presence.payloads)
|
.append(presence.payloads)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +381,9 @@ mod tests {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<presence xmlns='jabber:client'/>".parse().unwrap();
|
let elem: Element = "<presence xmlns='jabber:client'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[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();
|
let presence = Presence::try_from(elem).unwrap();
|
||||||
assert_eq!(presence.from, None);
|
assert_eq!(presence.from, None);
|
||||||
assert_eq!(presence.to, None);
|
assert_eq!(presence.to, None);
|
||||||
|
|
@ -366,9 +395,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 presence = Presence::new(Type::Unavailable);
|
||||||
let elem2 = presence.into();
|
let elem2 = presence.into();
|
||||||
assert!(elem.compare_to(&elem2));
|
assert!(elem.compare_to(&elem2));
|
||||||
|
|
@ -377,9 +410,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_show() {
|
fn test_show() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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();
|
let presence = Presence::try_from(elem).unwrap();
|
||||||
assert_eq!(presence.payloads.len(), 0);
|
assert_eq!(presence.payloads.len(), 0);
|
||||||
assert_eq!(presence.show, Show::Chat);
|
assert_eq!(presence.show, Show::Chat);
|
||||||
|
|
@ -388,9 +426,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_missing_show_value() {
|
fn test_missing_show_value() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = Presence::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -403,9 +445,14 @@ mod tests {
|
||||||
fn test_invalid_show() {
|
fn test_invalid_show() {
|
||||||
// "online" used to be a pretty common mistake.
|
// "online" used to be a pretty common mistake.
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = Presence::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -417,9 +464,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_status() {
|
fn test_empty_status() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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();
|
let presence = Presence::try_from(elem).unwrap();
|
||||||
assert_eq!(presence.payloads.len(), 0);
|
assert_eq!(presence.payloads.len(), 0);
|
||||||
assert_eq!(presence.statuses.len(), 1);
|
assert_eq!(presence.statuses.len(), 1);
|
||||||
|
|
@ -429,9 +480,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_status() {
|
fn test_status() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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();
|
let presence = Presence::try_from(elem).unwrap();
|
||||||
assert_eq!(presence.payloads.len(), 0);
|
assert_eq!(presence.payloads.len(), 0);
|
||||||
assert_eq!(presence.statuses.len(), 1);
|
assert_eq!(presence.statuses.len(), 1);
|
||||||
|
|
@ -462,15 +518,23 @@ mod tests {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => 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]
|
#[test]
|
||||||
fn test_priority() {
|
fn test_priority() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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();
|
let presence = Presence::try_from(elem).unwrap();
|
||||||
assert_eq!(presence.payloads.len(), 0);
|
assert_eq!(presence.payloads.len(), 0);
|
||||||
assert_eq!(presence.priority, -1i8);
|
assert_eq!(presence.priority, -1i8);
|
||||||
|
|
@ -479,9 +543,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_priority() {
|
fn test_invalid_priority() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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();
|
let error = Presence::try_from(elem).unwrap_err();
|
||||||
match error {
|
match error {
|
||||||
Error::ParseIntError(_) => (),
|
Error::ParseIntError(_) => (),
|
||||||
|
|
@ -492,9 +561,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown_child() {
|
fn test_unknown_child() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 presence = Presence::try_from(elem).unwrap();
|
||||||
let payload = &presence.payloads[0];
|
let payload = &presence.payloads[0];
|
||||||
assert!(payload.is("test", "invalid"));
|
assert!(payload.is("test", "invalid"));
|
||||||
|
|
@ -503,9 +577,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_status_child() {
|
fn test_invalid_status_child() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = Presence::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -517,9 +596,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
fn test_invalid_attribute() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = Presence::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,14 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::data_forms::DataForm;
|
||||||
|
use crate::date::DateTime;
|
||||||
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
|
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.
|
/// One PubSub item from a node.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -40,7 +35,9 @@ impl TryFrom<Element> for Item {
|
||||||
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
||||||
let payload = payloads.pop();
|
let payload = payloads.pop();
|
||||||
if !payloads.is_empty() {
|
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 {
|
Ok(Item {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -131,21 +128,29 @@ pub enum PubSubEvent {
|
||||||
|
|
||||||
fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
|
fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
|
||||||
let mut is_retract = None;
|
let mut is_retract = None;
|
||||||
let mut items = vec!();
|
let mut items = vec![];
|
||||||
let mut retracts = vec!();
|
let mut retracts = vec![];
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if child.is("item", ns::PUBSUB_EVENT) {
|
if child.is("item", ns::PUBSUB_EVENT) {
|
||||||
match is_retract {
|
match is_retract {
|
||||||
None => is_retract = Some(false),
|
None => is_retract = Some(false),
|
||||||
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())?);
|
items.push(Item::try_from(child.clone())?);
|
||||||
} else if child.is("retract", ns::PUBSUB_EVENT) {
|
} else if child.is("retract", ns::PUBSUB_EVENT) {
|
||||||
match is_retract {
|
match is_retract {
|
||||||
None => is_retract = Some(true),
|
None => is_retract = Some(true),
|
||||||
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_children!(child, "retract");
|
||||||
check_no_unknown_attributes!(child, "retract", ["id"]);
|
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 {
|
Ok(match is_retract {
|
||||||
Some(false) => PubSubEvent::PublishedItems { node, items },
|
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.")),
|
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 mut payloads = child.children().cloned().collect::<Vec<_>>();
|
||||||
let item = payloads.pop();
|
let item = payloads.pop();
|
||||||
if !payloads.is_empty() {
|
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 {
|
let form = match item {
|
||||||
None => None,
|
None => None,
|
||||||
|
|
@ -188,7 +198,9 @@ impl TryFrom<Element> for PubSubEvent {
|
||||||
for item in child.children() {
|
for item in child.children() {
|
||||||
if item.is("redirect", ns::PUBSUB_EVENT) {
|
if item.is("redirect", ns::PUBSUB_EVENT) {
|
||||||
if redirect.is_some() {
|
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);
|
let uri = get_attr!(item, "uri", required);
|
||||||
redirect = Some(uri);
|
redirect = Some(uri);
|
||||||
|
|
@ -222,15 +234,12 @@ impl TryFrom<Element> for PubSubEvent {
|
||||||
impl From<PubSubEvent> for Element {
|
impl From<PubSubEvent> for Element {
|
||||||
fn from(event: PubSubEvent) -> Element {
|
fn from(event: PubSubEvent) -> Element {
|
||||||
let payload = match event {
|
let payload = match event {
|
||||||
PubSubEvent::Configuration { node, form } => {
|
PubSubEvent::Configuration { node, form } => Element::builder("configuration")
|
||||||
Element::builder("configuration")
|
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.append(form)
|
.append(form)
|
||||||
.build()
|
.build(),
|
||||||
},
|
PubSubEvent::Delete { node, redirect } => Element::builder("purge")
|
||||||
PubSubEvent::Delete { node, redirect } => {
|
|
||||||
Element::builder("purge")
|
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.append(redirect.map(|redirect| {
|
.append(redirect.map(|redirect| {
|
||||||
|
|
@ -239,43 +248,45 @@ impl From<PubSubEvent> for Element {
|
||||||
.attr("uri", redirect)
|
.attr("uri", redirect)
|
||||||
.build()
|
.build()
|
||||||
}))
|
}))
|
||||||
.build()
|
.build(),
|
||||||
},
|
PubSubEvent::PublishedItems { node, items } => Element::builder("items")
|
||||||
PubSubEvent::PublishedItems { node, items } => {
|
|
||||||
Element::builder("items")
|
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.append(items)
|
.append(items)
|
||||||
.build()
|
.build(),
|
||||||
},
|
PubSubEvent::RetractedItems { node, items } => Element::builder("items")
|
||||||
PubSubEvent::RetractedItems { node, items } => {
|
|
||||||
Element::builder("items")
|
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.append(items.into_iter().map(|id| {
|
.append(
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|id| {
|
||||||
Element::builder("retract")
|
Element::builder("retract")
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("id", id)
|
.attr("id", id)
|
||||||
.build()
|
.build()
|
||||||
}).collect::<Vec<_>>())
|
})
|
||||||
.build()
|
.collect::<Vec<_>>(),
|
||||||
},
|
)
|
||||||
PubSubEvent::Purge { node } => {
|
.build(),
|
||||||
Element::builder("purge")
|
PubSubEvent::Purge { node } => Element::builder("purge")
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.build()
|
.build(),
|
||||||
},
|
PubSubEvent::Subscription {
|
||||||
PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => {
|
node,
|
||||||
Element::builder("subscription")
|
expiry,
|
||||||
|
jid,
|
||||||
|
subid,
|
||||||
|
subscription,
|
||||||
|
} => Element::builder("subscription")
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
.attr("node", node)
|
.attr("node", node)
|
||||||
.attr("expiry", expiry)
|
.attr("expiry", expiry)
|
||||||
.attr("jid", jid)
|
.attr("jid", jid)
|
||||||
.attr("subid", subid)
|
.attr("subid", subid)
|
||||||
.attr("subscription", subscription)
|
.attr("subscription", subscription)
|
||||||
.build()
|
.build(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
Element::builder("event")
|
Element::builder("event")
|
||||||
.ns(ns::PUBSUB_EVENT)
|
.ns(ns::PUBSUB_EVENT)
|
||||||
|
|
@ -287,12 +298,15 @@ impl From<PubSubEvent> for Element {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::str::FromStr;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_items() {
|
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 error = PubSubEvent::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -309,9 +323,12 @@ mod tests {
|
||||||
PubSubEvent::PublishedItems { node, items } => {
|
PubSubEvent::PublishedItems { node, items } => {
|
||||||
assert_eq!(node, NodeName(String::from("coucou")));
|
assert_eq!(node, NodeName(String::from("coucou")));
|
||||||
assert_eq!(items[0].id, Some(ItemId(String::from("test"))));
|
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);
|
assert_eq!(items[0].payload, None);
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +346,7 @@ mod tests {
|
||||||
Some(ref elem) => assert!(elem.is("foreign", "example:namespace")),
|
Some(ref elem) => assert!(elem.is("foreign", "example:namespace")),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +360,7 @@ mod tests {
|
||||||
assert_eq!(node, NodeName(String::from("something")));
|
assert_eq!(node, NodeName(String::from("something")));
|
||||||
assert_eq!(items[0], ItemId(String::from("coucou")));
|
assert_eq!(items[0], ItemId(String::from("coucou")));
|
||||||
assert_eq!(items[1], ItemId(String::from("test")));
|
assert_eq!(items[1], ItemId(String::from("test")));
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -356,19 +373,22 @@ mod tests {
|
||||||
PubSubEvent::Delete { node, redirect } => {
|
PubSubEvent::Delete { node, redirect } => {
|
||||||
assert_eq!(node, NodeName(String::from("coucou")));
|
assert_eq!(node, NodeName(String::from("coucou")));
|
||||||
assert_eq!(redirect, Some(String::from("hello")));
|
assert_eq!(redirect, Some(String::from("hello")));
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_purge() {
|
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();
|
let event = PubSubEvent::try_from(elem).unwrap();
|
||||||
match event {
|
match event {
|
||||||
PubSubEvent::Purge { node } => {
|
PubSubEvent::Purge { node } => {
|
||||||
assert_eq!(node, NodeName(String::from("coucou")));
|
assert_eq!(node, NodeName(String::from("coucou")));
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -381,14 +401,17 @@ mod tests {
|
||||||
PubSubEvent::Configuration { node, form: _ } => {
|
PubSubEvent::Configuration { node, form: _ } => {
|
||||||
assert_eq!(node, NodeName(String::from("coucou")));
|
assert_eq!(node, NodeName(String::from("coucou")));
|
||||||
//assert_eq!(form.type_, Result_);
|
//assert_eq!(form.type_, Result_);
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = PubSubEvent::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -399,7 +422,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_attribute() {
|
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 error = PubSubEvent::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -419,16 +444,29 @@ mod tests {
|
||||||
subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3'
|
subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3'
|
||||||
subscription='subscribed'/>
|
subscription='subscribed'/>
|
||||||
</event>
|
</event>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let event = PubSubEvent::try_from(elem.clone()).unwrap();
|
let event = PubSubEvent::try_from(elem.clone()).unwrap();
|
||||||
match event.clone() {
|
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!(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!(subscription, Some(Subscription::Subscribed));
|
||||||
assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap()));
|
assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap()));
|
||||||
assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap()));
|
assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap()));
|
||||||
},
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,14 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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::data_forms::DataForm;
|
||||||
|
use crate::error::Error;
|
||||||
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
|
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
|
// TODO: a better solution would be to split this into a query and a result elements, like for
|
||||||
// XEP-0030.
|
// XEP-0030.
|
||||||
|
|
@ -137,7 +132,9 @@ impl TryFrom<Element> for Item {
|
||||||
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
||||||
let payload = payloads.pop();
|
let payload = payloads.pop();
|
||||||
if !payloads.is_empty() {
|
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 {
|
Ok(Item {
|
||||||
payload,
|
payload,
|
||||||
|
|
@ -199,7 +196,9 @@ generate_element!(
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// Whether a retract request should notify subscribers or not.
|
/// Whether a retract request should notify subscribers or not.
|
||||||
Notify, "notify", bool
|
Notify,
|
||||||
|
"notify",
|
||||||
|
bool
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -235,11 +234,15 @@ impl TryFrom<Element> for SubscribeOptions {
|
||||||
for child in elem.children() {
|
for child in elem.children() {
|
||||||
if child.is("required", ns::PUBSUB) {
|
if child.is("required", ns::PUBSUB) {
|
||||||
if required {
|
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;
|
required = true;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::ParseError("Unknown child in subscribe-options element."));
|
return Err(Error::ParseError(
|
||||||
|
"Unknown child in subscribe-options element.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(SubscribeOptions { required })
|
Ok(SubscribeOptions { required })
|
||||||
|
|
@ -251,11 +254,9 @@ impl From<SubscribeOptions> for Element {
|
||||||
Element::builder("subscribe-options")
|
Element::builder("subscribe-options")
|
||||||
.ns(ns::PUBSUB)
|
.ns(ns::PUBSUB)
|
||||||
.append(if subscribe_options.required {
|
.append(if subscribe_options.required {
|
||||||
vec!(Element::builder("required")
|
vec![Element::builder("required").ns(ns::PUBSUB).build()]
|
||||||
.ns(ns::PUBSUB)
|
|
||||||
.build())
|
|
||||||
} else {
|
} else {
|
||||||
vec!()
|
vec![]
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +335,7 @@ pub enum PubSub {
|
||||||
create: Create,
|
create: Create,
|
||||||
|
|
||||||
/// The configure request for the new node.
|
/// The configure request for the new node.
|
||||||
configure: Option<Configure>
|
configure: Option<Configure>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Request to publish items to a node, with optional options.
|
/// Request to publish items to a node, with optional options.
|
||||||
|
|
@ -343,7 +344,7 @@ pub enum PubSub {
|
||||||
publish: Publish,
|
publish: Publish,
|
||||||
|
|
||||||
/// The options related to this publish request.
|
/// 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.
|
/// 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() {
|
for child in elem.children() {
|
||||||
if child.is("create", ns::PUBSUB) {
|
if child.is("create", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
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) {
|
} else if child.is("configure", ns::PUBSUB) {
|
||||||
if let Some(PubSub::Create { create, configure }) = payload {
|
if let Some(PubSub::Create { create, configure }) = payload {
|
||||||
if configure.is_some() {
|
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())?);
|
let configure = Some(Configure::try_from(child.clone())?);
|
||||||
payload = Some(PubSub::Create { create, configure });
|
payload = Some(PubSub::Create { create, configure });
|
||||||
} else {
|
} 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) {
|
} else if child.is("publish", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
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) {
|
} 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() {
|
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())?);
|
let publish_options = Some(PublishOptions::try_from(child.clone())?);
|
||||||
payload = Some(PubSub::Publish { publish, publish_options });
|
payload = Some(PubSub::Publish {
|
||||||
|
publish,
|
||||||
|
publish_options,
|
||||||
|
});
|
||||||
} else {
|
} 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) {
|
} else if child.is("affiliations", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let affiliations = Affiliations::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Affiliations(affiliations));
|
payload = Some(PubSub::Affiliations(affiliations));
|
||||||
} else if child.is("default", ns::PUBSUB) {
|
} else if child.is("default", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let default = Default::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Default(default));
|
payload = Some(PubSub::Default(default));
|
||||||
} else if child.is("items", ns::PUBSUB) {
|
} else if child.is("items", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let items = Items::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Items(items));
|
payload = Some(PubSub::Items(items));
|
||||||
} else if child.is("retract", ns::PUBSUB) {
|
} else if child.is("retract", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let retract = Retract::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Retract(retract));
|
payload = Some(PubSub::Retract(retract));
|
||||||
} else if child.is("subscription", ns::PUBSUB) {
|
} else if child.is("subscription", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let subscription = SubscriptionElem::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Subscription(subscription));
|
payload = Some(PubSub::Subscription(subscription));
|
||||||
} else if child.is("subscriptions", ns::PUBSUB) {
|
} else if child.is("subscriptions", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let subscriptions = Subscriptions::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Subscriptions(subscriptions));
|
payload = Some(PubSub::Subscriptions(subscriptions));
|
||||||
} else if child.is("unsubscribe", ns::PUBSUB) {
|
} else if child.is("unsubscribe", ns::PUBSUB) {
|
||||||
if payload.is_some() {
|
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())?;
|
let unsubscribe = Unsubscribe::try_from(child.clone())?;
|
||||||
payload = Some(PubSub::Unsubscribe(unsubscribe));
|
payload = Some(PubSub::Unsubscribe(unsubscribe));
|
||||||
|
|
@ -469,26 +509,29 @@ impl From<PubSub> for Element {
|
||||||
.ns(ns::PUBSUB)
|
.ns(ns::PUBSUB)
|
||||||
.append(match pubsub {
|
.append(match pubsub {
|
||||||
PubSub::Create { create, configure } => {
|
PubSub::Create { create, configure } => {
|
||||||
let mut elems = vec!(Element::from(create));
|
let mut elems = vec![Element::from(create)];
|
||||||
if let Some(configure) = configure {
|
if let Some(configure) = configure {
|
||||||
elems.push(Element::from(configure));
|
elems.push(Element::from(configure));
|
||||||
}
|
}
|
||||||
elems
|
elems
|
||||||
},
|
}
|
||||||
PubSub::Publish { publish, publish_options } => {
|
PubSub::Publish {
|
||||||
let mut elems = vec!(Element::from(publish));
|
publish,
|
||||||
|
publish_options,
|
||||||
|
} => {
|
||||||
|
let mut elems = vec![Element::from(publish)];
|
||||||
if let Some(publish_options) = publish_options {
|
if let Some(publish_options) = publish_options {
|
||||||
elems.push(Element::from(publish_options));
|
elems.push(Element::from(publish_options));
|
||||||
}
|
}
|
||||||
elems
|
elems
|
||||||
},
|
}
|
||||||
PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)),
|
PubSub::Affiliations(affiliations) => vec![Element::from(affiliations)],
|
||||||
PubSub::Default(default) => vec!(Element::from(default)),
|
PubSub::Default(default) => vec![Element::from(default)],
|
||||||
PubSub::Items(items) => vec!(Element::from(items)),
|
PubSub::Items(items) => vec![Element::from(items)],
|
||||||
PubSub::Retract(retract) => vec!(Element::from(retract)),
|
PubSub::Retract(retract) => vec![Element::from(retract)],
|
||||||
PubSub::Subscription(subscription) => vec!(Element::from(subscription)),
|
PubSub::Subscription(subscription) => vec![Element::from(subscription)],
|
||||||
PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)),
|
PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)],
|
||||||
PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)),
|
PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)],
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -501,7 +544,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
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 elem1 = elem.clone();
|
||||||
let pubsub = PubSub::try_from(elem).unwrap();
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
match pubsub.clone() {
|
match pubsub.clone() {
|
||||||
|
|
@ -515,7 +560,10 @@ mod tests {
|
||||||
let elem2 = Element::from(pubsub);
|
let elem2 = Element::from(pubsub);
|
||||||
assert!(elem1.compare_to(&elem2));
|
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 elem1 = elem.clone();
|
||||||
let pubsub = PubSub::try_from(elem).unwrap();
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
match pubsub.clone() {
|
match pubsub.clone() {
|
||||||
|
|
@ -532,7 +580,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_and_configure() {
|
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 elem1 = elem.clone();
|
||||||
let pubsub = PubSub::try_from(elem).unwrap();
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
match pubsub.clone() {
|
match pubsub.clone() {
|
||||||
|
|
@ -549,11 +600,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn publish() {
|
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 elem1 = elem.clone();
|
||||||
let pubsub = PubSub::try_from(elem).unwrap();
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
match pubsub.clone() {
|
match pubsub.clone() {
|
||||||
PubSub::Publish { publish, publish_options } => {
|
PubSub::Publish {
|
||||||
|
publish,
|
||||||
|
publish_options,
|
||||||
|
} => {
|
||||||
assert_eq!(&publish.node.0, "coucou");
|
assert_eq!(&publish.node.0, "coucou");
|
||||||
assert!(publish_options.is_none());
|
assert!(publish_options.is_none());
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +627,10 @@ mod tests {
|
||||||
let elem1 = elem.clone();
|
let elem1 = elem.clone();
|
||||||
let pubsub = PubSub::try_from(elem).unwrap();
|
let pubsub = PubSub::try_from(elem).unwrap();
|
||||||
match pubsub.clone() {
|
match pubsub.clone() {
|
||||||
PubSub::Publish { publish, publish_options } => {
|
PubSub::Publish {
|
||||||
|
publish,
|
||||||
|
publish_options,
|
||||||
|
} => {
|
||||||
assert_eq!(&publish.node.0, "coucou");
|
assert_eq!(&publish.node.0, "coucou");
|
||||||
assert!(publish_options.unwrap().form.is_none());
|
assert!(publish_options.unwrap().form.is_none());
|
||||||
}
|
}
|
||||||
|
|
@ -583,7 +643,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_empty_pubsub() {
|
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 error = PubSub::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -596,12 +658,17 @@ mod tests {
|
||||||
fn publish_option() {
|
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 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();
|
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]
|
#[test]
|
||||||
fn subscribe_options() {
|
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();
|
let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap();
|
||||||
assert_eq!(subscribe_options1.required, false);
|
assert_eq!(subscribe_options1.required, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ use crate::message::MessagePayload;
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Requests that this message is acked by the final recipient once
|
/// Requests that this message is acked by the final recipient once
|
||||||
/// received.
|
/// received.
|
||||||
Request, "request", RECEIPTS
|
Request,
|
||||||
|
"request",
|
||||||
|
RECEIPTS
|
||||||
);
|
);
|
||||||
|
|
||||||
impl MessagePayload for Request {}
|
impl MessagePayload for Request {}
|
||||||
|
|
@ -29,9 +31,9 @@ impl MessagePayload for Received {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -55,7 +57,9 @@ mod tests {
|
||||||
let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap();
|
let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap();
|
||||||
Received::try_from(elem).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();
|
Received::try_from(elem).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
|
||||||
use jid::Jid;
|
use jid::Jid;
|
||||||
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
|
|
||||||
|
|
||||||
generate_elem_id!(
|
generate_elem_id!(
|
||||||
/// Represents a group a contact is part of.
|
/// Represents a group a contact is part of.
|
||||||
Group, "group", ROSTER
|
Group,
|
||||||
|
"group",
|
||||||
|
ROSTER
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
|
|
@ -79,11 +81,11 @@ impl IqResultPayload for Roster {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
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")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -122,7 +124,9 @@ mod tests {
|
||||||
let roster2 = Roster::try_from(elem2).unwrap();
|
let roster2 = Roster::try_from(elem2).unwrap();
|
||||||
assert_eq!(roster.items, roster2.items);
|
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();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert_eq!(roster.ver, Some(String::from("ver9")));
|
assert_eq!(roster.ver, Some(String::from("ver9")));
|
||||||
assert!(roster.items.is_empty());
|
assert!(roster.items.is_empty());
|
||||||
|
|
@ -141,14 +145,22 @@ mod tests {
|
||||||
name='Benvolio'
|
name='Benvolio'
|
||||||
subscription='both'/>
|
subscription='both'/>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let roster = Roster::try_from(elem).unwrap();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert_eq!(roster.ver, Some(String::from("ver11")));
|
assert_eq!(roster.ver, Some(String::from("ver11")));
|
||||||
assert_eq!(roster.items.len(), 3);
|
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].name, Some(String::from("Romeo")));
|
||||||
assert_eq!(roster.items[0].subscription, Subscription::Both);
|
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]
|
#[test]
|
||||||
|
|
@ -160,12 +172,17 @@ mod tests {
|
||||||
<group>B</group>
|
<group>B</group>
|
||||||
</item>
|
</item>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let elem1 = elem.clone();
|
let elem1 = elem.clone();
|
||||||
let roster = Roster::try_from(elem).unwrap();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert!(roster.ver.is_none());
|
assert!(roster.ver.is_none());
|
||||||
assert_eq!(roster.items.len(), 1);
|
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].name, None);
|
||||||
assert_eq!(roster.items[0].groups.len(), 2);
|
assert_eq!(roster.items[0].groups.len(), 2);
|
||||||
assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
|
assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
|
||||||
|
|
@ -176,7 +193,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set() {
|
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();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert!(roster.ver.is_none());
|
assert!(roster.ver.is_none());
|
||||||
assert_eq!(roster.items.len(), 1);
|
assert_eq!(roster.items.len(), 1);
|
||||||
|
|
@ -188,25 +208,38 @@ mod tests {
|
||||||
<group>Servants</group>
|
<group>Servants</group>
|
||||||
</item>
|
</item>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let roster = Roster::try_from(elem).unwrap();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert!(roster.ver.is_none());
|
assert!(roster.ver.is_none());
|
||||||
assert_eq!(roster.items.len(), 1);
|
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].name, Some(String::from("Nurse")));
|
||||||
assert_eq!(roster.items[0].groups.len(), 1);
|
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#"
|
let elem: Element = r#"
|
||||||
<query xmlns='jabber:iq:roster'>
|
<query xmlns='jabber:iq:roster'>
|
||||||
<item jid='nurse@example.com'
|
<item jid='nurse@example.com'
|
||||||
subscription='remove'/>
|
subscription='remove'/>
|
||||||
</query>
|
</query>
|
||||||
"#.parse().unwrap();
|
"#
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let roster = Roster::try_from(elem).unwrap();
|
let roster = Roster::try_from(elem).unwrap();
|
||||||
assert!(roster.ver.is_none());
|
assert!(roster.ver.is_none());
|
||||||
assert_eq!(roster.items.len(), 1);
|
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].name.is_none());
|
||||||
assert!(roster.items[0].groups.is_empty());
|
assert!(roster.items[0].groups.is_empty());
|
||||||
assert_eq!(roster.items[0].subscription, Subscription::Remove);
|
assert_eq!(roster.items[0].subscription, Subscription::Remove);
|
||||||
|
|
@ -214,7 +247,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid() {
|
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 error = Roster::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -222,7 +257,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Unknown child in query element.");
|
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 error = Roster::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -233,7 +270,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_item() {
|
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 error = Roster::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -251,7 +290,10 @@ mod tests {
|
||||||
assert_eq!(error.description(), "Invalid JID, I guess?");
|
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 error = Roster::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
82
src/rsm.rs
82
src/rsm.rs
|
|
@ -75,10 +75,28 @@ impl From<SetQuery> for Element {
|
||||||
fn from(set: SetQuery) -> Element {
|
fn from(set: SetQuery) -> Element {
|
||||||
Element::builder("set")
|
Element::builder("set")
|
||||||
.ns(ns::RSM)
|
.ns(ns::RSM)
|
||||||
.append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()))
|
.append(set.max.map(|max| {
|
||||||
.append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build()))
|
Element::builder("max")
|
||||||
.append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build()))
|
.ns(ns::RSM)
|
||||||
.append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()))
|
.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()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,17 +156,26 @@ impl TryFrom<Element> for SetResult {
|
||||||
|
|
||||||
impl From<SetResult> for Element {
|
impl From<SetResult> for Element {
|
||||||
fn from(set: SetResult) -> Element {
|
fn from(set: SetResult) -> Element {
|
||||||
let first = set.first.clone()
|
let first = set.first.clone().map(|first| {
|
||||||
.map(|first| Element::builder("first")
|
Element::builder("first")
|
||||||
.ns(ns::RSM)
|
.ns(ns::RSM)
|
||||||
.attr("index", set.first_index)
|
.attr("index", set.first_index)
|
||||||
.append(first)
|
.append(first)
|
||||||
.build());
|
.build()
|
||||||
|
});
|
||||||
Element::builder("set")
|
Element::builder("set")
|
||||||
.ns(ns::RSM)
|
.ns(ns::RSM)
|
||||||
.append(first)
|
.append(first)
|
||||||
.append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build()))
|
.append(
|
||||||
.append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()))
|
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()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -174,14 +201,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let set = SetQuery::try_from(elem).unwrap();
|
||||||
assert_eq!(set.max, None);
|
assert_eq!(set.max, None);
|
||||||
assert_eq!(set.after, None);
|
assert_eq!(set.after, None);
|
||||||
assert_eq!(set.before, None);
|
assert_eq!(set.before, None);
|
||||||
assert_eq!(set.index, 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();
|
let set = SetResult::try_from(elem).unwrap();
|
||||||
match set.first {
|
match set.first {
|
||||||
Some(_) => panic!(),
|
Some(_) => panic!(),
|
||||||
|
|
@ -193,7 +224,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unknown() {
|
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 error = SetQuery::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -201,7 +234,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "This is not a RSM set element.");
|
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 error = SetResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -212,7 +247,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = SetQuery::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -220,7 +257,9 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(message, "Unknown child in set element.");
|
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 error = SetResult::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -231,7 +270,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
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 {
|
let rsm = SetQuery {
|
||||||
max: None,
|
max: None,
|
||||||
after: None,
|
after: None,
|
||||||
|
|
@ -241,7 +282,9 @@ mod tests {
|
||||||
let elem2 = rsm.into();
|
let elem2 = rsm.into();
|
||||||
assert_eq!(elem, elem2);
|
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 {
|
let rsm = SetResult {
|
||||||
first: None,
|
first: None,
|
||||||
first_index: None,
|
first_index: None,
|
||||||
|
|
@ -254,7 +297,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_first_index() {
|
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 elem1 = elem.clone();
|
||||||
let set = SetResult::try_from(elem).unwrap();
|
let set = SetResult::try_from(elem).unwrap();
|
||||||
assert_eq!(set.first, Some(String::from("coucou")));
|
assert_eq!(set.first, Some(String::from("coucou")));
|
||||||
|
|
|
||||||
49
src/sasl.rs
49
src/sasl.rs
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
use crate::helpers::Base64;
|
use crate::helpers::Base64;
|
||||||
|
|
||||||
|
|
@ -84,7 +84,9 @@ generate_element!(
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Sent by the client at any point after [auth](struct.Auth.html) if it
|
/// Sent by the client at any point after [auth](struct.Auth.html) if it
|
||||||
/// wants to cancel the current authentication process.
|
/// wants to cancel the current authentication process.
|
||||||
Abort, "abort", SASL
|
Abort,
|
||||||
|
"abort",
|
||||||
|
SASL
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -166,11 +168,15 @@ impl TryFrom<Element> for Failure {
|
||||||
check_no_children!(child, "text");
|
check_no_children!(child, "text");
|
||||||
let lang = get_attr!(child, "xml:lang", default);
|
let lang = get_attr!(child, "xml:lang", default);
|
||||||
if texts.insert(lang, child.text()).is_some() {
|
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) {
|
} else if child.has_ns(ns::SASL) {
|
||||||
if defined_condition.is_some() {
|
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_attributes!(child, "defined-condition");
|
||||||
check_no_children!(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."));
|
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 {
|
Ok(Failure {
|
||||||
defined_condition: defined_condition,
|
defined_condition: defined_condition,
|
||||||
|
|
@ -198,13 +205,19 @@ impl From<Failure> for Element {
|
||||||
Element::builder("failure")
|
Element::builder("failure")
|
||||||
.ns(ns::SASL)
|
.ns(ns::SASL)
|
||||||
.append(failure.defined_condition)
|
.append(failure.defined_condition)
|
||||||
.append(failure.texts.into_iter().map(|(lang, text)| {
|
.append(
|
||||||
|
failure
|
||||||
|
.texts
|
||||||
|
.into_iter()
|
||||||
|
.map(|(lang, text)| {
|
||||||
Element::builder("text")
|
Element::builder("text")
|
||||||
.ns(ns::SASL)
|
.ns(ns::SASL)
|
||||||
.attr("xml:lang", lang)
|
.attr("xml:lang", lang)
|
||||||
.append(text)
|
.append(text)
|
||||||
.build()
|
.build()
|
||||||
}).collect::<Vec<_>>())
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,8 +225,8 @@ impl From<Failure> for Element {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -243,7 +256,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let auth = Auth::try_from(elem).unwrap();
|
||||||
assert_eq!(auth.mechanism, Mechanism::Plain);
|
assert_eq!(auth.mechanism, Mechanism::Plain);
|
||||||
assert!(auth.data.is_empty());
|
assert!(auth.data.is_empty());
|
||||||
|
|
@ -251,7 +266,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn section_6_5_1() {
|
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();
|
let failure = Failure::try_from(elem).unwrap();
|
||||||
assert_eq!(failure.defined_condition, DefinedCondition::Aborted);
|
assert_eq!(failure.defined_condition, DefinedCondition::Aborted);
|
||||||
assert!(failure.texts.is_empty());
|
assert!(failure.texts.is_empty());
|
||||||
|
|
@ -262,9 +280,14 @@ mod tests {
|
||||||
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
|
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
|
||||||
<account-disabled/>
|
<account-disabled/>
|
||||||
<text xml:lang='en'>Call 212-555-1212 for assistance.</text>
|
<text xml:lang='en'>Call 212-555-1212 for assistance.</text>
|
||||||
</failure>".parse().unwrap();
|
</failure>"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
let failure = Failure::try_from(elem).unwrap();
|
let failure = Failure::try_from(elem).unwrap();
|
||||||
assert_eq!(failure.defined_condition, DefinedCondition::AccountDisabled);
|
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!(
|
generate_attribute!(
|
||||||
/// Whether to allow resumption of a previous stream.
|
/// Whether to allow resumption of a previous stream.
|
||||||
ResumeAttr, "resume", bool
|
ResumeAttr,
|
||||||
|
"resume",
|
||||||
|
bool
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -103,7 +105,9 @@ generate_element!(
|
||||||
|
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Requests the currently received stanzas by the other party.
|
/// Requests the currently received stanzas by the other party.
|
||||||
R, "r", SM
|
R,
|
||||||
|
"r",
|
||||||
|
SM
|
||||||
);
|
);
|
||||||
|
|
||||||
generate_element!(
|
generate_element!(
|
||||||
|
|
@ -135,14 +139,16 @@ generate_element!(
|
||||||
// TODO: add support for optional and required.
|
// TODO: add support for optional and required.
|
||||||
generate_empty_element!(
|
generate_empty_element!(
|
||||||
/// Represents availability of Stream Management in `<stream:features/>`.
|
/// Represents availability of Stream Management in `<stream:features/>`.
|
||||||
StreamManagement, "sm", SM
|
StreamManagement,
|
||||||
|
"sm",
|
||||||
|
SM
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -189,12 +195,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resume() {
|
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();
|
let enable = Enable::try_from(elem).unwrap();
|
||||||
assert_eq!(enable.max, None);
|
assert_eq!(enable.max, None);
|
||||||
assert_eq!(enable.resume, ResumeAttr::True);
|
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 enabled = Enabled::try_from(elem).unwrap();
|
||||||
let previd = enabled.id.unwrap();
|
let previd = enabled.id.unwrap();
|
||||||
assert_eq!(enabled.resume, ResumeAttr::True);
|
assert_eq!(enabled.resume, ResumeAttr::True);
|
||||||
|
|
@ -202,12 +212,16 @@ mod tests {
|
||||||
assert_eq!(enabled.max, Some(600));
|
assert_eq!(enabled.max, Some(600));
|
||||||
assert_eq!(enabled.location, None);
|
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();
|
let resume = Resume::try_from(elem).unwrap();
|
||||||
assert_eq!(resume.h, 5);
|
assert_eq!(resume.h, 5);
|
||||||
assert_eq!(resume.previd, previd);
|
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();
|
let resumed = Resumed::try_from(elem).unwrap();
|
||||||
assert_eq!(resumed.h, 5);
|
assert_eq!(resumed.h, 5);
|
||||||
assert_eq!(resumed.previd, previd);
|
assert_eq!(resumed.previd, previd);
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use try_from::TryFrom;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
|
||||||
use crate::message::MessagePayload;
|
|
||||||
use crate::presence::PresencePayload;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use jid::Jid;
|
use crate::message::MessagePayload;
|
||||||
use crate::ns;
|
use crate::ns;
|
||||||
|
use crate::presence::PresencePayload;
|
||||||
|
use jid::Jid;
|
||||||
|
|
||||||
generate_attribute!(
|
generate_attribute!(
|
||||||
/// The type of the error.
|
/// The type of the error.
|
||||||
|
|
@ -234,23 +234,30 @@ impl TryFrom<Element> for StanzaError {
|
||||||
check_no_children!(child, "text");
|
check_no_children!(child, "text");
|
||||||
let lang = get_attr!(elem, "xml:lang", default);
|
let lang = get_attr!(elem, "xml:lang", default);
|
||||||
if texts.insert(lang, child.text()).is_some() {
|
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) {
|
} else if child.has_ns(ns::XMPP_STANZAS) {
|
||||||
if defined_condition.is_some() {
|
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");
|
check_no_children!(child, "defined-condition");
|
||||||
let condition = DefinedCondition::try_from(child.clone())?;
|
let condition = DefinedCondition::try_from(child.clone())?;
|
||||||
defined_condition = Some(condition);
|
defined_condition = Some(condition);
|
||||||
} else {
|
} else {
|
||||||
if other.is_some() {
|
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());
|
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 {
|
Ok(StanzaError {
|
||||||
type_: type_,
|
type_: type_,
|
||||||
|
|
@ -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 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();
|
let error = StanzaError::try_from(elem).unwrap();
|
||||||
assert_eq!(error.type_, ErrorType::Cancel);
|
assert_eq!(error.type_, ErrorType::Cancel);
|
||||||
assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition);
|
assert_eq!(
|
||||||
|
error.defined_condition,
|
||||||
|
DefinedCondition::UndefinedCondition
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -330,9 +340,13 @@ mod tests {
|
||||||
assert_eq!(message, "Required attribute 'type' missing.");
|
assert_eq!(message, "Required attribute 'type' missing.");
|
||||||
|
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = StanzaError::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -344,9 +358,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_condition() {
|
fn test_invalid_condition() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[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")]
|
#[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 error = StanzaError::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ impl MessagePayload for OriginId {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use minidom::Element;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -58,19 +58,25 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let stanza_id = StanzaId::try_from(elem).unwrap();
|
||||||
assert_eq!(stanza_id.id, String::from("coucou"));
|
assert_eq!(stanza_id.id, String::from("coucou"));
|
||||||
assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap());
|
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();
|
let origin_id = OriginId::try_from(elem).unwrap();
|
||||||
assert_eq!(origin_id.id, String::from("coucou"));
|
assert_eq!(origin_id.id, String::from("coucou"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_child() {
|
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 error = StanzaId::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -92,7 +98,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_by() {
|
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 error = StanzaId::try_from(elem).unwrap_err();
|
||||||
let message = match error {
|
let message = match error {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
|
|
@ -103,8 +111,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
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'/>"
|
||||||
let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() };
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
let stanza_id = StanzaId {
|
||||||
|
id: String::from("coucou"),
|
||||||
|
by: Jid::from_str("coucou@coucou").unwrap(),
|
||||||
|
};
|
||||||
let elem2 = stanza_id.into();
|
let elem2 = stanza_id.into();
|
||||||
assert_eq!(elem, elem2);
|
assert_eq!(elem, elem2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ impl Stream {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ generate_empty_element!(
|
||||||
///
|
///
|
||||||
/// It should only be used in an `<iq type='get'/>`, as it can only
|
/// It should only be used in an `<iq type='get'/>`, as it can only
|
||||||
/// represent the request, and not a result.
|
/// represent the request, and not a result.
|
||||||
VersionQuery, "query", VERSION
|
VersionQuery,
|
||||||
|
"query",
|
||||||
|
VERSION
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IqGetPayload for VersionQuery {}
|
impl IqGetPayload for VersionQuery {}
|
||||||
|
|
@ -39,9 +41,9 @@ impl IqResultPayload for VersionResult {}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
|
||||||
use crate::compare_elements::NamespaceAwareCompare;
|
use crate::compare_elements::NamespaceAwareCompare;
|
||||||
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -59,7 +61,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple() {
|
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();
|
let version = VersionResult::try_from(elem).unwrap();
|
||||||
assert_eq!(version.name, String::from("xmpp-rs"));
|
assert_eq!(version.name, String::from("xmpp-rs"));
|
||||||
assert_eq!(version.version, String::from("0.3.0"));
|
assert_eq!(version.version, String::from("0.3.0"));
|
||||||
|
|
@ -74,7 +79,10 @@ mod tests {
|
||||||
os: None,
|
os: None,
|
||||||
};
|
};
|
||||||
let elem1 = Element::from(version);
|
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);
|
println!("{:?}", elem1);
|
||||||
assert!(elem1.compare_to(&elem2));
|
assert!(elem1.compare_to(&elem2));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ impl Open {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use try_from::TryFrom;
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
use try_from::TryFrom;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -89,7 +89,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple() {
|
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();
|
let open = Open::try_from(elem).unwrap();
|
||||||
assert_eq!(open.from, None);
|
assert_eq!(open.from, None);
|
||||||
assert_eq!(open.to, None);
|
assert_eq!(open.to, None);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue