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::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_: String = "type" => required
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" => optional,
35
36 /// The recommended display height in pixels.
37 height: Option<usize> = "height" => optional
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 try_from::TryFrom;
49 use minidom::Element;
50 use crate::error::Error;
51 use crate::data_forms::DataForm;
52 use std::error::Error as StdError;
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'/>".parse().unwrap();
80 let media = MediaElement::try_from(elem).unwrap();
81 assert_eq!(media.width.unwrap(), 32);
82 assert_eq!(media.height.unwrap(), 32);
83 }
84
85 #[test]
86 fn test_uri() {
87 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
88 let media = MediaElement::try_from(elem).unwrap();
89 assert_eq!(media.uris.len(), 1);
90 assert_eq!(media.uris[0].type_, "text/html");
91 assert_eq!(media.uris[0].uri, "https://example.org/");
92 }
93
94 #[test]
95 fn test_invalid_width_height() {
96 let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
97 let error = MediaElement::try_from(elem).unwrap_err();
98 let error = match error {
99 Error::ParseIntError(error) => error,
100 _ => panic!(),
101 };
102 assert_eq!(error.description(), "cannot parse integer from empty string");
103
104 let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
105 let error = MediaElement::try_from(elem).unwrap_err();
106 let error = match error {
107 Error::ParseIntError(error) => error,
108 _ => panic!(),
109 };
110 assert_eq!(error.description(), "invalid digit found in string");
111
112 let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
113 let error = MediaElement::try_from(elem).unwrap_err();
114 let error = match error {
115 Error::ParseIntError(error) => error,
116 _ => panic!(),
117 };
118 assert_eq!(error.description(), "cannot parse integer from empty string");
119
120 let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
121 let error = MediaElement::try_from(elem).unwrap_err();
122 let error = match error {
123 Error::ParseIntError(error) => error,
124 _ => panic!(),
125 };
126 assert_eq!(error.description(), "invalid digit found in string");
127 }
128
129 #[test]
130 fn test_unknown_child() {
131 let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
132 let error = MediaElement::try_from(elem).unwrap_err();
133 let message = match error {
134 Error::ParseError(string) => string,
135 _ => panic!(),
136 };
137 assert_eq!(message, "Unknown child in media element.");
138 }
139
140 #[test]
141 fn test_bad_uri() {
142 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().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, "Required attribute 'type' missing.");
149
150 let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
151 let error = MediaElement::try_from(elem).unwrap_err();
152 let message = match error {
153 Error::ParseError(string) => string,
154 _ => panic!(),
155 };
156 assert_eq!(message, "URI missing in uri.");
157 }
158
159 #[test]
160 fn test_xep_ex1() {
161 let elem: Element = r#"
162<media xmlns='urn:xmpp:media-element'>
163 <uri type='audio/x-wav'>
164 http://victim.example.com/challenges/speech.wav?F3A6292C
165 </uri>
166 <uri type='audio/ogg; codecs=speex'>
167 cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
168 </uri>
169 <uri type='audio/mpeg'>
170 http://victim.example.com/challenges/speech.mp3?F3A6292C
171 </uri>
172</media>"#.parse().unwrap();
173 let media = MediaElement::try_from(elem).unwrap();
174 assert!(media.width.is_none());
175 assert!(media.height.is_none());
176 assert_eq!(media.uris.len(), 3);
177 assert_eq!(media.uris[0].type_, "audio/x-wav");
178 assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
179 assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
180 assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
181 assert_eq!(media.uris[2].type_, "audio/mpeg");
182 assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
183 }
184
185 #[test]
186 fn test_xep_ex2() {
187 let elem: Element = r#"
188<x xmlns='jabber:x:data' type='form'>
189 [ ... ]
190 <field var='ocr'>
191 <media xmlns='urn:xmpp:media-element'
192 height='80'
193 width='290'>
194 <uri type='image/jpeg'>
195 http://www.victim.com/challenges/ocr.jpeg?F3A6292C
196 </uri>
197 <uri type='image/jpeg'>
198 cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
199 </uri>
200 </media>
201 </field>
202 [ ... ]
203</x>"#.parse().unwrap();
204 let form = DataForm::try_from(elem).unwrap();
205 assert_eq!(form.fields.len(), 1);
206 assert_eq!(form.fields[0].var, "ocr");
207 assert_eq!(form.fields[0].media[0].width, Some(290));
208 assert_eq!(form.fields[0].media[0].height, Some(80));
209 assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
210 assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
211 assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
212 assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
213 }
214}