mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 17:36:29 +03:00
add hop_info (#2751)
This commit is contained in:
@@ -1116,7 +1116,7 @@ INSERT INTO msgs
|
|||||||
txt, subject, txt_raw, param,
|
txt, subject, txt_raw, param,
|
||||||
bytes, mime_headers, mime_in_reply_to,
|
bytes, mime_headers, mime_in_reply_to,
|
||||||
mime_references, mime_modified, error, ephemeral_timer,
|
mime_references, mime_modified, error, ephemeral_timer,
|
||||||
ephemeral_timestamp, download_state
|
ephemeral_timestamp, download_state, hop_info
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
?, ?, ?, ?,
|
?, ?, ?, ?,
|
||||||
@@ -1125,7 +1125,7 @@ INSERT INTO msgs
|
|||||||
?, ?, ?, ?,
|
?, ?, ?, ?,
|
||||||
?, ?, ?, ?,
|
?, ?, ?, ?,
|
||||||
?, ?, ?, ?,
|
?, ?, ?, ?,
|
||||||
?
|
?, ?
|
||||||
);
|
);
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
@@ -1199,6 +1199,7 @@ INSERT INTO msgs
|
|||||||
} else {
|
} else {
|
||||||
DownloadState::Done
|
DownloadState::Done
|
||||||
},
|
},
|
||||||
|
mime_parser.hop_info
|
||||||
])?;
|
])?;
|
||||||
let row_id = conn.last_insert_rowid();
|
let row_id = conn.last_insert_rowid();
|
||||||
|
|
||||||
|
|||||||
119
src/dc_tools.rs
119
src/dc_tools.rs
@@ -5,6 +5,7 @@ use core::cmp::{max, min};
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
use std::str::from_utf8;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
@@ -14,6 +15,9 @@ use async_std::{fs, io};
|
|||||||
|
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
|
use mailparse::dateparse;
|
||||||
|
use mailparse::headers::Headers;
|
||||||
|
use mailparse::MailHeaderMap;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
use crate::chat::{add_device_msg, add_device_msg_with_importance};
|
use crate::chat::{add_device_msg, add_device_msg_with_importance};
|
||||||
@@ -670,13 +674,126 @@ pub fn remove_subject_prefix(last_subject: &str) -> String {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Types and methods to create hop-info for message-info
|
||||||
|
|
||||||
|
fn extract_address_from_receive_header<'a>(header: &'a str, start: &str) -> Option<&'a str> {
|
||||||
|
let header_len = header.len();
|
||||||
|
header.find(start).and_then(|mut begin| {
|
||||||
|
begin += start.len();
|
||||||
|
let end = header
|
||||||
|
.get(begin..)?
|
||||||
|
.find(|c: char| c.is_whitespace())
|
||||||
|
.unwrap_or(header_len);
|
||||||
|
header.get(begin..begin + end)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_receive_header(header: &str) -> String {
|
||||||
|
let header = header.replace(&['\r', '\n'][..], "");
|
||||||
|
let mut hop_info = String::from("Hop: ");
|
||||||
|
|
||||||
|
if let Some(from) = extract_address_from_receive_header(&header, "from ") {
|
||||||
|
hop_info += &format!("From: {}; ", from.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(by) = extract_address_from_receive_header(&header, "by ") {
|
||||||
|
hop_info += &format!("By: {}; ", by.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(date) = dateparse(&header) {
|
||||||
|
// In tests, use the UTC timezone so that the test is reproducible
|
||||||
|
#[cfg(test)]
|
||||||
|
let date_obj = chrono::Utc.timestamp(date, 0);
|
||||||
|
#[cfg(not(test))]
|
||||||
|
let date_obj = Local.timestamp(date, 0);
|
||||||
|
|
||||||
|
hop_info += &format!("Date: {}", date_obj.to_rfc2822());
|
||||||
|
};
|
||||||
|
|
||||||
|
hop_info
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parses "receive"-headers
|
||||||
|
pub(crate) fn parse_receive_headers(headers: &Headers) -> String {
|
||||||
|
headers
|
||||||
|
.get_all_headers("Received")
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter_map(|header_map_item| from_utf8(header_map_item.get_value_raw()).ok())
|
||||||
|
.map(parse_receive_header)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(clippy::indexing_slicing)]
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::test_utils::TestContext;
|
use crate::{
|
||||||
|
config::Config, dc_receive_imf::dc_receive_imf, message::get_msg_info,
|
||||||
|
test_utils::TestContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_receive_headers() {
|
||||||
|
// Test `parse_receive_headers()` with some more-or-less random emails from the test-data
|
||||||
|
let raw = include_bytes!("../test-data/message/mail_with_cc.txt");
|
||||||
|
let expected =
|
||||||
|
"Hop: From: localhost; By: hq5.merlinux.eu; Date: Sat, 14 Sep 2019 17:00:22 +0000\n\
|
||||||
|
Hop: From: hq5.merlinux.eu; By: hq5.merlinux.eu; Date: Sat, 14 Sep 2019 17:00:25 +0000";
|
||||||
|
check_parse_receive_headers(raw, expected);
|
||||||
|
|
||||||
|
let raw = include_bytes!("../test-data/message/wrong-html.eml");
|
||||||
|
let expected =
|
||||||
|
"Hop: From: oxbsltgw18.schlund.de; By: mrelayeu.kundenserver.de; Date: Thu, 06 Aug 2020 16:40:31 +0000\n\
|
||||||
|
Hop: From: mout.kundenserver.de; By: dd37930.kasserver.com; Date: Thu, 06 Aug 2020 16:40:32 +0000";
|
||||||
|
check_parse_receive_headers(raw, expected);
|
||||||
|
|
||||||
|
let raw = include_bytes!("../test-data/message/posteo_ndn.eml");
|
||||||
|
let expected =
|
||||||
|
"Hop: By: mout01.posteo.de; Date: Tue, 09 Jun 2020 18:44:22 +0000\n\
|
||||||
|
Hop: From: mout01.posteo.de; By: mx04.posteo.de; Date: Tue, 09 Jun 2020 18:44:22 +0000\n\
|
||||||
|
Hop: From: mx04.posteo.de; By: mailin06.posteo.de; Date: Tue, 09 Jun 2020 18:44:23 +0000\n\
|
||||||
|
Hop: From: mailin06.posteo.de; By: proxy02.posteo.de; Date: Tue, 09 Jun 2020 18:44:23 +0000\n\
|
||||||
|
Hop: From: proxy02.posteo.de; By: proxy02.posteo.name; Date: Tue, 09 Jun 2020 18:44:23 +0000\n\
|
||||||
|
Hop: From: proxy02.posteo.name; By: dovecot03.posteo.local; Date: Tue, 09 Jun 2020 18:44:24 +0000";
|
||||||
|
check_parse_receive_headers(raw, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_parse_receive_headers(raw: &[u8], expected: &str) {
|
||||||
|
let mail = mailparse::parse_mail(raw).unwrap();
|
||||||
|
let hop_info = parse_receive_headers(&mail.get_headers());
|
||||||
|
assert_eq!(hop_info, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_parse_receive_headers_integration() -> anyhow::Result<()> {
|
||||||
|
let t = TestContext::new_alice().await;
|
||||||
|
t.set_config(Config::ShowEmails, Some("2")).await?;
|
||||||
|
let raw = include_bytes!("../test-data/message/mail_with_cc.txt");
|
||||||
|
dc_receive_imf(&t, raw, "INBOX", 1, false).await.unwrap();
|
||||||
|
let g = t.get_last_msg().await;
|
||||||
|
|
||||||
|
let expected = r"State: Fresh
|
||||||
|
|
||||||
|
hi
|
||||||
|
|
||||||
|
Message-ID: 2dfdbde7@example.org
|
||||||
|
Last seen as: INBOX/1
|
||||||
|
|
||||||
|
Hop: From: localhost; By: hq5.merlinux.eu; Date: Sat, 14 Sep 2019 17:00:22 +0000
|
||||||
|
Hop: From: hq5.merlinux.eu; By: hq5.merlinux.eu; Date: Sat, 14 Sep 2019 17:00:25 +0000";
|
||||||
|
let result = get_msg_info(&t, g.id).await.unwrap();
|
||||||
|
// little hack to ignore the first row of a parsed email because it contains a
|
||||||
|
// send time that depends and the test runtime which makes it impossible to
|
||||||
|
// compare with a static string
|
||||||
|
let capped_result = &result[result.find("State").unwrap()..];
|
||||||
|
|
||||||
|
assert_eq!(expected, capped_result);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rust_ftoa() {
|
fn test_rust_ftoa() {
|
||||||
|
|||||||
@@ -1169,9 +1169,16 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
|
|||||||
}
|
}
|
||||||
if let Some(ref server_folder) = msg.server_folder {
|
if let Some(ref server_folder) = msg.server_folder {
|
||||||
if !server_folder.is_empty() {
|
if !server_folder.is_empty() {
|
||||||
ret += &format!("\nLast seen as: {}/{}", server_folder, msg.server_uid);
|
ret += &format!("\nLast seen as: {}/{}\n", server_folder, msg.server_uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let hop_info: Option<String> = context
|
||||||
|
.sql
|
||||||
|
.query_get_value("SELECT hop_info FROM msgs WHERE id=?;", paramsv![msg_id])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
ret += "\n";
|
||||||
|
ret += &hop_info.unwrap_or_else(|| "No Hop Info".to_owned());
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use crate::blob::BlobObject;
|
|||||||
use crate::constants::{Viewtype, DC_DESIRED_TEXT_LEN, DC_ELLIPSIS};
|
use crate::constants::{Viewtype, DC_DESIRED_TEXT_LEN, DC_ELLIPSIS};
|
||||||
use crate::contact::addr_normalize;
|
use crate::contact::addr_normalize;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::dc_tools::{dc_get_filemeta, dc_truncate};
|
use crate::dc_tools::{dc_get_filemeta, dc_truncate, parse_receive_headers};
|
||||||
use crate::dehtml::dehtml;
|
use crate::dehtml::dehtml;
|
||||||
use crate::e2ee;
|
use crate::e2ee;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
@@ -82,6 +82,8 @@ pub struct MimeMessage {
|
|||||||
/// This is non-empty only if the message was actually encrypted. It is used
|
/// This is non-empty only if the message was actually encrypted. It is used
|
||||||
/// for e.g. late-parsing HTML.
|
/// for e.g. late-parsing HTML.
|
||||||
pub decoded_data: Vec<u8>,
|
pub decoded_data: Vec<u8>,
|
||||||
|
|
||||||
|
pub(crate) hop_info: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -294,6 +296,7 @@ impl MimeMessage {
|
|||||||
footer: None,
|
footer: None,
|
||||||
is_mime_modified: false,
|
is_mime_modified: false,
|
||||||
decoded_data: Vec::new(),
|
decoded_data: Vec::new(),
|
||||||
|
hop_info: parse_receive_headers(&mail.get_headers()),
|
||||||
};
|
};
|
||||||
|
|
||||||
match partial {
|
match partial {
|
||||||
|
|||||||
@@ -497,7 +497,10 @@ item TEXT DEFAULT '');"#,
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
if dbversion < 81 {
|
||||||
|
sql.execute_migration("ALTER TABLE msgs ADD COLUMN hop_info TEXT;", 81)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
Ok((
|
Ok((
|
||||||
recalc_fingerprints,
|
recalc_fingerprints,
|
||||||
update_icons,
|
update_icons,
|
||||||
|
|||||||
Reference in New Issue
Block a user