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