Google takeout: Decode MIME-encoded values in X-Gmail-Labels (fixes #7)
This commit is contained in:
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. This projec
|
||||
|
||||
### Fixed
|
||||
- Mailbox roles must be unique per archive (#8).
|
||||
- Google takeout: Decode MIME-encoded values in `X-Gmail-Labels`.
|
||||
|
||||
# Change Log
|
||||
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use mail_parser::HeaderValue;
|
||||
use mail_parser::parsers::MessageStream;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LabelClassification {
|
||||
pub mailboxes: Vec<String>,
|
||||
@@ -43,7 +47,8 @@ pub struct MailboxAssignment {
|
||||
}
|
||||
|
||||
pub fn parse_header(value: &str) -> Vec<String> {
|
||||
value
|
||||
let decoded = decode_encoded_words(value);
|
||||
decoded
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|t| !t.is_empty())
|
||||
@@ -51,6 +56,19 @@ pub fn parse_header(value: &str) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn decode_encoded_words(value: &str) -> Cow<'_, str> {
|
||||
if !value.contains("=?") {
|
||||
return Cow::Borrowed(value);
|
||||
}
|
||||
let mut buf = Vec::with_capacity(value.len() + 1);
|
||||
buf.extend_from_slice(value.as_bytes());
|
||||
buf.push(b'\n');
|
||||
match MessageStream::new(&buf).parse_unstructured() {
|
||||
HeaderValue::Text(text) => Cow::Owned(text.into_owned()),
|
||||
_ => Cow::Borrowed(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn classify(tokens: &[String]) -> LabelClassification {
|
||||
let mut mailboxes: Vec<String> = Vec::new();
|
||||
let mut keywords: BTreeSet<String> = BTreeSet::new();
|
||||
@@ -158,6 +176,24 @@ mod tests {
|
||||
assert_eq!(v, vec!["Inbox", "Opened", "Starred", "Important"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_header_decodes_rfc2047_encoded_words() {
|
||||
let v = parse_header("=?UTF-8?Q?=C3=85pnet,Kategori:_Reklame?=");
|
||||
assert_eq!(v, vec!["Åpnet", "Kategori: Reklame"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_header_decodes_mixed_plain_and_encoded_tokens() {
|
||||
let v = parse_header("Inbox,=?UTF-8?Q?=C3=85pnet?=,Github");
|
||||
assert_eq!(v, vec!["Inbox", "Åpnet", "Github"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_header_leaves_plain_ascii_untouched() {
|
||||
let v = parse_header("Inbox,Opened,Label_001/Label_002");
|
||||
assert_eq!(v, vec!["Inbox", "Opened", "Label_001/Label_002"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_header_empty_yields_empty() {
|
||||
assert!(parse_header("").is_empty());
|
||||
|
||||
Reference in New Issue
Block a user