mail_parser/decoders/
hex.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::quoted_printable::HEX_MAP;
8
9#[derive(PartialEq, Debug)]
10enum HexState {
11    None,
12    Percent,
13    Hex1,
14}
15
16pub fn decode_hex(src: &[u8]) -> (bool, Vec<u8>) {
17    let mut state = HexState::None;
18    let mut hex1 = 0;
19    let mut result = Vec::with_capacity(src.len());
20    let mut success = true;
21
22    for ch in src {
23        match ch {
24            b'%' => {
25                if let HexState::None = state {
26                    state = HexState::Percent
27                } else {
28                    success = false;
29                    break;
30                }
31            }
32            _ => match state {
33                HexState::None => {
34                    result.push(*ch);
35                }
36                HexState::Percent => {
37                    hex1 = HEX_MAP[*ch as usize];
38                    if hex1 != -1 {
39                        state = HexState::Hex1;
40                    } else {
41                        success = false;
42                        break;
43                    }
44                }
45                HexState::Hex1 => {
46                    let hex2 = HEX_MAP[*ch as usize];
47
48                    state = HexState::None;
49                    if hex2 != -1 {
50                        result.push(((hex1 as u8) << 4) | hex2 as u8);
51                    } else {
52                        success = false;
53                        break;
54                    }
55                }
56            },
57        }
58    }
59
60    (success, result)
61}
62
63#[cfg(test)]
64mod tests {
65    use crate::decoders::hex::decode_hex;
66
67    #[test]
68    fn decode_hex_line() {
69        let inputs = [
70            ("this%20is%20some%20text", "this is some text"),
71            ("this is some text", "this is some text"),
72        ];
73
74        for input in inputs {
75            let (success, result) = decode_hex(input.0.as_bytes());
76
77            assert!(success, "Failed for '{:?}'", input.0);
78
79            let result_str = std::str::from_utf8(&result).unwrap();
80
81            /*println!(
82                "Decoded '{}'\n -> to ->\n'{}'\n{}",
83                input.0.escape_debug(),
84                result_str.escape_debug(),
85                "-".repeat(50)
86            );*/
87
88            assert_eq!(
89                input.1,
90                result_str,
91                "Failed for '{}'",
92                input.0.escape_debug()
93            );
94        }
95    }
96}