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 crate::util::helpers::TrimmedPlainText;
8
9generate_element!(
10 /// Represents an URI used in a media element.
11 URI, "uri", MEDIA_ELEMENT,
12 attributes: [
13 /// The MIME type of the URI referenced.
14 ///
15 /// See the [IANA MIME Media Types Registry][1] for a list of
16 /// registered types, but unregistered or yet-to-be-registered are
17 /// accepted too.
18 ///
19 /// [1]: https://www.iana.org/assignments/media-types/media-types.xhtml
20 type_: Required<String> = "type"
21 ],
22 text: (
23 /// The actual URI contained.
24 uri: TrimmedPlainText<String>
25 )
26);
27
28generate_element!(
29 /// References a media element, to be used in [data
30 /// forms](../data_forms/index.html).
31 MediaElement, "media", MEDIA_ELEMENT,
32 attributes: [
33 /// The recommended display width in pixels.
34 width: Option<usize> = "width",
35
36 /// The recommended display height in pixels.
37 height: Option<usize> = "height"
38 ],
39 children: [
40 /// A list of URIs referencing this media.
41 uris: Vec<URI> = ("uri", MEDIA_ELEMENT) => URI
42 ]
43);
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::data_forms::DataForm;
49 use crate::util::error::Error;
50 use minidom::Element;
51 use std::error::Error as StdError;
52 use std::convert::TryFrom;
53
54 #[cfg(target_pointer_width = "32")]
55 #[test]
56 fn test_size() {
57 assert_size!(URI, 24);
58 assert_size!(MediaElement, 28);
59 }
60
61 #[cfg(target_pointer_width = "64")]
62 #[test]
63 fn test_size() {
64 assert_size!(URI, 48);
65 assert_size!(MediaElement, 56);
66 }
67
68 #[test]
69 fn test_simple() {
70 let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
71 let media = MediaElement::try_from(elem).unwrap();
72 assert!(media.width.is_none());
73 assert!(media.height.is_none());
74 assert!(media.uris.is_empty());
75 }
76
77 #[test]
78 fn test_width_height() {
79 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>"
80 .parse()
81 .unwrap();
82 let media = MediaElement::try_from(elem).unwrap();
83 assert_eq!(media.width.unwrap(), 32);
84 assert_eq!(media.height.unwrap(), 32);
85 }
86
87 #[test]
88 fn test_uri() {
89 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
90 let media = MediaElement::try_from(elem).unwrap();
91 assert_eq!(media.uris.len(), 1);
92 assert_eq!(media.uris[0].type_, "text/html");
93 assert_eq!(media.uris[0].uri, "https://example.org/");
94 }
95
96 #[test]
97 fn test_invalid_width_height() {
98 let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>"
99 .parse()
100 .unwrap();
101 let error = MediaElement::try_from(elem).unwrap_err();
102 let error = match error {
103 Error::ParseIntError(error) => error,
104 _ => panic!(),
105 };
106 assert_eq!(
107 error.description(),
108 "cannot parse integer from empty string"
109 );
110
111 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>"
112 .parse()
113 .unwrap();
114 let error = MediaElement::try_from(elem).unwrap_err();
115 let error = match error {
116 Error::ParseIntError(error) => error,
117 _ => panic!(),
118 };
119 assert_eq!(error.description(), "invalid digit found in string");
120
121 let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>"
122 .parse()
123 .unwrap();
124 let error = MediaElement::try_from(elem).unwrap_err();
125 let error = match error {
126 Error::ParseIntError(error) => error,
127 _ => panic!(),
128 };
129 assert_eq!(
130 error.description(),
131 "cannot parse integer from empty string"
132 );
133
134 let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>"
135 .parse()
136 .unwrap();
137 let error = MediaElement::try_from(elem).unwrap_err();
138 let error = match error {
139 Error::ParseIntError(error) => error,
140 _ => panic!(),
141 };
142 assert_eq!(error.description(), "invalid digit found in string");
143 }
144
145 #[test]
146 fn test_unknown_child() {
147 let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>"
148 .parse()
149 .unwrap();
150 let error = MediaElement::try_from(elem).unwrap_err();
151 let message = match error {
152 Error::ParseError(string) => string,
153 _ => panic!(),
154 };
155 assert_eq!(message, "Unknown child in media element.");
156 }
157
158 #[test]
159 fn test_bad_uri() {
160 let elem: Element =
161 "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>"
162 .parse()
163 .unwrap();
164 let error = MediaElement::try_from(elem).unwrap_err();
165 let message = match error {
166 Error::ParseError(string) => string,
167 _ => panic!(),
168 };
169 assert_eq!(message, "Required attribute 'type' missing.");
170
171 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>"
172 .parse()
173 .unwrap();
174 let error = MediaElement::try_from(elem).unwrap_err();
175 let message = match error {
176 Error::ParseError(string) => string,
177 _ => panic!(),
178 };
179 assert_eq!(message, "URI missing in uri.");
180 }
181
182 #[test]
183 fn test_xep_ex1() {
184 let elem: Element = r#"
185<media xmlns='urn:xmpp:media-element'>
186 <uri type='audio/x-wav'>
187 http://victim.example.com/challenges/speech.wav?F3A6292C
188 </uri>
189 <uri type='audio/ogg; codecs=speex'>
190 cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
191 </uri>
192 <uri type='audio/mpeg'>
193 http://victim.example.com/challenges/speech.mp3?F3A6292C
194 </uri>
195</media>"#
196 .parse()
197 .unwrap();
198 let media = MediaElement::try_from(elem).unwrap();
199 assert!(media.width.is_none());
200 assert!(media.height.is_none());
201 assert_eq!(media.uris.len(), 3);
202 assert_eq!(media.uris[0].type_, "audio/x-wav");
203 assert_eq!(
204 media.uris[0].uri,
205 "http://victim.example.com/challenges/speech.wav?F3A6292C"
206 );
207 assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
208 assert_eq!(
209 media.uris[1].uri,
210 "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
211 );
212 assert_eq!(media.uris[2].type_, "audio/mpeg");
213 assert_eq!(
214 media.uris[2].uri,
215 "http://victim.example.com/challenges/speech.mp3?F3A6292C"
216 );
217 }
218
219 #[test]
220 fn test_xep_ex2() {
221 let elem: Element = r#"
222<x xmlns='jabber:x:data' type='form'>
223 [ ... ]
224 <field var='ocr'>
225 <media xmlns='urn:xmpp:media-element'
226 height='80'
227 width='290'>
228 <uri type='image/jpeg'>
229 http://www.victim.com/challenges/ocr.jpeg?F3A6292C
230 </uri>
231 <uri type='image/jpeg'>
232 cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
233 </uri>
234 </media>
235 </field>
236 [ ... ]
237</x>"#
238 .parse()
239 .unwrap();
240 let form = DataForm::try_from(elem).unwrap();
241 assert_eq!(form.fields.len(), 1);
242 assert_eq!(form.fields[0].var, "ocr");
243 assert_eq!(form.fields[0].media[0].width, Some(290));
244 assert_eq!(form.fields[0].media[0].height, Some(80));
245 assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
246 assert_eq!(
247 form.fields[0].media[0].uris[0].uri,
248 "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
249 );
250 assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
251 assert_eq!(
252 form.fields[0].media[0].uris[1].uri,
253 "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
254 );
255 }
256}