mirror of
https://github.com/chatmail/core.git
synced 2026-05-20 15:26:30 +03:00
feat: do not collect email addresses from messages after configuration
This can only result in adding unencrypted email-contacts and we do not want to encourage creating unencrypted chats.
This commit is contained in:
@@ -175,11 +175,6 @@ pub enum Config {
|
|||||||
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
|
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
|
||||||
MediaQuality,
|
MediaQuality,
|
||||||
|
|
||||||
/// If set to "1", then existing messages are considered to be already fetched.
|
|
||||||
/// This flag is reset after successful configuration.
|
|
||||||
#[strum(props(default = "1"))]
|
|
||||||
FetchedExistingMsgs,
|
|
||||||
|
|
||||||
/// Timer in seconds after which the message is deleted from the
|
/// Timer in seconds after which the message is deleted from the
|
||||||
/// server.
|
/// server.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use percent_encoding::utf8_percent_encode;
|
|||||||
use server_params::{ServerParams, expand_param_vector};
|
use server_params::{ServerParams, expand_param_vector};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use crate::config::{self, Config};
|
use crate::config::Config;
|
||||||
use crate::constants::NON_ALPHANUMERIC_WITHOUT_DOT;
|
use crate::constants::NON_ALPHANUMERIC_WITHOUT_DOT;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::imap::Imap;
|
use crate::imap::Imap;
|
||||||
@@ -631,8 +631,6 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Option<&'
|
|||||||
|
|
||||||
progress!(ctx, 920);
|
progress!(ctx, 920);
|
||||||
|
|
||||||
ctx.set_config_internal(Config::FetchedExistingMsgs, config::from_bool(false))
|
|
||||||
.await?;
|
|
||||||
ctx.scheduler.interrupt_inbox().await;
|
ctx.scheduler.interrupt_inbox().await;
|
||||||
|
|
||||||
progress!(ctx, 940);
|
progress!(ctx, 940);
|
||||||
|
|||||||
@@ -946,12 +946,6 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.insert("secondary_addrs", secondary_addrs);
|
res.insert("secondary_addrs", secondary_addrs);
|
||||||
res.insert(
|
|
||||||
"fetched_existing_msgs",
|
|
||||||
self.get_config_bool(Config::FetchedExistingMsgs)
|
|
||||||
.await?
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
res.insert(
|
res.insert(
|
||||||
"show_emails",
|
"show_emails",
|
||||||
self.get_config_int(Config::ShowEmails).await?.to_string(),
|
self.get_config_int(Config::ShowEmails).await?.to_string(),
|
||||||
|
|||||||
132
src/imap.rs
132
src/imap.rs
@@ -16,7 +16,6 @@ use std::{
|
|||||||
use anyhow::{Context as _, Result, bail, ensure, format_err};
|
use anyhow::{Context as _, Result, bail, ensure, format_err};
|
||||||
use async_channel::{self, Receiver, Sender};
|
use async_channel::{self, Receiver, Sender};
|
||||||
use async_imap::types::{Fetch, Flag, Name, NameAttribute, UnsolicitedResponse};
|
use async_imap::types::{Fetch, Flag, Name, NameAttribute, UnsolicitedResponse};
|
||||||
use deltachat_contact_tools::ContactAddress;
|
|
||||||
use futures::{FutureExt as _, TryStreamExt};
|
use futures::{FutureExt as _, TryStreamExt};
|
||||||
use futures_lite::FutureExt;
|
use futures_lite::FutureExt;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
@@ -30,7 +29,7 @@ use crate::chat::{self, ChatId, ChatIdBlocked, add_device_msg};
|
|||||||
use crate::chatlist_events;
|
use crate::chatlist_events;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::{self, Blocked, DC_VERSION_STR, ShowEmails};
|
use crate::constants::{self, Blocked, DC_VERSION_STR, ShowEmails};
|
||||||
use crate::contact::{Contact, ContactId, Modifier, Origin};
|
use crate::contact::ContactId;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||||
@@ -59,7 +58,6 @@ pub mod select_folder;
|
|||||||
pub(crate) mod session;
|
pub(crate) mod session;
|
||||||
|
|
||||||
use client::{Client, determine_capabilities};
|
use client::{Client, determine_capabilities};
|
||||||
use mailparse::SingleInfo;
|
|
||||||
use session::Session;
|
use session::Session;
|
||||||
|
|
||||||
pub(crate) const GENERATED_PREFIX: &str = "GEN_";
|
pub(crate) const GENERATED_PREFIX: &str = "GEN_";
|
||||||
@@ -837,27 +835,6 @@ impl Imap {
|
|||||||
|
|
||||||
Ok((read_cnt, fetch_more))
|
Ok((read_cnt, fetch_more))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the recipients from old emails sent by the user and add them as contacts.
|
|
||||||
/// This way, we can already offer them some email addresses they can write to.
|
|
||||||
///
|
|
||||||
/// Then, Fetch the last messages DC_FETCH_EXISTING_MSGS_COUNT emails from the server
|
|
||||||
/// and show them in the chat list.
|
|
||||||
pub(crate) async fn fetch_existing_msgs(
|
|
||||||
&mut self,
|
|
||||||
context: &Context,
|
|
||||||
session: &mut Session,
|
|
||||||
) -> Result<()> {
|
|
||||||
add_all_recipients_as_contacts(context, session, Config::ConfiguredMvboxFolder)
|
|
||||||
.await
|
|
||||||
.context("failed to get recipients from the movebox")?;
|
|
||||||
add_all_recipients_as_contacts(context, session, Config::ConfiguredInboxFolder)
|
|
||||||
.await
|
|
||||||
.context("failed to get recipients from the inbox")?;
|
|
||||||
|
|
||||||
info!(context, "Done fetching existing messages.");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
@@ -1312,41 +1289,6 @@ impl Session {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the from, to and bcc addresses from all existing outgoing emails.
|
|
||||||
pub async fn get_all_recipients(&mut self, context: &Context) -> Result<Vec<SingleInfo>> {
|
|
||||||
let mut uids: Vec<_> = self
|
|
||||||
.uid_search(get_imap_self_sent_search_command(context).await?)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
uids.sort_unstable();
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
|
||||||
for (_, uid_set) in build_sequence_sets(&uids)? {
|
|
||||||
let mut list = self
|
|
||||||
.uid_fetch(uid_set, "(UID BODY.PEEK[HEADER.FIELDS (FROM TO CC BCC)])")
|
|
||||||
.await
|
|
||||||
.context("IMAP Could not fetch")?;
|
|
||||||
|
|
||||||
while let Some(msg) = list.try_next().await? {
|
|
||||||
match get_fetch_headers(&msg) {
|
|
||||||
Ok(headers) => {
|
|
||||||
if let Some(from) = mimeparser::get_from(&headers)
|
|
||||||
&& context.is_self_addr(&from.addr).await?
|
|
||||||
{
|
|
||||||
result.extend(mimeparser::get_recipients(&headers));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(context, "{}", err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a list of messages by server UID.
|
/// Fetches a list of messages by server UID.
|
||||||
///
|
///
|
||||||
/// Sends pairs of UID and info about each downloaded message to the provided channel.
|
/// Sends pairs of UID and info about each downloaded message to the provided channel.
|
||||||
@@ -2467,18 +2409,6 @@ async fn get_modseq(context: &Context, transport_id: u32, folder: &str) -> Resul
|
|||||||
.unwrap_or(0))
|
.unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the imap search expression for all self-sent mails (for all self addresses)
|
|
||||||
pub(crate) async fn get_imap_self_sent_search_command(context: &Context) -> Result<String> {
|
|
||||||
// See https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4 for syntax of SEARCH and OR
|
|
||||||
let mut search_command = format!("FROM \"{}\"", context.get_primary_self_addr().await?);
|
|
||||||
|
|
||||||
for item in context.get_secondary_self_addrs().await? {
|
|
||||||
search_command = format!("OR ({search_command}) (FROM \"{item}\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(search_command)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether to ignore fetching messages from a folder.
|
/// Whether to ignore fetching messages from a folder.
|
||||||
///
|
///
|
||||||
/// This caters for the [`Config::OnlyFetchMvbox`] setting which means mails from folders
|
/// This caters for the [`Config::OnlyFetchMvbox`] setting which means mails from folders
|
||||||
@@ -2551,66 +2481,6 @@ impl std::fmt::Display for UidRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn add_all_recipients_as_contacts(
|
|
||||||
context: &Context,
|
|
||||||
session: &mut Session,
|
|
||||||
folder: Config,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mailbox = if let Some(m) = context.get_config(folder).await? {
|
|
||||||
m
|
|
||||||
} else {
|
|
||||||
info!(
|
|
||||||
context,
|
|
||||||
"Folder {} is not configured, skipping fetching contacts from it.", folder
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let create = false;
|
|
||||||
let folder_exists = session
|
|
||||||
.select_with_uidvalidity(context, &mailbox, create)
|
|
||||||
.await
|
|
||||||
.with_context(|| format!("could not select {mailbox}"))?;
|
|
||||||
if !folder_exists {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let recipients = session
|
|
||||||
.get_all_recipients(context)
|
|
||||||
.await
|
|
||||||
.context("could not get recipients")?;
|
|
||||||
|
|
||||||
let mut any_modified = false;
|
|
||||||
for recipient in recipients {
|
|
||||||
let recipient_addr = match ContactAddress::new(&recipient.addr) {
|
|
||||||
Err(err) => {
|
|
||||||
warn!(
|
|
||||||
context,
|
|
||||||
"Could not add contact for recipient with address {:?}: {:#}",
|
|
||||||
recipient.addr,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Ok(recipient_addr) => recipient_addr,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (_, modified) = Contact::add_or_lookup(
|
|
||||||
context,
|
|
||||||
&recipient.display_name.unwrap_or_default(),
|
|
||||||
&recipient_addr,
|
|
||||||
Origin::OutgoingTo,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
if modified != Modifier::None {
|
|
||||||
any_modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if any_modified {
|
|
||||||
context.emit_event(EventType::ContactsChanged(None));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod imap_tests;
|
mod imap_tests;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::contact::Contact;
|
||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::TestContext;
|
||||||
use crate::transport::add_pseudo_transport;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_folder_meaning_by_name() {
|
fn test_get_folder_meaning_by_name() {
|
||||||
@@ -264,31 +264,6 @@ async fn test_target_folder_setupmsg() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn test_get_imap_search_command() -> Result<()> {
|
|
||||||
let t = TestContext::new_alice().await;
|
|
||||||
assert_eq!(
|
|
||||||
get_imap_self_sent_search_command(&t.ctx).await?,
|
|
||||||
r#"FROM "alice@example.org""#
|
|
||||||
);
|
|
||||||
|
|
||||||
add_pseudo_transport(&t, "alice@another.com").await?;
|
|
||||||
t.ctx.set_primary_self_addr("alice@another.com").await?;
|
|
||||||
assert_eq!(
|
|
||||||
get_imap_self_sent_search_command(&t.ctx).await?,
|
|
||||||
r#"OR (FROM "alice@another.com") (FROM "alice@example.org")"#
|
|
||||||
);
|
|
||||||
|
|
||||||
add_pseudo_transport(&t, "alice@third.com").await?;
|
|
||||||
t.ctx.set_primary_self_addr("alice@third.com").await?;
|
|
||||||
assert_eq!(
|
|
||||||
get_imap_self_sent_search_command(&t.ctx).await?,
|
|
||||||
r#"OR (OR (FROM "alice@third.com") (FROM "alice@another.com")) (FROM "alice@example.org")"#
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uid_grouper() {
|
fn test_uid_grouper() {
|
||||||
// Input: sequence of (rowid: i64, uid: u32, target: String)
|
// Input: sequence of (rowid: i64, uid: u32, target: String)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use tokio_util::sync::CancellationToken;
|
|||||||
use tokio_util::task::TaskTracker;
|
use tokio_util::task::TaskTracker;
|
||||||
|
|
||||||
pub(crate) use self::connectivity::ConnectivityStore;
|
pub(crate) use self::connectivity::ConnectivityStore;
|
||||||
use crate::config::{self, Config};
|
use crate::config::Config;
|
||||||
use crate::contact::{ContactId, RecentlySeenLoop};
|
use crate::contact::{ContactId, RecentlySeenLoop};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::download::{download_known_post_messages_without_pre_message, download_msgs};
|
use crate::download::{download_known_post_messages_without_pre_message, download_msgs};
|
||||||
@@ -477,29 +477,6 @@ async fn inbox_fetch_idle(ctx: &Context, imap: &mut Imap, mut session: Session)
|
|||||||
};
|
};
|
||||||
|
|
||||||
maybe_send_stats(ctx).await.log_err(ctx).ok();
|
maybe_send_stats(ctx).await.log_err(ctx).ok();
|
||||||
match ctx.get_config_bool(Config::FetchedExistingMsgs).await {
|
|
||||||
Ok(fetched_existing_msgs) => {
|
|
||||||
if !fetched_existing_msgs {
|
|
||||||
// Consider it done even if we fail.
|
|
||||||
//
|
|
||||||
// This operation is not critical enough to retry,
|
|
||||||
// especially if the error is persistent.
|
|
||||||
if let Err(err) = ctx
|
|
||||||
.set_config_internal(Config::FetchedExistingMsgs, config::from_bool(true))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
warn!(ctx, "Can't set Config::FetchedExistingMsgs: {:#}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(err) = imap.fetch_existing_msgs(ctx, &mut session).await {
|
|
||||||
warn!(ctx, "Failed to fetch existing messages: {:#}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(ctx, "Can't get Config::FetchedExistingMsgs: {:#}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session
|
session
|
||||||
.update_metadata(ctx)
|
.update_metadata(ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user