rfc2047_decoder/
evaluator.rs1use 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#[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}