rfc2047_decoder/
evaluator.rs

1use base64::{
2    alphabet,
3    engine::{GeneralPurpose, GeneralPurposeConfig},
4    Engine,
5};
6use charset::Charset;
7use std::{result, string};
8use thiserror::Error;
9
10use crate::parser::{ClearText, Encoding, ParsedEncodedWord, ParsedEncodedWords};
11
12/// All errors which the evaluator can throw.
13#[derive(Error, Debug, PartialEq)]
14pub enum Error {
15    #[error(transparent)]
16    DecodeUtf8Error(#[from] string::FromUtf8Error),
17    #[error(transparent)]
18    DecodeBase64Error(#[from] base64::DecodeError),
19    #[error(transparent)]
20    DecodeQuotedPrintableError(#[from] quoted_printable::QuotedPrintableError),
21}
22
23type Result<T> = result::Result<T, Error>;
24
25fn decode_base64(encoded_bytes: Vec<u8>) -> Result<Vec<u8>> {
26    let base64_decoder = {
27        let config = GeneralPurposeConfig::new().with_decode_allow_trailing_bits(true);
28        GeneralPurpose::new(&alphabet::STANDARD, config)
29    };
30
31    let decoded_bytes = base64_decoder.decode(encoded_bytes)?;
32
33    Ok(decoded_bytes)
34}
35
36fn decode_quoted_printable(encoded_bytes: Vec<u8>) -> Result<Vec<u8>> {
37    let parse_mode = quoted_printable::ParseMode::Robust;
38
39    const SPACE: u8 = b' ';
40    const UNDERSCORE: u8 = b'_';
41
42    let encoded_bytes = encoded_bytes
43        .iter()
44        .map(|b| if *b == UNDERSCORE { SPACE } else { *b })
45        .collect::<Vec<_>>();
46
47    let decoded_bytes = quoted_printable::decode(encoded_bytes, parse_mode)?;
48
49    Ok(decoded_bytes)
50}
51
52fn decode_with_encoding(encoding: Encoding, encoded_bytes: Vec<u8>) -> Result<Vec<u8>> {
53    match encoding {
54        Encoding::B => decode_base64(encoded_bytes),
55        Encoding::Q => decode_quoted_printable(encoded_bytes),
56    }
57}
58
59fn decode_with_charset(charset: Option<Charset>, decoded_bytes: Vec<u8>) -> Result<String> {
60    let decoded_str = match charset {
61        Some(charset) => charset.decode(&decoded_bytes).0,
62        None => charset::decode_ascii(&decoded_bytes),
63    };
64
65    Ok(decoded_str.into_owned())
66}
67
68fn decode_utf8_string(clear_text: ClearText) -> Result<String> {
69    let decoded_bytes = String::from_utf8(clear_text)?;
70    Ok(decoded_bytes)
71}
72
73fn decode_parsed_encoded_word(
74    charset: Option<Charset>,
75    encoding: Encoding,
76    encoded_text: Vec<u8>,
77) -> Result<String> {
78    let decoded_bytes = decode_with_encoding(encoding, encoded_text)?;
79    let decoded_str = decode_with_charset(charset, decoded_bytes)?;
80    Ok(decoded_str)
81}
82
83pub fn run(parsed_encoded_words: ParsedEncodedWords) -> Result<String> {
84    parsed_encoded_words
85        .into_iter()
86        .map(|parsed_encoded_word| match parsed_encoded_word {
87            ParsedEncodedWord::ClearText(clear_text) => decode_utf8_string(clear_text),
88            ParsedEncodedWord::EncodedWord {
89                charset,
90                encoding,
91                encoded_text,
92            } => decode_parsed_encoded_word(charset, encoding, encoded_text),
93        })
94        .collect()
95}