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 crate::Element;
51 use std::convert::TryFrom;
52
53 #[cfg(target_pointer_width = "32")]
54 #[test]
55 fn test_size() {
56 assert_size!(URI, 24);
57 assert_size!(MediaElement, 28);
58 }
59
60 #[cfg(target_pointer_width = "64")]
61 #[test]
62 fn test_size() {
63 assert_size!(URI, 48);
64 assert_size!(MediaElement, 56);
65 }
66
67 #[test]
68 fn test_simple() {
69 let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
70 let media = MediaElement::try_from(elem).unwrap();
71 assert!(media.width.is_none());
72 assert!(media.height.is_none());
73 assert!(media.uris.is_empty());
74 }
75
76 #[test]
77 fn test_width_height() {
78 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>"
79 .parse()
80 .unwrap();
81 let media = MediaElement::try_from(elem).unwrap();
82 assert_eq!(media.width.unwrap(), 32);
83 assert_eq!(media.height.unwrap(), 32);
84 }
85
86 #[test]
87 fn test_uri() {
88 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
89 let media = MediaElement::try_from(elem).unwrap();
90 assert_eq!(media.uris.len(), 1);
91 assert_eq!(media.uris[0].type_, "text/html");
92 assert_eq!(media.uris[0].uri, "https://example.org/");
93 }
94
95 #[test]
96 fn test_invalid_width_height() {
97 let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>"
98 .parse()
99 .unwrap();
100 let error = MediaElement::try_from(elem).unwrap_err();
101 let error = match error {
102 Error::ParseIntError(error) => error,
103 _ => panic!(),
104 };
105 assert_eq!(error.to_string(), "cannot parse integer from empty string");
106
107 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>"
108 .parse()
109 .unwrap();
110 let error = MediaElement::try_from(elem).unwrap_err();
111 let error = match error {
112 Error::ParseIntError(error) => error,
113 _ => panic!(),
114 };
115 assert_eq!(error.to_string(), "invalid digit found in string");
116
117 let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>"
118 .parse()
119 .unwrap();
120 let error = MediaElement::try_from(elem).unwrap_err();
121 let error = match error {
122 Error::ParseIntError(error) => error,
123 _ => panic!(),
124 };
125 assert_eq!(error.to_string(), "cannot parse integer from empty string");
126
127 let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>"
128 .parse()
129 .unwrap();
130 let error = MediaElement::try_from(elem).unwrap_err();
131 let error = match error {
132 Error::ParseIntError(error) => error,
133 _ => panic!(),
134 };
135 assert_eq!(error.to_string(), "invalid digit found in string");
136 }
137
138 #[test]
139 fn test_unknown_child() {
140 let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>"
141 .parse()
142 .unwrap();
143 let error = MediaElement::try_from(elem).unwrap_err();
144 let message = match error {
145 Error::ParseError(string) => string,
146 _ => panic!(),
147 };
148 assert_eq!(message, "Unknown child in media element.");
149 }
150
151 #[test]
152 fn test_bad_uri() {
153 let elem: Element =
154 "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>"
155 .parse()
156 .unwrap();
157 let error = MediaElement::try_from(elem).unwrap_err();
158 let message = match error {
159 Error::ParseError(string) => string,
160 _ => panic!(),
161 };
162 assert_eq!(message, "Required attribute 'type' missing.");
163
164 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>"
165 .parse()
166 .unwrap();
167 let error = MediaElement::try_from(elem).unwrap_err();
168 let message = match error {
169 Error::ParseError(string) => string,
170 _ => panic!(),
171 };
172 assert_eq!(message, "URI missing in uri.");
173 }
174
175 #[test]
176 fn test_xep_ex1() {
177 let elem: Element = r#"<media xmlns='urn:xmpp:media-element'>
178 <uri type='audio/x-wav'>
179 http://victim.example.com/challenges/speech.wav?F3A6292C
180 </uri>
181 <uri type='audio/ogg; codecs=speex'>
182 cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
183 </uri>
184 <uri type='audio/mpeg'>
185 http://victim.example.com/challenges/speech.mp3?F3A6292C
186 </uri>
187</media>"#
188 .parse()
189 .unwrap();
190 let media = MediaElement::try_from(elem).unwrap();
191 assert!(media.width.is_none());
192 assert!(media.height.is_none());
193 assert_eq!(media.uris.len(), 3);
194 assert_eq!(media.uris[0].type_, "audio/x-wav");
195 assert_eq!(
196 media.uris[0].uri,
197 "http://victim.example.com/challenges/speech.wav?F3A6292C"
198 );
199 assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
200 assert_eq!(
201 media.uris[1].uri,
202 "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
203 );
204 assert_eq!(media.uris[2].type_, "audio/mpeg");
205 assert_eq!(
206 media.uris[2].uri,
207 "http://victim.example.com/challenges/speech.mp3?F3A6292C"
208 );
209 }
210
211 #[test]
212 fn test_xep_ex2() {
213 let elem: Element = r#"<x xmlns='jabber:x:data' type='form'>
214 [ ... ]
215 <field var='ocr'>
216 <media xmlns='urn:xmpp:media-element'
217 height='80'
218 width='290'>
219 <uri type='image/jpeg'>
220 http://www.victim.com/challenges/ocr.jpeg?F3A6292C
221 </uri>
222 <uri type='image/jpeg'>
223 cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
224 </uri>
225 </media>
226 </field>
227 [ ... ]
228</x>"#
229 .parse()
230 .unwrap();
231 let form = DataForm::try_from(elem).unwrap();
232 assert_eq!(form.fields.len(), 1);
233 assert_eq!(form.fields[0].var, "ocr");
234 assert_eq!(form.fields[0].media[0].width, Some(290));
235 assert_eq!(form.fields[0].media[0].height, Some(80));
236 assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
237 assert_eq!(
238 form.fields[0].media[0].uris[0].uri,
239 "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
240 );
241 assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
242 assert_eq!(
243 form.fields[0].media[0].uris[1].uri,
244 "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
245 );
246 }
247}