mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
Add a job to resync folder UIDs
This commit is contained in:
committed by
link2xt
parent
763334d0aa
commit
b4c85c534d
@@ -108,6 +108,7 @@ const PREFETCH_FLAGS: &str = "(UID BODY.PEEK[HEADER.FIELDS (\
|
||||
AUTOCRYPT-SETUP-MESSAGE\
|
||||
)])";
|
||||
const DELETE_CHECK_FLAGS: &str = "(UID BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])";
|
||||
const RFC724MID_UID: &str = "(UID BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])";
|
||||
const JUST_UID: &str = "(UID)";
|
||||
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
||||
const SELECT_ALL: &str = "1:*";
|
||||
@@ -517,6 +518,84 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronizes UIDs in the database with UIDs on the server.
|
||||
///
|
||||
/// It is assumed that no operations are taking place on the same
|
||||
/// folder at the moment. Make sure to run it in the same
|
||||
/// thread/task as other network operations on this folder to
|
||||
/// avoid race conditions.
|
||||
pub(crate) async fn resync_folder_uids(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: String,
|
||||
) -> Result<()> {
|
||||
// Collect pairs of UID and Message-ID.
|
||||
let mut msg_ids = BTreeMap::new();
|
||||
|
||||
self.select_folder(context, Some(&folder)).await?;
|
||||
|
||||
let session = if let Some(ref mut session) = &mut self.session {
|
||||
session
|
||||
} else {
|
||||
return Err(Error::NoConnection);
|
||||
};
|
||||
|
||||
match session.uid_fetch("1:*", RFC724MID_UID).await {
|
||||
Ok(mut list) => {
|
||||
while let Some(fetch) = list.next().await {
|
||||
let msg = fetch.map_err(|err| Error::Other(err.to_string()))?;
|
||||
|
||||
// Get Message-ID
|
||||
let message_id = get_fetch_headers(&msg)
|
||||
.and_then(|headers| prefetch_get_message_id(&headers))
|
||||
.ok();
|
||||
|
||||
if let (Some(uid), Some(rfc724_mid)) = (msg.uid, message_id) {
|
||||
msg_ids.insert(uid, rfc724_mid);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::Other(format!(
|
||||
"Can't resync folder {}: {}",
|
||||
folder, err
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Resync: collected {} message IDs in folder {}",
|
||||
msg_ids.len(),
|
||||
&folder
|
||||
);
|
||||
|
||||
// Write collected UIDs to SQLite database.
|
||||
context
|
||||
.sql
|
||||
.with_conn(move |mut conn| {
|
||||
let conn2 = &mut conn;
|
||||
let tx = conn2.transaction()?;
|
||||
tx.execute(
|
||||
"UPDATE msgs SET server_uid=0 WHERE server_folder=?",
|
||||
params![folder],
|
||||
)?;
|
||||
for (uid, rfc724_mid) in &msg_ids {
|
||||
// This may detect previously undetected moved
|
||||
// messages, so we update server_folder too.
|
||||
tx.execute(
|
||||
"UPDATE msgs \
|
||||
SET server_folder=?,server_uid=? WHERE rfc724_mid=?",
|
||||
params![folder, uid, rfc724_mid],
|
||||
)?;
|
||||
}
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// return Result with (uid_validity, last_seen_uid) tuple.
|
||||
pub(crate) async fn select_with_uidvalidity(
|
||||
&mut self,
|
||||
|
||||
45
src/job.rs
45
src/job.rs
@@ -102,6 +102,10 @@ pub enum Action {
|
||||
MoveMsg = 200,
|
||||
DeleteMsgOnImap = 210,
|
||||
|
||||
// UID synchronization is high-priority to make sure correct UIDs
|
||||
// are used by message moving/deletion.
|
||||
ResyncFolders = 300,
|
||||
|
||||
// Jobs in the SMTP-thread, range from DC_SMTP_THREAD..DC_SMTP_THREAD+999
|
||||
MaybeSendLocations = 5005, // low priority ...
|
||||
MaybeSendLocationsEnded = 5007,
|
||||
@@ -124,6 +128,7 @@ impl From<Action> for Thread {
|
||||
|
||||
Housekeeping => Thread::Imap,
|
||||
DeleteMsgOnImap => Thread::Imap,
|
||||
ResyncFolders => Thread::Imap,
|
||||
EmptyServer => Thread::Imap,
|
||||
MarkseenMsgOnImap => Thread::Imap,
|
||||
MoveMsg => Thread::Imap,
|
||||
@@ -619,6 +624,44 @@ impl Job {
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronizes UIDs for sentbox, inbox and mvbox, in this order.
|
||||
///
|
||||
/// If a copy of the message is present in multiple folders, mvbox
|
||||
/// is preferred to inbox, which is in turn preferred to
|
||||
/// sentbox. This is because in the database it is impossible to
|
||||
/// store multiple UIDs for one message, so we prefer to
|
||||
/// automatically delete messages in the folders managed by Delta
|
||||
/// Chat in contrast to the Sent folder, which is normally managed
|
||||
/// by the user via webmail or another email client.
|
||||
async fn resync_folders(&mut self, context: &Context, imap: &mut Imap) -> Status {
|
||||
if let Err(err) = imap.connect_configured(context).await {
|
||||
warn!(context, "could not connect: {:?}", err);
|
||||
return Status::RetryLater;
|
||||
}
|
||||
|
||||
if let Some(sentbox_folder) = &context.get_config(Config::ConfiguredSentboxFolder).await {
|
||||
job_try!(
|
||||
imap.resync_folder_uids(context, sentbox_folder.to_string())
|
||||
.await
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(inbox_folder) = &context.get_config(Config::ConfiguredInboxFolder).await {
|
||||
job_try!(
|
||||
imap.resync_folder_uids(context, inbox_folder.to_string())
|
||||
.await
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(mvbox_folder) = &context.get_config(Config::ConfiguredMvboxFolder).await {
|
||||
job_try!(
|
||||
imap.resync_folder_uids(context, mvbox_folder.to_string())
|
||||
.await
|
||||
);
|
||||
}
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
|
||||
async fn empty_server(&mut self, context: &Context, imap: &mut Imap) -> Status {
|
||||
if let Err(err) = imap.connect_configured(context).await {
|
||||
warn!(context, "could not connect: {:?}", err);
|
||||
@@ -986,6 +1029,7 @@ async fn perform_job_action(
|
||||
}
|
||||
Action::EmptyServer => job.empty_server(context, connection.inbox()).await,
|
||||
Action::DeleteMsgOnImap => job.delete_msg_on_imap(context, connection.inbox()).await,
|
||||
Action::ResyncFolders => job.resync_folders(context, connection.inbox()).await,
|
||||
Action::MarkseenMsgOnImap => job.markseen_msg_on_imap(context, connection.inbox()).await,
|
||||
Action::MoveMsg => job.move_msg(context, connection.inbox()).await,
|
||||
Action::Housekeeping => {
|
||||
@@ -1043,6 +1087,7 @@ pub async fn add(context: &Context, job: Job) {
|
||||
Action::Housekeeping
|
||||
| Action::EmptyServer
|
||||
| Action::DeleteMsgOnImap
|
||||
| Action::ResyncFolders
|
||||
| Action::MarkseenMsgOnImap
|
||||
| Action::MoveMsg => {
|
||||
info!(context, "interrupt: imap");
|
||||
|
||||
Reference in New Issue
Block a user