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