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 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}