media_element.rs

  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}