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/.
|
|
|
|
|
2024-07-24 15:48:42 +00:00
|
|
|
use xso::{text::EmptyAsError, AsXml, FromXml};
|
2017-11-24 04:46:08 +00:00
|
|
|
|
2024-07-24 15:48:42 +00:00
|
|
|
use crate::ns;
|
|
|
|
|
|
|
|
/// Represents an URI used in a media element.
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
|
|
|
#[xml(namespace = ns::MEDIA_ELEMENT, name = "uri")]
|
|
|
|
pub struct Uri {
|
|
|
|
/// The MIME type of the URI referenced.
|
|
|
|
///
|
|
|
|
/// See the [IANA MIME Media Types Registry][1] for a list of
|
|
|
|
/// registered types, but unregistered or yet-to-be-registered are
|
|
|
|
/// accepted too.
|
|
|
|
///
|
|
|
|
/// [1]: <https://www.iana.org/assignments/media-types/media-types.xhtml>
|
|
|
|
#[xml(attribute(name = "type"))]
|
|
|
|
pub type_: String,
|
|
|
|
|
|
|
|
/// The actual URI contained.
|
|
|
|
#[xml(text(codec = EmptyAsError))]
|
|
|
|
pub uri: String,
|
|
|
|
}
|
2017-05-25 00:00:17 +00:00
|
|
|
|
2018-08-02 16:47:06 +00:00
|
|
|
generate_element!(
|
|
|
|
/// References a media element, to be used in [data
|
|
|
|
/// forms](../data_forms/index.html).
|
|
|
|
MediaElement, "media", MEDIA_ELEMENT,
|
2017-11-24 04:48:31 +00:00
|
|
|
attributes: [
|
2018-08-02 16:47:06 +00:00
|
|
|
/// The recommended display width in pixels.
|
2019-02-24 19:26:40 +00:00
|
|
|
width: Option<usize> = "width",
|
2018-08-02 16:47:06 +00:00
|
|
|
|
|
|
|
/// The recommended display height in pixels.
|
2019-02-24 19:26:40 +00:00
|
|
|
height: Option<usize> = "height"
|
2017-11-24 04:48:31 +00:00
|
|
|
],
|
|
|
|
children: [
|
2018-08-02 16:47:06 +00:00
|
|
|
/// A list of URIs referencing this media.
|
2024-07-24 15:48:42 +00:00
|
|
|
uris: Vec<Uri> = ("uri", MEDIA_ELEMENT) => Uri
|
2017-11-24 04:48:31 +00:00
|
|
|
]
|
|
|
|
);
|
2017-05-25 00:00:17 +00:00
|
|
|
|
2017-04-18 19:44:36 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2017-05-06 19:48:41 +00:00
|
|
|
use super::*;
|
2018-12-18 14:27:30 +00:00
|
|
|
use crate::data_forms::DataForm;
|
2024-07-24 18:28:22 +00:00
|
|
|
use minidom::Element;
|
2024-06-21 14:27:43 +00:00
|
|
|
use xso::error::{Error, FromElementError};
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2018-10-28 12:10:48 +00:00
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
|
|
#[test]
|
|
|
|
fn test_size() {
|
2024-07-24 15:48:42 +00:00
|
|
|
assert_size!(Uri, 24);
|
2018-10-28 12:10:48 +00:00
|
|
|
assert_size!(MediaElement, 28);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_pointer_width = "64")]
|
2018-10-26 12:26:16 +00:00
|
|
|
#[test]
|
|
|
|
fn test_size() {
|
2024-07-24 15:48:42 +00:00
|
|
|
assert_size!(Uri, 48);
|
2018-10-26 12:26:16 +00:00
|
|
|
assert_size!(MediaElement, 56);
|
|
|
|
}
|
|
|
|
|
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() {
|
2018-12-18 14:32:05 +00:00
|
|
|
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() {
|
2018-12-18 14:32:05 +00:00
|
|
|
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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::TextParseError(error))
|
|
|
|
if error.is::<std::num::ParseIntError>() =>
|
|
|
|
{
|
|
|
|
error
|
|
|
|
}
|
2017-05-24 21:41:09 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2020-01-21 22:46:00 +00:00
|
|
|
assert_eq!(error.to_string(), "cannot parse integer from empty string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2018-12-18 14:32:05 +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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::TextParseError(error))
|
|
|
|
if error.is::<std::num::ParseIntError>() =>
|
|
|
|
{
|
|
|
|
error
|
|
|
|
}
|
2017-05-24 21:41:09 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2020-01-21 22:46:00 +00:00
|
|
|
assert_eq!(error.to_string(), "invalid digit found in string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2018-12-18 14:32:05 +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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::TextParseError(error))
|
|
|
|
if error.is::<std::num::ParseIntError>() =>
|
|
|
|
{
|
|
|
|
error
|
|
|
|
}
|
2017-05-24 21:41:09 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2020-01-21 22:46:00 +00:00
|
|
|
assert_eq!(error.to_string(), "cannot parse integer from empty string");
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2018-12-18 14:32:05 +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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::TextParseError(error))
|
|
|
|
if error.is::<std::num::ParseIntError>() =>
|
|
|
|
{
|
|
|
|
error
|
|
|
|
}
|
2017-05-24 21:41:09 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2020-01-21 22:46:00 +00:00
|
|
|
assert_eq!(error.to_string(), "invalid digit found in string");
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_unknown_child() {
|
2018-12-18 14:32:05 +00:00
|
|
|
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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::Other(string)) => string,
|
2017-04-18 19:44:36 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
assert_eq!(message, "Unknown child in media element.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bad_uri() {
|
2018-12-18 14:32:05 +00:00
|
|
|
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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::Other(string)) => string,
|
2017-04-18 19:44:36 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2024-07-24 15:48:42 +00:00
|
|
|
assert_eq!(
|
|
|
|
message,
|
|
|
|
"Required attribute field 'type_' on Uri element missing."
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
|
2018-12-18 14:32:05 +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 {
|
2024-06-21 14:27:43 +00:00
|
|
|
FromElementError::Invalid(Error::Other(string)) => string,
|
2017-04-18 19:44:36 +00:00
|
|
|
_ => panic!(),
|
|
|
|
};
|
2024-07-24 15:48:42 +00:00
|
|
|
assert_eq!(message, "Empty text node.");
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_xep_ex1() {
|
2022-03-22 22:29:25 +00:00
|
|
|
let elem: Element = r#"<media xmlns='urn:xmpp:media-element'>
|
2024-07-24 15:48:42 +00:00
|
|
|
<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>
|
2018-12-18 14:32:05 +00:00
|
|
|
</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");
|
2018-12-18 14:32:05 +00:00
|
|
|
assert_eq!(
|
|
|
|
media.uris[0].uri,
|
|
|
|
"http://victim.example.com/challenges/speech.wav?F3A6292C"
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
|
2018-12-18 14:32:05 +00:00
|
|
|
assert_eq!(
|
|
|
|
media.uris[1].uri,
|
|
|
|
"cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
assert_eq!(media.uris[2].type_, "audio/mpeg");
|
2018-12-18 14:32:05 +00:00
|
|
|
assert_eq!(
|
|
|
|
media.uris[2].uri,
|
|
|
|
"http://victim.example.com/challenges/speech.mp3?F3A6292C"
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_xep_ex2() {
|
2022-03-22 22:29:25 +00:00
|
|
|
let elem: Element = r#"<x xmlns='jabber:x:data' type='form'>
|
2017-04-18 19:44:36 +00:00
|
|
|
[ ... ]
|
|
|
|
<field var='ocr'>
|
|
|
|
<media xmlns='urn:xmpp:media-element'
|
|
|
|
height='80'
|
|
|
|
width='290'>
|
2024-07-24 15:48:42 +00:00
|
|
|
<uri type='image/jpeg'>http://www.victim.com/challenges/ocr.jpeg?F3A6292C</uri>
|
|
|
|
<uri type='image/jpeg'>cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org</uri>
|
2017-04-18 19:44:36 +00:00
|
|
|
</media>
|
|
|
|
</field>
|
|
|
|
[ ... ]
|
2018-12-18 14:32:05 +00:00
|
|
|
</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);
|
2023-08-30 13:55:18 +00:00
|
|
|
assert_eq!(form.fields[0].var.as_deref(), Some("ocr"));
|
2017-04-18 19:44:36 +00:00
|
|
|
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");
|
2018-12-18 14:32:05 +00:00
|
|
|
assert_eq!(
|
|
|
|
form.fields[0].media[0].uris[0].uri,
|
|
|
|
"http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
|
2018-12-18 14:32:05 +00:00
|
|
|
assert_eq!(
|
|
|
|
form.fields[0].media[0].uris[1].uri,
|
|
|
|
"cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
|
|
|
|
);
|
2017-04-18 19:44:36 +00:00
|
|
|
}
|
|
|
|
}
|