Hello world!
This commit is contained in:
commit
9cf1521775
9 changed files with 892 additions and 0 deletions
2
.hgignore
Normal file
2
.hgignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
Cargo.lock
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "xmpp-parsers"
|
||||
version = "0.1.0"
|
||||
authors = ["Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>"]
|
||||
|
||||
[dependencies]
|
||||
minidom = "0.1.1"
|
148
src/data_forms.rs
Normal file
148
src/data_forms.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
extern crate minidom;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use error::Error;
|
||||
use ns::{DATA_FORMS_NS, MEDIA_ELEMENT_NS};
|
||||
|
||||
use media_element::{MediaElement, parse_media_element};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Field {
|
||||
pub var: String,
|
||||
pub type_: String, // TODO: use an enum here.
|
||||
pub label: Option<String>,
|
||||
pub values: Vec<String>,
|
||||
pub media: Vec<MediaElement>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DataFormType {
|
||||
Cancel,
|
||||
Form,
|
||||
Result_,
|
||||
Submit,
|
||||
}
|
||||
|
||||
impl FromStr for DataFormType {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<DataFormType, Error> {
|
||||
if s == "cancel" {
|
||||
Ok(DataFormType::Cancel)
|
||||
} else if s == "form" {
|
||||
Ok(DataFormType::Form)
|
||||
} else if s == "result" {
|
||||
Ok(DataFormType::Result_)
|
||||
} else if s == "submit" {
|
||||
Ok(DataFormType::Submit)
|
||||
} else {
|
||||
Err(Error::ParseError("Unknown data form type."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataForm {
|
||||
pub type_: DataFormType,
|
||||
pub form_type: Option<String>,
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
|
||||
pub fn parse_data_form(root: &Element) -> Result<DataForm, Error> {
|
||||
assert!(root.is("x", DATA_FORMS_NS));
|
||||
let type_: DataFormType = match root.attr("type") {
|
||||
Some(type_) => type_.parse()?,
|
||||
None => return Err(Error::ParseError("Type attribute on data form is mandatory.")),
|
||||
};
|
||||
let mut fields = vec!();
|
||||
let mut form_type = None;
|
||||
for field in root.children() {
|
||||
if field.is("field", DATA_FORMS_NS) {
|
||||
let var = field.attr("var").ok_or(Error::ParseError("Field must have a 'var' attribute."))?;
|
||||
let field_type = field.attr("type").unwrap_or("text-single");
|
||||
let label = field.attr("label").and_then(|label| label.parse().ok());
|
||||
let mut values = vec!();
|
||||
let mut media = vec!();
|
||||
for element in field.children() {
|
||||
if element.is("value", DATA_FORMS_NS) {
|
||||
values.push(element.text());
|
||||
} else if element.is("media", MEDIA_ELEMENT_NS) {
|
||||
match parse_media_element(element) {
|
||||
Ok(media_element) => media.push(media_element),
|
||||
Err(_) => (), // TODO: is it really nice to swallow this error?
|
||||
}
|
||||
} else {
|
||||
return Err(Error::ParseError("Field child isn’t a value or media element."));
|
||||
}
|
||||
}
|
||||
if var == "FORM_TYPE" && field_type == "hidden" {
|
||||
if form_type != None {
|
||||
return Err(Error::ParseError("More than one FORM_TYPE in a data form."));
|
||||
}
|
||||
if values.len() != 1 {
|
||||
return Err(Error::ParseError("Wrong number of values in FORM_TYPE."));
|
||||
}
|
||||
form_type = Some(values[0].clone());
|
||||
}
|
||||
fields.push(Field {
|
||||
var: var.to_owned(),
|
||||
type_: field_type.to_owned(),
|
||||
label: label,
|
||||
values: values,
|
||||
media: media,
|
||||
});
|
||||
} else {
|
||||
return Err(Error::ParseError("Unknown field type in data form."));
|
||||
}
|
||||
}
|
||||
Ok(DataForm { type_: type_, form_type: form_type, fields: fields })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use minidom::Element;
|
||||
use error::Error;
|
||||
use data_forms;
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<x xmlns='jabber:x:data' type='result'/>".parse().unwrap();
|
||||
let form = data_forms::parse_data_form(&elem).unwrap();
|
||||
assert_eq!(form.type_, data_forms::DataFormType::Result_);
|
||||
assert!(form.form_type.is_none());
|
||||
assert!(form.fields.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<x xmlns='jabber:x:data'/>".parse().unwrap();
|
||||
let error = data_forms::parse_data_form(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Type attribute on data form is mandatory.");
|
||||
|
||||
let elem: Element = "<x xmlns='jabber:x:data' type='coucou'/>".parse().unwrap();
|
||||
let error = data_forms::parse_data_form(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Unknown data form type.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_child() {
|
||||
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>".parse().unwrap();
|
||||
let error = data_forms::parse_data_form(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Unknown field type in data form.");
|
||||
}
|
||||
}
|
193
src/disco.rs
Normal file
193
src/disco.rs
Normal file
|
@ -0,0 +1,193 @@
|
|||
extern crate minidom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use error::Error;
|
||||
use ns::{DISCO_INFO_NS, DATA_FORMS_NS};
|
||||
|
||||
use data_forms::{DataForm, DataFormType, parse_data_form};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Feature {
|
||||
pub var: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Identity {
|
||||
pub category: String, // TODO: use an enum here.
|
||||
pub type_: String, // TODO: use an enum here.
|
||||
pub xml_lang: String,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Disco {
|
||||
pub node: Option<String>,
|
||||
pub identities: Vec<Identity>,
|
||||
pub features: Vec<Feature>,
|
||||
pub extensions: Vec<DataForm>,
|
||||
}
|
||||
|
||||
pub fn parse_disco(root: &Element) -> Result<Disco, Error> {
|
||||
assert!(root.is("query", DISCO_INFO_NS));
|
||||
let mut identities: Vec<Identity> = vec!();
|
||||
let mut features: Vec<Feature> = vec!();
|
||||
let mut extensions: Vec<DataForm> = vec!();
|
||||
|
||||
let node = root.attr("node")
|
||||
.and_then(|node| node.parse().ok());
|
||||
|
||||
for child in root.children() {
|
||||
if child.is("feature", DISCO_INFO_NS) {
|
||||
let feature = child.attr("var")
|
||||
.ok_or(Error::ParseError("Feature must have a 'var' attribute."))?;
|
||||
features.push(Feature {
|
||||
var: feature.to_owned(),
|
||||
});
|
||||
} else if child.is("identity", DISCO_INFO_NS) {
|
||||
let category = child.attr("category")
|
||||
.ok_or(Error::ParseError("Identity must have a 'category' attribute."))?;
|
||||
if category == "" {
|
||||
return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
|
||||
}
|
||||
|
||||
let type_ = child.attr("type")
|
||||
.ok_or(Error::ParseError("Identity must have a 'type' attribute."))?;
|
||||
if type_ == "" {
|
||||
return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
|
||||
}
|
||||
|
||||
// TODO: this must check for the namespace of the attribute, but minidom doesn’t support that yet, see issue #2.
|
||||
let xml_lang = child.attr("lang").unwrap_or("");
|
||||
let name = child.attr("name")
|
||||
.and_then(|node| node.parse().ok());
|
||||
identities.push(Identity {
|
||||
category: category.to_owned(),
|
||||
type_: type_.to_owned(),
|
||||
xml_lang: xml_lang.to_owned(),
|
||||
name: name,
|
||||
});
|
||||
} else if child.is("x", DATA_FORMS_NS) {
|
||||
let data_form = parse_data_form(child)?;
|
||||
match data_form.type_ {
|
||||
DataFormType::Result_ => (),
|
||||
_ => return Err(Error::ParseError("Data form must have a 'result' type in disco#info.")),
|
||||
}
|
||||
match data_form.form_type {
|
||||
Some(_) => extensions.push(data_form),
|
||||
None => return Err(Error::ParseError("Data form found without a FORM_TYPE.")),
|
||||
}
|
||||
} else {
|
||||
return Err(Error::ParseError("Unknown element in disco#info."));
|
||||
}
|
||||
}
|
||||
|
||||
if identities.is_empty() {
|
||||
return Err(Error::ParseError("There must be at least one identity in disco#info."));
|
||||
}
|
||||
if features.is_empty() {
|
||||
return Err(Error::ParseError("There must be at least one feature in disco#info."));
|
||||
}
|
||||
if !features.contains(&Feature { var: DISCO_INFO_NS.to_owned() }) {
|
||||
return Err(Error::ParseError("disco#info feature not present in disco#info."));
|
||||
}
|
||||
|
||||
return Ok(Disco {
|
||||
node: node,
|
||||
identities: identities,
|
||||
features: features,
|
||||
extensions: extensions
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use minidom::Element;
|
||||
use error::Error;
|
||||
use disco;
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#info'/></query>".parse().unwrap();
|
||||
let query = disco::parse_disco(&elem).unwrap();
|
||||
assert!(query.node.is_none());
|
||||
assert_eq!(query.identities.len(), 1);
|
||||
assert_eq!(query.features.len(), 1);
|
||||
assert!(query.extensions.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Unknown element in disco#info.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "There must be at least one identity in disco#info.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Identity must have a 'category' attribute.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Identity must have a non-empty 'category' attribute.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Identity must have a 'type' attribute.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou' type=''/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Identity must have a non-empty 'type' attribute.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Feature must have a 'var' attribute.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "There must be at least one feature in disco#info.");
|
||||
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#items'/></query>".parse().unwrap();
|
||||
let error = disco::parse_disco(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "disco#info feature not present in disco#info.");
|
||||
}
|
||||
}
|
330
src/ecaps2.rs
Normal file
330
src/ecaps2.rs
Normal file
|
@ -0,0 +1,330 @@
|
|||
extern crate minidom;
|
||||
|
||||
use minidom::Element;
|
||||
|
||||
use error::Error;
|
||||
|
||||
use disco::{Feature, Identity, Disco, parse_disco};
|
||||
use data_forms::DataForm;
|
||||
|
||||
fn compute_item(field: String) -> Vec<u8> {
|
||||
let mut bytes = field.as_bytes().to_vec();
|
||||
bytes.push(0x1f);
|
||||
bytes
|
||||
}
|
||||
|
||||
fn compute_items<T, F: Fn(T) -> Vec<u8>>(things: Vec<T>, separator: u8, encode: F) -> Vec<u8> {
|
||||
let mut string: Vec<u8> = vec!();
|
||||
let mut accumulator: Vec<Vec<u8>> = vec!();
|
||||
for thing in things {
|
||||
let bytes = encode(thing);
|
||||
accumulator.push(bytes);
|
||||
}
|
||||
// This works using the expected i;octet collation.
|
||||
accumulator.sort();
|
||||
for mut bytes in accumulator {
|
||||
string.append(&mut bytes);
|
||||
}
|
||||
string.push(separator);
|
||||
string
|
||||
}
|
||||
|
||||
fn compute_features(features: Vec<Feature>) -> Vec<u8> {
|
||||
compute_items(features, 0x1c, |feature| compute_item(feature.var))
|
||||
}
|
||||
|
||||
fn compute_identities(identities: Vec<Identity>) -> Vec<u8> {
|
||||
compute_items(identities, 0x1c, |identity| {
|
||||
let mut bytes = compute_item(identity.category);
|
||||
bytes.append(&mut compute_item(identity.type_));
|
||||
bytes.append(&mut compute_item(identity.xml_lang));
|
||||
bytes.append(&mut compute_item(identity.name.unwrap_or(String::new())));
|
||||
bytes.push(0x1e);
|
||||
bytes
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_extensions(extensions: Vec<DataForm>) -> Vec<u8> {
|
||||
compute_items(extensions, 0x1c, |extension| {
|
||||
compute_items(extension.fields, 0x1d, |field| {
|
||||
let mut bytes = compute_item(field.var);
|
||||
bytes.append(&mut compute_items(field.values, 0x1e,
|
||||
|value| compute_item(value)));
|
||||
bytes
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_disco(disco: Disco) -> Vec<u8> {
|
||||
let features_string = compute_features(disco.features);
|
||||
let identities_string = compute_identities(disco.identities);
|
||||
let extensions_string = compute_extensions(disco.extensions);
|
||||
|
||||
let mut final_string = vec!();
|
||||
final_string.extend(features_string);
|
||||
final_string.extend(identities_string);
|
||||
final_string.extend(extensions_string);
|
||||
final_string
|
||||
}
|
||||
|
||||
pub fn convert_element(root: &Element) -> Result<Vec<u8>, Error> {
|
||||
let disco = parse_disco(root)?;
|
||||
let final_string = compute_disco(disco);
|
||||
Ok(final_string)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use minidom::Element;
|
||||
use ecaps2;
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#info'/></query>".parse().unwrap();
|
||||
let ecaps2 = ecaps2::convert_element(&elem).unwrap();
|
||||
assert_eq!(ecaps2.len(), 54);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xep_ex1() {
|
||||
let elem: Element = r#"
|
||||
<query xmlns="http://jabber.org/protocol/disco#info">
|
||||
<identity category="client" name="BombusMod" type="mobile"/>
|
||||
<feature var="http://jabber.org/protocol/si"/>
|
||||
<feature var="http://jabber.org/protocol/bytestreams"/>
|
||||
<feature var="http://jabber.org/protocol/chatstates"/>
|
||||
<feature var="http://jabber.org/protocol/disco#info"/>
|
||||
<feature var="http://jabber.org/protocol/disco#items"/>
|
||||
<feature var="urn:xmpp:ping"/>
|
||||
<feature var="jabber:iq:time"/>
|
||||
<feature var="jabber:iq:privacy"/>
|
||||
<feature var="jabber:iq:version"/>
|
||||
<feature var="http://jabber.org/protocol/rosterx"/>
|
||||
<feature var="urn:xmpp:time"/>
|
||||
<feature var="jabber:x:oob"/>
|
||||
<feature var="http://jabber.org/protocol/ibb"/>
|
||||
<feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
|
||||
<feature var="urn:xmpp:receipts"/>
|
||||
<feature var="jabber:iq:roster"/>
|
||||
<feature var="jabber:iq:last"/>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104,
|
||||
97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
|
||||
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
|
||||
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
|
||||
108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||
114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
|
||||
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
|
||||
114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111,
|
||||
102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
|
||||
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98,
|
||||
98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97,
|
||||
98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111,
|
||||
110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
|
||||
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105,
|
||||
112, 116, 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116,
|
||||
105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111,
|
||||
98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111,
|
||||
100, 31, 30, 28, 28];
|
||||
let ecaps2 = ecaps2::convert_element(&elem).unwrap();
|
||||
assert_eq!(ecaps2.len(), 0x1d9);
|
||||
assert_eq!(ecaps2, expected);
|
||||
|
||||
/*
|
||||
let sha_256 = hash(ecaps2, "sha-256");
|
||||
assert_eq!(sha_256, "kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=");
|
||||
let sha3_256 = hash(ecaps2, "sha3-256");
|
||||
assert_eq!(sha3_256, "79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=");
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xep_ex2() {
|
||||
let elem: Element = r#"
|
||||
<query xmlns="http://jabber.org/protocol/disco#info">
|
||||
<identity category="client" name="Tkabber" type="pc" xml:lang="en"/>
|
||||
<identity category="client" name="Ткаббер" type="pc" xml:lang="ru"/>
|
||||
<feature var="games:board"/>
|
||||
<feature var="http://jabber.org/protocol/activity"/>
|
||||
<feature var="http://jabber.org/protocol/activity+notify"/>
|
||||
<feature var="http://jabber.org/protocol/bytestreams"/>
|
||||
<feature var="http://jabber.org/protocol/chatstates"/>
|
||||
<feature var="http://jabber.org/protocol/commands"/>
|
||||
<feature var="http://jabber.org/protocol/disco#info"/>
|
||||
<feature var="http://jabber.org/protocol/disco#items"/>
|
||||
<feature var="http://jabber.org/protocol/evil"/>
|
||||
<feature var="http://jabber.org/protocol/feature-neg"/>
|
||||
<feature var="http://jabber.org/protocol/geoloc"/>
|
||||
<feature var="http://jabber.org/protocol/geoloc+notify"/>
|
||||
<feature var="http://jabber.org/protocol/ibb"/>
|
||||
<feature var="http://jabber.org/protocol/iqibb"/>
|
||||
<feature var="http://jabber.org/protocol/mood"/>
|
||||
<feature var="http://jabber.org/protocol/mood+notify"/>
|
||||
<feature var="http://jabber.org/protocol/rosterx"/>
|
||||
<feature var="http://jabber.org/protocol/si"/>
|
||||
<feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
|
||||
<feature var="http://jabber.org/protocol/tune"/>
|
||||
<feature var="http://www.facebook.com/xmpp/messages"/>
|
||||
<feature var="http://www.xmpp.org/extensions/xep-0084.html#ns-metadata+notify"/>
|
||||
<feature var="jabber:iq:avatar"/>
|
||||
<feature var="jabber:iq:browse"/>
|
||||
<feature var="jabber:iq:dtcp"/>
|
||||
<feature var="jabber:iq:filexfer"/>
|
||||
<feature var="jabber:iq:ibb"/>
|
||||
<feature var="jabber:iq:inband"/>
|
||||
<feature var="jabber:iq:jidlink"/>
|
||||
<feature var="jabber:iq:last"/>
|
||||
<feature var="jabber:iq:oob"/>
|
||||
<feature var="jabber:iq:privacy"/>
|
||||
<feature var="jabber:iq:roster"/>
|
||||
<feature var="jabber:iq:time"/>
|
||||
<feature var="jabber:iq:version"/>
|
||||
<feature var="jabber:x:data"/>
|
||||
<feature var="jabber:x:event"/>
|
||||
<feature var="jabber:x:oob"/>
|
||||
<feature var="urn:xmpp:avatar:metadata+notify"/>
|
||||
<feature var="urn:xmpp:ping"/>
|
||||
<feature var="urn:xmpp:receipts"/>
|
||||
<feature var="urn:xmpp:time"/>
|
||||
<x xmlns="jabber:x:data" type="result">
|
||||
<field type="hidden" var="FORM_TYPE">
|
||||
<value>urn:xmpp:dataforms:softwareinfo</value>
|
||||
</field>
|
||||
<field var="software">
|
||||
<value>Tkabber</value>
|
||||
</field>
|
||||
<field var="software_version">
|
||||
<value>0.11.1-svn-20111216-mod (Tcl/Tk 8.6b2)</value>
|
||||
</field>
|
||||
<field var="os">
|
||||
<value>Windows</value>
|
||||
</field>
|
||||
<field var="os_version">
|
||||
<value>XP</value>
|
||||
</field>
|
||||
</x>
|
||||
</query>
|
||||
"#.parse().unwrap();
|
||||
let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97,
|
||||
99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116,
|
||||
121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114,
|
||||
101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98,
|
||||
98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,
|
||||
111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111,
|
||||
109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106,
|
||||
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
|
||||
111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102,
|
||||
111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
|
||||
100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108,
|
||||
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
|
||||
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102,
|
||||
101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116,
|
||||
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
|
||||
112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111,
|
||||
99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47,
|
||||
103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31,
|
||||
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
|
||||
114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
|
||||
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
|
||||
46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47,
|
||||
105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111,
|
||||
99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58,
|
||||
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111,
|
||||
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
|
||||
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
|
||||
99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
|
||||
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104,
|
||||
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
|
||||
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47,
|
||||
112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116,
|
||||
114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47,
|
||||
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
|
||||
111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116,
|
||||
116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111,
|
||||
111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101,
|
||||
115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47,
|
||||
119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101,
|
||||
120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45,
|
||||
48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101,
|
||||
116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31,
|
||||
106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97,
|
||||
114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111,
|
||||
119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98,
|
||||
101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31,
|
||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31,
|
||||
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106,
|
||||
97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99,
|
||||
121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111,
|
||||
115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
|
||||
116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
|
||||
58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114,
|
||||
58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101,
|
||||
114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109,
|
||||
112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97,
|
||||
100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114,
|
||||
110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114,
|
||||
110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
|
||||
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109,
|
||||
101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101,
|
||||
110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101,
|
||||
110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
|
||||
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79,
|
||||
82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112,
|
||||
112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111,
|
||||
102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115,
|
||||
31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118,
|
||||
101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102,
|
||||
116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
|
||||
115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105,
|
||||
111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50,
|
||||
48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108,
|
||||
47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28];
|
||||
let ecaps2 = ecaps2::convert_element(&elem).unwrap();
|
||||
assert_eq!(ecaps2.len(), 0x543);
|
||||
assert_eq!(ecaps2, expected);
|
||||
|
||||
/*
|
||||
let sha_256 = hash(ecaps2, "sha-256");
|
||||
assert_eq!(sha_256, "u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=");
|
||||
let sha3_256 = hash(ecaps2, "sha3-256");
|
||||
assert_eq!(sha3_256, "XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=");
|
||||
*/
|
||||
}
|
||||
}
|
23
src/error.rs
Normal file
23
src/error.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use std::convert::From;
|
||||
use std::io;
|
||||
|
||||
use minidom;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IoError(io::Error),
|
||||
XMLError(minidom::Error),
|
||||
ParseError(&'static str),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Error {
|
||||
Error::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<minidom::Error> for Error {
|
||||
fn from(err: minidom::Error) -> Error {
|
||||
Error::XMLError(err)
|
||||
}
|
||||
}
|
9
src/lib.rs
Normal file
9
src/lib.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
extern crate minidom;
|
||||
|
||||
pub mod error;
|
||||
pub mod ns;
|
||||
|
||||
pub mod disco;
|
||||
pub mod data_forms;
|
||||
pub mod media_element;
|
||||
pub mod ecaps2;
|
177
src/media_element.rs
Normal file
177
src/media_element.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use minidom::Element;
|
||||
|
||||
use error::Error;
|
||||
|
||||
const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct URI {
|
||||
pub type_: String,
|
||||
pub uri: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MediaElement {
|
||||
pub width: Option<usize>,
|
||||
pub height: Option<usize>,
|
||||
pub uris: Vec<URI>,
|
||||
}
|
||||
|
||||
pub fn parse_media_element(root: &Element) -> Result<MediaElement, Error> {
|
||||
assert!(root.is("media", MEDIA_ELEMENT_NS));
|
||||
let width = root.attr("width").and_then(|width| width.parse().ok());
|
||||
let height = root.attr("height").and_then(|height| height.parse().ok());
|
||||
let mut uris = vec!();
|
||||
for uri in root.children() {
|
||||
if uri.is("uri", MEDIA_ELEMENT_NS) {
|
||||
let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?;
|
||||
let text = uri.text().trim().to_owned();
|
||||
if text == "" {
|
||||
return Err(Error::ParseError("URI missing in uri."));
|
||||
}
|
||||
uris.push(URI { type_: type_.to_owned(), uri: text });
|
||||
} else {
|
||||
return Err(Error::ParseError("Unknown child in media element."));
|
||||
}
|
||||
}
|
||||
Ok(MediaElement { width: width, height: height, uris: uris })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use minidom::Element;
|
||||
use error::Error;
|
||||
use media_element;
|
||||
use data_forms;
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.width.is_none());
|
||||
assert!(media.height.is_none());
|
||||
assert!(media.uris.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_width_height() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert_eq!(media.width.unwrap(), 32);
|
||||
assert_eq!(media.height.unwrap(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert_eq!(media.uris.len(), 1);
|
||||
assert_eq!(media.uris[0].type_, "text/html");
|
||||
assert_eq!(media.uris[0].uri, "https://example.org/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_width_height() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.width.is_none());
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.width.is_none());
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.height.is_none());
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.height.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_child() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
|
||||
let error = media_element::parse_media_element(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Unknown child in media element.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_uri() {
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().unwrap();
|
||||
let error = media_element::parse_media_element(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "Attribute type on uri is mandatory.");
|
||||
|
||||
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
|
||||
let error = media_element::parse_media_element(&elem).unwrap_err();
|
||||
let message = match error {
|
||||
Error::ParseError(string) => string,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(message, "URI missing in uri.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xep_ex1() {
|
||||
let elem: Element = r#"
|
||||
<media xmlns='urn:xmpp:media-element'>
|
||||
<uri type='audio/x-wav'>
|
||||
http://victim.example.com/challenges/speech.wav?F3A6292C
|
||||
</uri>
|
||||
<uri type='audio/ogg; codecs=speex'>
|
||||
cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
|
||||
</uri>
|
||||
<uri type='audio/mpeg'>
|
||||
http://victim.example.com/challenges/speech.mp3?F3A6292C
|
||||
</uri>
|
||||
</media>"#.parse().unwrap();
|
||||
let media = media_element::parse_media_element(&elem).unwrap();
|
||||
assert!(media.width.is_none());
|
||||
assert!(media.height.is_none());
|
||||
assert_eq!(media.uris.len(), 3);
|
||||
assert_eq!(media.uris[0].type_, "audio/x-wav");
|
||||
assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
|
||||
assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
|
||||
assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
|
||||
assert_eq!(media.uris[2].type_, "audio/mpeg");
|
||||
assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xep_ex2() {
|
||||
let elem: Element = r#"
|
||||
<x xmlns='jabber:x:data' type='form'>
|
||||
[ ... ]
|
||||
<field var='ocr'>
|
||||
<media xmlns='urn:xmpp:media-element'
|
||||
height='80'
|
||||
width='290'>
|
||||
<uri type='image/jpeg'>
|
||||
http://www.victim.com/challenges/ocr.jpeg?F3A6292C
|
||||
</uri>
|
||||
<uri type='image/jpeg'>
|
||||
cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
|
||||
</uri>
|
||||
</media>
|
||||
</field>
|
||||
[ ... ]
|
||||
</x>"#.parse().unwrap();
|
||||
let form = data_forms::parse_data_form(&elem).unwrap();
|
||||
assert_eq!(form.fields.len(), 1);
|
||||
assert_eq!(form.fields[0].var, "ocr");
|
||||
assert_eq!(form.fields[0].media[0].width, Some(290));
|
||||
assert_eq!(form.fields[0].media[0].height, Some(80));
|
||||
assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
|
||||
assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
|
||||
assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
|
||||
assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
|
||||
}
|
||||
}
|
3
src/ns.rs
Normal file
3
src/ns.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub const DISCO_INFO_NS: &'static str = "http://jabber.org/protocol/disco#info";
|
||||
pub const DATA_FORMS_NS: &'static str = "jabber:x:data";
|
||||
pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element";
|
Loading…
Reference in a new issue