2017-04-29 21:14:34 +00:00
|
|
|
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// 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/.
|
|
|
|
|
2017-07-20 19:03:15 +00:00
|
|
|
use try_from::TryFrom;
|
2017-05-06 19:48:41 +00:00
|
|
|
|
2017-07-21 00:20:29 +00:00
|
|
|
use minidom::Element;
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
use error::Error;
|
|
|
|
|
2017-04-20 22:16:12 +00:00
|
|
|
use ns;
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2017-04-20 23:41:15 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2017-04-18 19:44:36 +00:00
|
|
|
pub struct URI {
|
|
|
|
pub type_: String,
|
|
|
|
pub uri: String,
|
|
|
|
}
|
|
|
|
|
2017-10-31 22:01:52 +00:00
|
|
|
impl TryFrom<Element> for URI {
|
|
|
|
type Err = Error;
|
|
|
|
|
|
|
|
fn try_from(elem: Element) -> Result<URI, Error> {
|
|
|
|
check_self!(elem, "uri", ns::MEDIA_ELEMENT);
|
|
|
|
check_no_unknown_attributes!(elem, "uri", ["type"]);
|
|
|
|
check_no_children!(elem, "uri");
|
|
|
|
let uri = elem.text().trim().to_owned();
|
|
|
|
if uri == "" {
|
|
|
|
return Err(Error::ParseError("URI missing in uri."));
|
|
|
|
}
|
|
|
|
Ok(URI {
|
|
|
|
type_: get_attr!(elem, "type", required),
|
|
|
|
uri: uri,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 00:00:17 +00:00
|
|
|
impl From<URI> for Element {
|
|
|
|
fn from(uri: URI) -> Element {
|
|
|
|
Element::builder("uri")
|
|
|
|
.ns(ns::MEDIA_ELEMENT)
|
|
|
|
.attr("type", uri.type_)
|
|
|
|
.append(uri.uri)
|
|
|
|
.build()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 23:41:15 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2017-04-18 19:44:36 +00:00
|
|
|
pub struct MediaElement {
|
|
|
|
pub width: Option<usize>,
|
|
|
|
pub height: Option<usize>,
|
|
|
|
pub uris: Vec<URI>,
|
|
|
|
}
|
|
|
|
|
2017-05-23 22:31:33 +00:00
|
|
|
impl TryFrom<Element> for MediaElement {
|
2017-07-20 19:03:15 +00:00
|
|
|
type Err = Error;
|
2017-05-06 19:48:41 +00:00
|
|
|
|
2017-05-23 22:31:33 +00:00
|
|
|
fn try_from(elem: Element) -> Result<MediaElement, Error> {
|
2017-05-06 19:48:41 +00:00
|
|
|
if !elem.is("media", ns::MEDIA_ELEMENT) {
|
|
|
|
return Err(Error::ParseError("This is not a media element."));
|
|
|
|
}
|
2017-04-19 22:41:54 +00:00
|
|
|
|
2017-05-24 21:41:09 +00:00
|
|
|
let mut media = MediaElement {
|
|
|
|
width: get_attr!(elem, "width", optional),
|
|
|
|
height: get_attr!(elem, "height", optional),
|
|
|
|
uris: vec!(),
|
|
|
|
};
|
2017-10-31 22:01:52 +00:00
|
|
|
for child in elem.children() {
|
|
|
|
if child.is("uri", ns::MEDIA_ELEMENT) {
|
|
|
|
media.uris.push(URI::try_from(child.clone())?);
|
2017-05-06 19:48:41 +00:00
|
|
|
} else {
|
|
|
|
return Err(Error::ParseError("Unknown child in media element."));
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-24 21:41:09 +00:00
|
|
|
Ok(media)
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 00:00:17 +00:00
|
|
|
impl From<MediaElement> for Element {
|
|
|
|
fn from(media: MediaElement) -> Element {
|
|
|
|
Element::builder("media")
|
|
|
|
.ns(ns::MEDIA_ELEMENT)
|
|
|
|
.attr("width", media.width)
|
|
|
|
.attr("height", media.height)
|
|
|
|
.append(media.uris)
|
|
|
|
.build()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-18 19:44:36 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2017-05-06 19:48:41 +00:00
|
|
|
use super::*;
|
2017-05-06 19:51:39 +00:00
|
|
|
use data_forms::DataForm;
|
2017-05-24 21:41:09 +00:00
|
|
|
use std::error::Error as StdError;
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_simple() {
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
|
2017-05-23 22:31:33 +00:00
|
|
|
let media = MediaElement::try_from(elem).unwrap();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-23 22:31:33 +00:00
|
|
|
let media = MediaElement::try_from(elem).unwrap();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-23 22:31:33 +00:00
|
|
|
let media = MediaElement::try_from(elem).unwrap();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-24 21:41:09 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
|
|
|
let error = match error {
|
|
|
|
Error::ParseIntError(error) => error,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
assert_eq!(error.description(), "cannot parse integer from empty string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
|
2017-05-24 21:41:09 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
|
|
|
let error = match error {
|
|
|
|
Error::ParseIntError(error) => error,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
assert_eq!(error.description(), "invalid digit found in string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
|
2017-05-24 21:41:09 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
|
|
|
let error = match error {
|
|
|
|
Error::ParseIntError(error) => error,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
assert_eq!(error.description(), "cannot parse integer from empty string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
|
2017-05-24 21:41:09 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
|
|
|
let error = match error {
|
|
|
|
Error::ParseIntError(error) => error,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
assert_eq!(error.description(), "invalid digit found in string");
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_unknown_child() {
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
|
2017-05-23 22:31:33 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-23 22:31:33 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
2017-04-18 19:44:36 +00:00
|
|
|
let message = match error {
|
|
|
|
Error::ParseError(string) => string,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
2017-05-24 21:41:09 +00:00
|
|
|
assert_eq!(message, "Required attribute 'type' missing.");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
|
|
|
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
|
2017-05-23 22:31:33 +00:00
|
|
|
let error = MediaElement::try_from(elem).unwrap_err();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-23 22:31:33 +00:00
|
|
|
let media = MediaElement::try_from(elem).unwrap();
|
2017-04-18 19:44:36 +00:00
|
|
|
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();
|
2017-05-23 22:31:33 +00:00
|
|
|
let form = DataForm::try_from(elem).unwrap();
|
2017-04-18 19:44:36 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|