1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use std::convert::TryFrom;
8
9use minidom::Element;
10
11use error::Error;
12
13use ns;
14
15#[derive(Debug, Clone)]
16pub struct URI {
17 pub type_: String,
18 pub uri: String,
19}
20
21#[derive(Debug, Clone)]
22pub struct MediaElement {
23 pub width: Option<usize>,
24 pub height: Option<usize>,
25 pub uris: Vec<URI>,
26}
27
28impl<'a> TryFrom<&'a Element> for MediaElement {
29 type Error = Error;
30
31 fn try_from(elem: &'a Element) -> Result<MediaElement, Error> {
32 if !elem.is("media", ns::MEDIA_ELEMENT) {
33 return Err(Error::ParseError("This is not a media element."));
34 }
35
36 let width = elem.attr("width").and_then(|width| width.parse().ok());
37 let height = elem.attr("height").and_then(|height| height.parse().ok());
38 let mut uris = vec!();
39 for uri in elem.children() {
40 if uri.is("uri", ns::MEDIA_ELEMENT) {
41 let type_ = uri.attr("type").ok_or(Error::ParseError("Attribute type on uri is mandatory."))?;
42 let text = uri.text().trim().to_owned();
43 if text == "" {
44 return Err(Error::ParseError("URI missing in uri."));
45 }
46 uris.push(URI { type_: type_.to_owned(), uri: text });
47 } else {
48 return Err(Error::ParseError("Unknown child in media element."));
49 }
50 }
51 Ok(MediaElement { width: width, height: height, uris: uris })
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use data_forms::DataForm;
59
60 #[test]
61 fn test_simple() {
62 let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
63 let media = MediaElement::try_from(&elem).unwrap();
64 assert!(media.width.is_none());
65 assert!(media.height.is_none());
66 assert!(media.uris.is_empty());
67 }
68
69 #[test]
70 fn test_width_height() {
71 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>".parse().unwrap();
72 let media = MediaElement::try_from(&elem).unwrap();
73 assert_eq!(media.width.unwrap(), 32);
74 assert_eq!(media.height.unwrap(), 32);
75 }
76
77 #[test]
78 fn test_uri() {
79 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
80 let media = MediaElement::try_from(&elem).unwrap();
81 assert_eq!(media.uris.len(), 1);
82 assert_eq!(media.uris[0].type_, "text/html");
83 assert_eq!(media.uris[0].uri, "https://example.org/");
84 }
85
86 #[test]
87 fn test_invalid_width_height() {
88 let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
89 let media = MediaElement::try_from(&elem).unwrap();
90 assert!(media.width.is_none());
91
92 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
93 let media = MediaElement::try_from(&elem).unwrap();
94 assert!(media.width.is_none());
95
96 let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
97 let media = MediaElement::try_from(&elem).unwrap();
98 assert!(media.height.is_none());
99
100 let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
101 let media = MediaElement::try_from(&elem).unwrap();
102 assert!(media.height.is_none());
103 }
104
105 #[test]
106 fn test_unknown_child() {
107 let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
108 let error = MediaElement::try_from(&elem).unwrap_err();
109 let message = match error {
110 Error::ParseError(string) => string,
111 _ => panic!(),
112 };
113 assert_eq!(message, "Unknown child in media element.");
114 }
115
116 #[test]
117 fn test_bad_uri() {
118 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().unwrap();
119 let error = MediaElement::try_from(&elem).unwrap_err();
120 let message = match error {
121 Error::ParseError(string) => string,
122 _ => panic!(),
123 };
124 assert_eq!(message, "Attribute type on uri is mandatory.");
125
126 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
127 let error = MediaElement::try_from(&elem).unwrap_err();
128 let message = match error {
129 Error::ParseError(string) => string,
130 _ => panic!(),
131 };
132 assert_eq!(message, "URI missing in uri.");
133 }
134
135 #[test]
136 fn test_xep_ex1() {
137 let elem: Element = r#"
138<media xmlns='urn:xmpp:media-element'>
139 <uri type='audio/x-wav'>
140 http://victim.example.com/challenges/speech.wav?F3A6292C
141 </uri>
142 <uri type='audio/ogg; codecs=speex'>
143 cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
144 </uri>
145 <uri type='audio/mpeg'>
146 http://victim.example.com/challenges/speech.mp3?F3A6292C
147 </uri>
148</media>"#.parse().unwrap();
149 let media = MediaElement::try_from(&elem).unwrap();
150 assert!(media.width.is_none());
151 assert!(media.height.is_none());
152 assert_eq!(media.uris.len(), 3);
153 assert_eq!(media.uris[0].type_, "audio/x-wav");
154 assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
155 assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
156 assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
157 assert_eq!(media.uris[2].type_, "audio/mpeg");
158 assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
159 }
160
161 #[test]
162 fn test_xep_ex2() {
163 let elem: Element = r#"
164<x xmlns='jabber:x:data' type='form'>
165 [ ... ]
166 <field var='ocr'>
167 <media xmlns='urn:xmpp:media-element'
168 height='80'
169 width='290'>
170 <uri type='image/jpeg'>
171 http://www.victim.com/challenges/ocr.jpeg?F3A6292C
172 </uri>
173 <uri type='image/jpeg'>
174 cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
175 </uri>
176 </media>
177 </field>
178 [ ... ]
179</x>"#.parse().unwrap();
180 let form = DataForm::try_from(&elem).unwrap();
181 assert_eq!(form.fields.len(), 1);
182 assert_eq!(form.fields[0].var, "ocr");
183 assert_eq!(form.fields[0].media[0].width, Some(290));
184 assert_eq!(form.fields[0].media[0].height, Some(80));
185 assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
186 assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
187 assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
188 assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
189 }
190}