Compare commits

...

2 Commits

Author SHA1 Message Date
link2xt
80f59b3ff0 WIP attempt to parse List-Id header 2023-05-16 15:33:02 +00:00
link2xt
5bdc3cefb7 fix: handle quote marks in the List-Id when setting chat name 2023-05-16 15:32:43 +00:00
3 changed files with 56 additions and 6 deletions

View File

@@ -63,6 +63,10 @@ pub(crate) struct MimeMessage {
/// Whether the From address was repeated in the signed part
/// (and we know that the signer intended to send from this address)
pub from_is_signed: bool,
/// List-Id header as defined in <https://datatracker.ietf.org/doc/html/rfc2919>.
pub list_id: Option<SingleInfo>,
pub list_post: Option<String>,
pub chat_disposition_notification_to: Option<SingleInfo>,
pub decryption_info: DecryptionInfo,
@@ -212,6 +216,7 @@ impl MimeMessage {
let mut headers = Default::default();
let mut recipients = Default::default();
let mut from = Default::default();
let mut list_id = Default::default();
let mut list_post = Default::default();
let mut chat_disposition_notification_to = None;
@@ -221,6 +226,7 @@ impl MimeMessage {
&mut headers,
&mut recipients,
&mut from,
&mut list_id,
&mut list_post,
&mut chat_disposition_notification_to,
&mail.headers,
@@ -239,6 +245,7 @@ impl MimeMessage {
&mut headers,
&mut recipients,
&mut from,
&mut list_id,
&mut list_post,
&mut chat_disposition_notification_to,
&part.headers,
@@ -344,6 +351,7 @@ impl MimeMessage {
&mut headers,
&mut recipients,
&mut signed_from,
&mut list_id,
&mut list_post,
&mut chat_disposition_notification_to,
&mail.headers,
@@ -386,6 +394,7 @@ impl MimeMessage {
parts: Vec::new(),
header: headers,
recipients,
list_id,
list_post,
from,
from_is_signed,
@@ -1375,6 +1384,7 @@ impl MimeMessage {
headers: &mut HashMap<String, String>,
recipients: &mut Vec<SingleInfo>,
from: &mut Option<SingleInfo>,
list_id: &mut Option<SingleInfo>,
list_post: &mut Option<String>,
chat_disposition_notification_to: &mut Option<SingleInfo>,
fields: &[mailparse::MailHeader<'_>],
@@ -1392,6 +1402,14 @@ impl MimeMessage {
}
Err(e) => warn!(context, "Could not read {} address: {}", key, e),
}
} else if key == HeaderDef::ListId.get_headername() {
match addrparse_header(field) {
Ok(addrlist) => {
*list_id = addrlist.extract_single_info();
}
Err(e) => warn!(context, "Could not read {} address: {}", key, e),
}
eprintln!("LIST ID parsed as {:?}", *list_id);
} else {
let value = field.get_value();
headers.insert(key.to_string(), value);

View File

@@ -5,7 +5,7 @@ use std::collections::HashSet;
use std::convert::TryFrom;
use anyhow::{bail, ensure, Context as _, Result};
use mailparse::{parse_mail, SingleInfo};
use mailparse::{addrparse, parse_mail, SingleInfo};
use num_traits::FromPrimitive;
use once_cell::sync::Lazy;
use regex::Regex;
@@ -1835,15 +1835,23 @@ async fn create_or_lookup_mailinglist(
mime_parser: &MimeMessage,
) -> Result<Option<(ChatId, Blocked)>> {
static LIST_ID: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(.+)<(.+)>$").unwrap());
eprintln!("List ID is {list_id_header:?}");
let (mut name, listid) = match LIST_ID.captures(list_id_header) {
Some(cap) => (cap[1].trim().to_string(), cap[2].trim().to_string()),
None => (
"".to_string(),
list_id_header
.trim()
.trim_start_matches('<')
.trim_end_matches('>')
.to_string(),
match addrparse(list_id_header)
.ok()
.and_then(|addrlist| addrlist.extract_single_info())
.and_then(|info| info.display_name)
{
Some(name) => name.clone(),
None => list_id_header
.trim()
.trim_start_matches('<')
.trim_end_matches('>')
.to_string(),
},
),
};

View File

@@ -1058,6 +1058,30 @@ async fn test_mailing_list_multiple_names_in_subject() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_with_quotes() -> Result<()> {
let t = TestContext::new_alice().await;
receive_imf(
&t,
b"From: Foo Bar <foobar@lists.example.net>\n\
To: Alice <alice@example.org>\n\
Subject: confirm xxyyzz\n\
Message-ID: <3333@example.org>\n\
List-Id: \"Mailing list for \\\"Foo Bar\\\"\"\n\
\t<foo-bar.lists.example.net>\n\
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
\n\
hello\n",
false,
)
.await?;
let msg = t.get_last_msg().await;
let chat_id = msg.get_chat_id();
let chat = Chat::load_from_db(&t, chat_id).await?;
assert_eq!(chat.name, "Mailing list for \"Foo Bar\"");
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_majordomo_mailing_list() -> Result<()> {
let t = TestContext::new_alice().await;