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#"
178<media xmlns='urn:xmpp:media-element'>
179 <uri type='audio/x-wav'>
180 http://victim.example.com/challenges/speech.wav?F3A6292C
181 </uri>
182 <uri type='audio/ogg; codecs=speex'>
183 cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
184 </uri>
185 <uri type='audio/mpeg'>
186 http://victim.example.com/challenges/speech.mp3?F3A6292C
187 </uri>
188</media>"#
189 .parse()
190 .unwrap();
191 let media = MediaElement::try_from(elem).unwrap();
192 assert!(media.width.is_none());
193 assert!(media.height.is_none());
194 assert_eq!(media.uris.len(), 3);
195 assert_eq!(media.uris[0].type_, "audio/x-wav");
196 assert_eq!(
197 media.uris[0].uri,
198 "http://victim.example.com/challenges/speech.wav?F3A6292C"
199 );
200 assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
201 assert_eq!(
202 media.uris[1].uri,
203 "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
204 );
205 assert_eq!(media.uris[2].type_, "audio/mpeg");
206 assert_eq!(
207 media.uris[2].uri,
208 "http://victim.example.com/challenges/speech.mp3?F3A6292C"
209 );
210 }
211
212 #[test]
213 fn test_xep_ex2() {
214 let elem: Element = r#"
215<x xmlns='jabber:x:data' type='form'>
216 [ ... ]
217 <field var='ocr'>
218 <media xmlns='urn:xmpp:media-element'
219 height='80'
220 width='290'>
221 <uri type='image/jpeg'>
222 http://www.victim.com/challenges/ocr.jpeg?F3A6292C
223 </uri>
224 <uri type='image/jpeg'>
225 cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
226 </uri>
227 </media>
228 </field>
229 [ ... ]
230</x>"#
231 .parse()
232 .unwrap();
233 let form = DataForm::try_from(elem).unwrap();
234 assert_eq!(form.fields.len(), 1);
235 assert_eq!(form.fields[0].var, "ocr");
236 assert_eq!(form.fields[0].media[0].width, Some(290));
237 assert_eq!(form.fields[0].media[0].height, Some(80));
238 assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
239 assert_eq!(
240 form.fields[0].media[0].uris[0].uri,
241 "http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
242 );
243 assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
244 assert_eq!(
245 form.fields[0].media[0].uris[1].uri,
246 "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
247 );
248 }
249}