jingle_ft.rs

  1extern crate minidom;
  2
  3use hashes::{Hash, parse_hash};
  4
  5use minidom::Element;
  6
  7use error::Error;
  8use ns;
  9
 10#[derive(Debug, Clone, PartialEq)]
 11pub struct Range {
 12    pub offset: u64,
 13    pub length: Option<u64>,
 14    pub hashes: Vec<Hash>,
 15}
 16
 17#[derive(Debug, Clone)]
 18pub struct File {
 19    pub date: Option<String>,
 20    pub media_type: Option<String>,
 21    pub name: Option<String>,
 22    pub size: Option<String>,
 23    pub range: Option<Range>,
 24    pub hashes: Vec<Hash>,
 25}
 26
 27#[derive(Debug, Clone)]
 28pub struct Description {
 29    pub file: File,
 30}
 31
 32#[derive(Debug, Clone)]
 33pub struct Creator {
 34    pub creator: String,
 35}
 36
 37#[derive(Debug, Clone)]
 38pub struct Checksum {
 39    pub name: String,
 40    pub creator: Creator,
 41    pub file: File,
 42}
 43
 44pub fn parse_jingle_ft(root: &Element) -> Result<Description, Error> {
 45    if !root.is("description", ns::JINGLE_FT) {
 46        return Err(Error::ParseError("This is not a JingleFT description element."));
 47    }
 48    if root.children().collect::<Vec<_>>().len() != 1 {
 49        return Err(Error::ParseError("JingleFT description element must have exactly one child."));
 50    }
 51
 52    let mut date = None;
 53    let mut media_type = None;
 54    let mut name = None;
 55    let mut size = None;
 56    let mut range = None;
 57    let mut hashes = vec!();
 58    for description_payload in root.children() {
 59        if !description_payload.is("file", ns::JINGLE_FT) {
 60            return Err(Error::ParseError("Unknown element in JingleFT description."));
 61        }
 62        for file_payload in description_payload.children() {
 63            if file_payload.is("date", ns::JINGLE_FT) {
 64                if date.is_some() {
 65                    return Err(Error::ParseError("File must not have more than one date."));
 66                }
 67                date = Some(file_payload.text());
 68            } else if file_payload.is("media-type", ns::JINGLE_FT) {
 69                if media_type.is_some() {
 70                    return Err(Error::ParseError("File must not have more than one media-type."));
 71                }
 72                media_type = Some(file_payload.text());
 73            } else if file_payload.is("name", ns::JINGLE_FT) {
 74                if name.is_some() {
 75                    return Err(Error::ParseError("File must not have more than one name."));
 76                }
 77                name = Some(file_payload.text());
 78            } else if file_payload.is("size", ns::JINGLE_FT) {
 79                if size.is_some() {
 80                    return Err(Error::ParseError("File must not have more than one size."));
 81                }
 82                size = Some(file_payload.text());
 83            } else if file_payload.is("range", ns::JINGLE_FT) {
 84                if range.is_some() {
 85                    return Err(Error::ParseError("File must not have more than one range."));
 86                }
 87                let offset = file_payload.attr("offset").unwrap_or("0").parse()?;
 88                let length = match file_payload.attr("length") {
 89                    Some(length) => Some(length.parse()?),
 90                    None => None,
 91                };
 92                let mut range_hashes = vec!();
 93                for hash_element in file_payload.children() {
 94                    if !hash_element.is("hash", ns::HASHES) {
 95                        return Err(Error::ParseError("Unknown element in JingleFT range."));
 96                    }
 97                    range_hashes.push(parse_hash(hash_element)?);
 98                }
 99                range = Some(Range {
100                    offset: offset,
101                    length: length,
102                    hashes: range_hashes,
103                });
104            } else if file_payload.is("hash", ns::HASHES) {
105                hashes.push(parse_hash(file_payload)?);
106            } else {
107                return Err(Error::ParseError("Unknown element in JingleFT file."));
108            }
109        }
110    }
111
112    Ok(Description {
113        file: File {
114            date: date,
115            media_type: media_type,
116            name: name,
117            size: size,
118            range: range,
119            hashes: hashes,
120        },
121    })
122}
123
124#[cfg(test)]
125mod tests {
126    use minidom::Element;
127    use jingle_ft;
128
129    #[test]
130    fn test_description() {
131        let elem: Element = r#"
132<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
133  <file>
134    <media-type>text/plain</media-type>
135    <name>test.txt</name>
136    <date>2015-07-26T21:46:00</date>
137    <size>6144</size>
138    <hash xmlns='urn:xmpp:hashes:2'
139          algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
140  </file>
141</description>
142"#.parse().unwrap();
143
144        let desc = jingle_ft::parse_jingle_ft(&elem).unwrap();
145        assert_eq!(desc.file.media_type, Some(String::from("text/plain")));
146        assert_eq!(desc.file.name, Some(String::from("test.txt")));
147        assert_eq!(desc.file.date, Some(String::from("2015-07-26T21:46:00")));
148        assert_eq!(desc.file.size, Some(String::from("6144")));
149        assert_eq!(desc.file.range, None);
150        assert_eq!(desc.file.hashes[0].algo, "sha-1");
151        assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48=");
152    }
153
154    #[test]
155    fn test_request() {
156        let elem: Element = r#"
157<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
158  <file>
159    <hash xmlns='urn:xmpp:hashes:2'
160          algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
161  </file>
162</description>
163"#.parse().unwrap();
164
165        let desc = jingle_ft::parse_jingle_ft(&elem).unwrap();
166        assert_eq!(desc.file.media_type, None);
167        assert_eq!(desc.file.name, None);
168        assert_eq!(desc.file.date, None);
169        assert_eq!(desc.file.size, None);
170        assert_eq!(desc.file.range, None);
171        assert_eq!(desc.file.hashes[0].algo, "sha-1");
172        assert_eq!(desc.file.hashes[0].hash, "w0mcJylzCn+AfvuGdqkty2+KP48=");
173    }
174}