Parse standard ndns (e.g. Gmail)

This commit is contained in:
Hocuri
2020-06-04 18:13:01 +02:00
parent e08e817988
commit f02c17cae4
4 changed files with 365 additions and 9 deletions

View File

@@ -2324,4 +2324,52 @@ mod tests {
"Carl"
);
}
#[async_std::test]
async fn test_parse_ndn() {
let t = dummy_context().await;
t.ctx
.set_config(Config::Addr, Some("alice@example.org"))
.await
.unwrap();
t.ctx
.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
.await
.unwrap();
t.ctx
.set_config(Config::Configured, Some("1"))
.await
.unwrap();
dc_receive_imf(
&t.ctx,
b"From: alice@example.org\n\
To: assidhfaaspocwaeofi@gmail.com\n\
Subject: foo\n\
Message-ID: <CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com>\n\
Chat-Version: 1.0\n\
Chat-Disposition-Notification-To: alice@example.org\n\
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
\n\
hello\n",
"INBOX",
1,
false,
)
.await
.unwrap();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
let msg_id = chats.get_msg_id(0).unwrap();
let raw = include_bytes!("../test-data/message/gmail_ndn.eml");
dc_receive_imf(&t.ctx, raw, "INBOX", 1, false)
.await
.unwrap();
assert_eq!(
Message::load_from_db(&t.ctx, msg_id).await.unwrap().state,
MessageState::OutFailed
);
}
}

View File

@@ -1384,6 +1384,28 @@ pub async fn mdn_from_ext(
None
}
pub async fn ndn_from_ext(
context: &Context,
from_id: u32,
rfc724_mid: &str,
error: impl AsRef<str>,
) {
if from_id <= DC_MSG_ID_LAST_SPECIAL || rfc724_mid.is_empty() {
return;
}
match rfc724_mid_exists(context, rfc724_mid).await {
Ok(Some((_, _, msg_id))) => {
set_msg_failed(context, msg_id, Some(error)).await;
}
Ok(None) => info!(
context,
"Failed to select NDN, could not find failed msg {}", rfc724_mid
),
Err(e) => info!(context, "Failed to select NDN {:?}", e),
}
}
/// The number of messages assigned to real chat (!=deaddrop, !=trash)
pub async fn get_real_msg_cnt(context: &Context) -> i32 {
match context

View File

@@ -54,6 +54,7 @@ pub struct MimeMessage {
pub(crate) user_avatar: Option<AvatarAction>,
pub(crate) group_avatar: Option<AvatarAction>,
pub(crate) reports: Vec<Report>,
pub(crate) failed_msg: Option<String>,
}
#[derive(Debug, PartialEq)]
@@ -182,6 +183,7 @@ impl MimeMessage {
message_kml: None,
user_avatar: None,
group_avatar: None,
failed_msg: None,
};
parser.parse_mime_recursive(context, &mail).await?;
parser.parse_headers(context)?;
@@ -550,8 +552,8 @@ impl MimeMessage {
(mime::MULTIPART, "report") => {
/* RFC 6522: the first part is for humans, the second for machines */
if mail.subparts.len() >= 2 {
if let Some(report_type) = mail.ctype.params.get("report-type") {
if report_type == "disposition-notification" {
match mail.ctype.params.get("report-type").map(|s| s as &str) {
Some("disposition-notification") => {
if let Some(report) = self.process_report(context, mail)? {
self.reports.push(report);
}
@@ -565,13 +567,24 @@ impl MimeMessage {
self.parts.push(part);
any_part_added = true;
} else {
/* eg. `report-type=delivery-status`;
maybe we should show them as a little error icon */
}
Some("delivery-status") => {
if let Some(report) = self.process_delivery_status(context, mail)? {
self.failed_msg = Some(report);
}
let mut part = Part::default();
part.typ = Viewtype::Unknown;
self.parts.push(part);
any_part_added = true;
}
Some(_) => {
if let Some(first) = mail.subparts.iter().next() {
any_part_added = self.parse_mime_recursive(context, first).await?;
}
}
None => {}
}
}
}
@@ -842,10 +855,6 @@ impl MimeMessage {
/// Handle reports (only MDNs for now)
pub async fn handle_reports(&self, context: &Context, from_id: u32, sent_timestamp: i64) {
if self.reports.is_empty() {
return;
}
for report in &self.reports {
for original_message_id in
std::iter::once(&report.original_message_id).chain(&report.additional_message_ids)
@@ -858,6 +867,41 @@ impl MimeMessage {
}
}
}
if let Some(original_message_id) = &self.failed_msg {
message::ndn_from_ext(context, from_id, original_message_id, "TODO error message").await
}
}
fn process_delivery_status(
&self,
context: &Context,
report: &mailparse::ParsedMail<'_>,
) -> Result<Option<String>> {
// parse as mailheaders
if let Some(original_msg) = report
.subparts
.iter()
.find(|p| p.ctype.mimetype == "message/rfc822")
{
let report_body = original_msg.get_body_raw()?;
let (report_fields, _) = mailparse::parse_headers(&report_body)?;
if let Some(original_message_id) = report_fields
.get_header_value(HeaderDef::MessageId)
.and_then(|v| parse_message_id(&v).ok())
{
return Ok(Some(original_message_id));
}
warn!(
context,
"ignoring unknown ndn-notification, Message-Id: {:?}",
report_fields.get_header_value(HeaderDef::MessageId)
);
}
Ok(None)
}
}