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 TryFrom<Element> for MediaElement {
 29    type Error = Error;
 30
 31    fn try_from(elem: 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 mut media = MediaElement {
 37            width: get_attr!(elem, "width", optional),
 38            height: get_attr!(elem, "height", optional),
 39            uris: vec!(),
 40        };
 41        for uri in elem.children() {
 42            if uri.is("uri", ns::MEDIA_ELEMENT) {
 43                let type_ = get_attr!(uri, "type", required);
 44                let text = uri.text().trim().to_owned();
 45                if text == "" {
 46                    return Err(Error::ParseError("URI missing in uri."));
 47                }
 48                media.uris.push(URI { type_: type_, uri: text });
 49            } else {
 50                return Err(Error::ParseError("Unknown child in media element."));
 51            }
 52        }
 53        Ok(media)
 54    }
 55}
 56
 57#[cfg(test)]
 58mod tests {
 59    use super::*;
 60    use data_forms::DataForm;
 61    use std::error::Error as StdError;
 62
 63    #[test]
 64    fn test_simple() {
 65        let elem: Element = "<media xmlns='urn:xmpp:media-element'/>".parse().unwrap();
 66        let media = MediaElement::try_from(elem).unwrap();
 67        assert!(media.width.is_none());
 68        assert!(media.height.is_none());
 69        assert!(media.uris.is_empty());
 70    }
 71
 72    #[test]
 73    fn test_width_height() {
 74        let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>".parse().unwrap();
 75        let media = MediaElement::try_from(elem).unwrap();
 76        assert_eq!(media.width.unwrap(), 32);
 77        assert_eq!(media.height.unwrap(), 32);
 78    }
 79
 80    #[test]
 81    fn test_uri() {
 82        let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'>https://example.org/</uri></media>".parse().unwrap();
 83        let media = MediaElement::try_from(elem).unwrap();
 84        assert_eq!(media.uris.len(), 1);
 85        assert_eq!(media.uris[0].type_, "text/html");
 86        assert_eq!(media.uris[0].uri, "https://example.org/");
 87    }
 88
 89    #[test]
 90    fn test_invalid_width_height() {
 91        let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
 92        let error = MediaElement::try_from(elem).unwrap_err();
 93        let error = match error {
 94            Error::ParseIntError(error) => error,
 95            _ => panic!(),
 96        };
 97        assert_eq!(error.description(), "cannot parse integer from empty string");
 98
 99        let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
100        let error = MediaElement::try_from(elem).unwrap_err();
101        let error = match error {
102            Error::ParseIntError(error) => error,
103            _ => panic!(),
104        };
105        assert_eq!(error.description(), "invalid digit found in string");
106
107        let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
108        let error = MediaElement::try_from(elem).unwrap_err();
109        let error = match error {
110            Error::ParseIntError(error) => error,
111            _ => panic!(),
112        };
113        assert_eq!(error.description(), "cannot parse integer from empty string");
114
115        let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
116        let error = MediaElement::try_from(elem).unwrap_err();
117        let error = match error {
118            Error::ParseIntError(error) => error,
119            _ => panic!(),
120        };
121        assert_eq!(error.description(), "invalid digit found in string");
122    }
123
124    #[test]
125    fn test_unknown_child() {
126        let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></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, "Unknown child in media element.");
133    }
134
135    #[test]
136    fn test_bad_uri() {
137        let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().unwrap();
138        let error = MediaElement::try_from(elem).unwrap_err();
139        let message = match error {
140            Error::ParseError(string) => string,
141            _ => panic!(),
142        };
143        assert_eq!(message, "Required attribute 'type' missing.");
144
145        let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
146        let error = MediaElement::try_from(elem).unwrap_err();
147        let message = match error {
148            Error::ParseError(string) => string,
149            _ => panic!(),
150        };
151        assert_eq!(message, "URI missing in uri.");
152    }
153
154    #[test]
155    fn test_xep_ex1() {
156        let elem: Element = r#"
157<media xmlns='urn:xmpp:media-element'>
158  <uri type='audio/x-wav'>
159    http://victim.example.com/challenges/speech.wav?F3A6292C
160  </uri>
161  <uri type='audio/ogg; codecs=speex'>
162    cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org
163  </uri>
164  <uri type='audio/mpeg'>
165    http://victim.example.com/challenges/speech.mp3?F3A6292C
166  </uri>
167</media>"#.parse().unwrap();
168        let media = MediaElement::try_from(elem).unwrap();
169        assert!(media.width.is_none());
170        assert!(media.height.is_none());
171        assert_eq!(media.uris.len(), 3);
172        assert_eq!(media.uris[0].type_, "audio/x-wav");
173        assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
174        assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
175        assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
176        assert_eq!(media.uris[2].type_, "audio/mpeg");
177        assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
178    }
179
180    #[test]
181    fn test_xep_ex2() {
182        let elem: Element = r#"
183<x xmlns='jabber:x:data' type='form'>
184  [ ... ]
185  <field var='ocr'>
186    <media xmlns='urn:xmpp:media-element'
187           height='80'
188           width='290'>
189      <uri type='image/jpeg'>
190        http://www.victim.com/challenges/ocr.jpeg?F3A6292C
191      </uri>
192      <uri type='image/jpeg'>
193        cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org
194      </uri>
195    </media>
196  </field>
197  [ ... ]
198</x>"#.parse().unwrap();
199        let form = DataForm::try_from(elem).unwrap();
200        assert_eq!(form.fields.len(), 1);
201        assert_eq!(form.fields[0].var, "ocr");
202        assert_eq!(form.fields[0].media[0].width, Some(290));
203        assert_eq!(form.fields[0].media[0].height, Some(80));
204        assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
205        assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
206        assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
207        assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
208    }
209}