1#[allow(unused, deprecated)]
2use std::ascii::AsciiExt;
3use std::error::Error;
4use std::fmt;
5use std::iter::Enumerate;
6use std::str::Bytes;
7
8use super::{Mime, MimeIter, Source, ParamSource, Indexed, CHARSET, UTF_8};
9
10#[derive(Debug)]
11pub enum ParseError {
12    MissingSlash,
13    MissingEqual,
14    MissingQuote,
15    InvalidToken {
16        pos: usize,
17        byte: u8,
18    },
19}
20
21impl ParseError {
22    fn s(&self) -> &str {
23        use self::ParseError::*;
24
25        match *self {
26            MissingSlash => "a slash (/) was missing between the type and subtype",
27            MissingEqual => "an equals sign (=) was missing between a parameter and its value",
28            MissingQuote => "a quote (\") was missing from a parameter value",
29            InvalidToken { .. } => "an invalid token was encountered",
30        }
31    }
32}
33
34impl fmt::Display for ParseError {
35    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36        if let ParseError::InvalidToken { pos, byte } = *self {
37            write!(f, "{}, {:X} at position {}", self.s(), byte, pos)
38        } else {
39            f.write_str(self.s())
40        }
41    }
42}
43
44impl Error for ParseError {
45    #[allow(deprecated)]
47    fn description(&self) -> &str {
48        self.s()
49    }
50}
51
52impl<'a> MimeIter<'a> {
53    pub fn new(s: &'a str) -> Self {
55        Self {
56            pos: 0,
57            source: s,
58        }
59    }
60}
61
62impl<'a> Iterator for MimeIter<'a> {
63    type Item = Result<Mime, &'a str>;
64
65    fn next(&mut self) -> Option<Self::Item> {
66        let start = self.pos;
67        let len = self.source.bytes().len();
68
69        if start >= len {
70            return None
71        }
72
73        match parse(&self.source[start ..len]) {
75            Ok(value) => {
76                self.pos = len;
77                Some(Ok(value))
78            }
79            Err(ParseError::InvalidToken { pos, .. }) => {
80                if pos == 0 {
82                    self.pos += 1;
83                    return self.next()
84                }
85                let slice = &self.source[start .. start + pos];
86                return match parse(slice) {
88                    Ok(mime) => {
89                        self.pos = start + pos + 1;
90                        Some(Ok(mime))
91                    }
92                    Err(_) => {
93                        if start + pos < len {
94                            self.pos = start + pos;
97                            Some(Err(slice))
98                        } else {
99                            None
100                        }
101                    }
102                }
103            }
104            Err(_) => None,
107        }
108    }
109}
110
111pub fn parse(s: &str) -> Result<Mime, ParseError> {
112    if s == "*/*" {
113        return Ok(::STAR_STAR);
114    }
115
116    let mut iter = s.bytes().enumerate();
117    let mut start;
119    let slash;
120    loop {
121        match iter.next() {
122            Some((_, c)) if is_token(c) => (),
123            Some((i, b'/')) if i > 0 => {
124                slash = i;
125                start = i + 1;
126                break;
127            },
128            None => return Err(ParseError::MissingSlash), Some((pos, byte)) => return Err(ParseError::InvalidToken {
130                pos: pos,
131                byte: byte,
132            })
133        };
134
135    }
136
137    let mut plus = None;
139    loop {
140        match iter.next() {
141            Some((i, b'+')) if i > start => {
142                plus = Some(i);
143            },
144            Some((i, b';')) if i > start => {
145                start = i;
146                break;
147            },
148            Some((_, c)) if is_token(c) => (),
149            None => {
150                return Ok(Mime {
151                    source: Source::Dynamic(s.to_ascii_lowercase()),
152                    slash: slash,
153                    plus: plus,
154                    params: ParamSource::None,
155                });
156            },
157            Some((pos, byte)) => return Err(ParseError::InvalidToken {
158                pos: pos,
159                byte: byte,
160            })
161        };
162    }
163
164    let params = params_from_str(s, &mut iter, start)?;
166
167    let src = match params {
168        ParamSource::Utf8(_)  => s.to_ascii_lowercase(),
169        ParamSource::Custom(semicolon, ref indices) => lower_ascii_with_params(s, semicolon, indices),
170        ParamSource::None => {
171            s[..start].to_ascii_lowercase()
173        }
174    };
175
176    Ok(Mime {
177        source: Source::Dynamic(src),
178        slash: slash,
179        plus: plus,
180        params: params,
181    })
182}
183
184
185fn params_from_str(s: &str, iter: &mut Enumerate<Bytes>, mut start: usize) -> Result<ParamSource, ParseError> {
186    let semicolon = start;
187    start += 1;
188    let mut params = ParamSource::None;
189    'params: while start < s.len() {
190        let name;
191        'name: loop {
193            match iter.next() {
194                Some((i, b' ')) if i == start => {
195                    start = i + 1;
196                    continue 'params;
197                },
198                Some((_, c)) if is_token(c) => (),
199                Some((i, b'=')) if i > start => {
200                    name = Indexed(start, i);
201                    start = i + 1;
202                    break 'name;
203                },
204                None => return Err(ParseError::MissingEqual),
205                Some((pos, byte)) => return Err(ParseError::InvalidToken {
206                    pos: pos,
207                    byte: byte,
208                }),
209            }
210        }
211
212        let value;
213        let mut is_quoted = false;
215
216        'value: loop {
217            if is_quoted {
218                match iter.next() {
219                    Some((i, b'"')) if i > start => {
220                        value = Indexed(start, i);
221                        break 'value;
222                    },
223                    Some((_, c)) if is_restricted_quoted_char(c) => (),
224                    None => return Err(ParseError::MissingQuote),
225                    Some((pos, byte)) => return Err(ParseError::InvalidToken {
226                        pos: pos,
227                        byte: byte,
228                    }),
229                }
230            } else {
231                match iter.next() {
232                    Some((i, b'"')) if i == start => {
233                        is_quoted = true;
234                        start = i + 1;
235                    },
236                    Some((_, c)) if is_token(c) => (),
237                    Some((i, b';')) if i > start => {
238                        value = Indexed(start, i);
239                        start = i + 1;
240                        break 'value;
241                    }
242                    None => {
243                        value = Indexed(start, s.len());
244                        start = s.len();
245                        break 'value;
246                    },
247
248                    Some((pos, byte)) => return Err(ParseError::InvalidToken {
249                        pos: pos,
250                        byte: byte,
251                    }),
252                }
253            }
254        }
255
256        if is_quoted {
257            'ws: loop {
258                match iter.next() {
259                    Some((i, b';')) => {
260                        start = i + 1;
262                        break 'ws;
263                    },
264                    Some((_, b' ')) => {
265                        },
267                    None => {
268                        start = s.len();
270                        break 'ws;
271                    },
272                    Some((pos, byte)) => return Err(ParseError::InvalidToken {
273                        pos: pos,
274                        byte: byte,
275                    }),
276                }
277            }
278        }
279
280        match params {
281            ParamSource::Utf8(i) => {
282                let i = i + 2;
283                let charset = Indexed(i, "charset".len() + i);
284                let utf8 = Indexed(charset.1 + 1, charset.1 + "utf-8".len() + 1);
285                params = ParamSource::Custom(semicolon, vec![
286                    (charset, utf8),
287                    (name, value),
288                ]);
289            },
290            ParamSource::Custom(_, ref mut vec) => {
291                vec.push((name, value));
292            },
293            ParamSource::None => {
294                if semicolon + 2 == name.0 && CHARSET == &s[name.0..name.1] {
295                    if UTF_8 == &s[value.0..value.1] {
296                        params = ParamSource::Utf8(semicolon);
297                        continue 'params;
298                    }
299                }
300                params = ParamSource::Custom(semicolon, vec![(name, value)]);
301            },
302        }
303    }
304    Ok(params)
305}
306
307fn lower_ascii_with_params(s: &str, semi: usize, params: &[(Indexed, Indexed)]) -> String {
308    let mut owned = s.to_owned();
309    owned[..semi].make_ascii_lowercase();
310
311    for &(ref name, ref value) in params {
312        owned[name.0..name.1].make_ascii_lowercase();
313        if &owned[name.0..name.1] == CHARSET.source {
317            owned[value.0..value.1].make_ascii_lowercase();
318        }
319    }
320
321    owned
322}
323
324macro_rules! byte_map {
362    ($($flag:expr,)*) => ([
363        $($flag != 0,)*
364    ])
365}
366
367static TOKEN_MAP: [bool; 256] = byte_map![
368    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370    0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
371    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
372    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
373    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
374    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
375    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
376    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
378    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
379    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
380    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
381    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
382    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
383    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
384];
385
386fn is_token(c: u8) -> bool {
387    TOKEN_MAP[c as usize]
388}
389
390fn is_restricted_quoted_char(c: u8) -> bool {
391    c > 31 && c != 127
392}
393
394#[test]
395#[allow(warnings)] fn test_lookup_tables() {
397    for (i, &valid) in TOKEN_MAP.iter().enumerate() {
398        let i = i as u8;
399        let should = match i {
400            b'a'...b'z' |
401            b'A'...b'Z' |
402            b'0'...b'9' |
403            b'!' |
404            b'#' |
405            b'$' |
406            b'%' |
407            b'&' |
408            b'\'' |
409            b'*' |
410            b'+' |
411            b'-' |
412            b'.' |
413            b'^' |
414            b'_' |
415            b'`' |
416            b'|' |
417            b'~' => true,
418            _ => false
419        };
420        assert_eq!(valid, should, "{:?} ({}) should be {}", i as char, i, should);
421    }
422}
423
424#[test]
425fn test_parse_iterator() {
426    let mut iter = MimeIter::new("application/json, application/json");
427    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
428    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
429    assert_eq!(iter.next(), None);
430
431    let mut iter = MimeIter::new("application/json");
432    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
433    assert_eq!(iter.next(), None);
434
435    let mut iter = MimeIter::new("application/json;  ");
436    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
437    assert_eq!(iter.next(), None);
438}
439
440#[test]
441fn test_parse_iterator_invalid() {
442    let mut iter = MimeIter::new("application/json, invalid, application/json");
443    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
444    assert_eq!(iter.next().unwrap().unwrap_err(), "invalid");
445    assert_eq!(iter.next().unwrap().unwrap(), parse("application/json").unwrap());
446    assert_eq!(iter.next(), None);
447}
448
449#[test]
450fn test_parse_iterator_all_invalid() {
451    let mut iter = MimeIter::new("application/json, text/html");
452    assert_eq!(iter.next().unwrap().unwrap_err(), "application/json");
453    assert_eq!(iter.next(), None);
454}