mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 01:16:31 +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\
|
AUTOCRYPT-SETUP-MESSAGE\
|
||||||
)])";
|
)])";
|
||||||
const DELETE_CHECK_FLAGS: &str = "(UID BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])";
|
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 JUST_UID: &str = "(UID)";
|
||||||
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
||||||
const SELECT_ALL: &str = "1:*";
|
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.
|
/// return Result with (uid_validity, last_seen_uid) tuple.
|
||||||
pub(crate) async fn select_with_uidvalidity(
|
pub(crate) async fn select_with_uidvalidity(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
45
src/job.rs
45
src/job.rs
@@ -102,6 +102,10 @@ pub enum Action {
|
|||||||
MoveMsg = 200,
|
MoveMsg = 200,
|
||||||
DeleteMsgOnImap = 210,
|
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
|
// Jobs in the SMTP-thread, range from DC_SMTP_THREAD..DC_SMTP_THREAD+999
|
||||||
MaybeSendLocations = 5005, // low priority ...
|
MaybeSendLocations = 5005, // low priority ...
|
||||||
MaybeSendLocationsEnded = 5007,
|
MaybeSendLocationsEnded = 5007,
|
||||||
@@ -124,6 +128,7 @@ impl From<Action> for Thread {
|
|||||||
|
|
||||||
Housekeeping => Thread::Imap,
|
Housekeeping => Thread::Imap,
|
||||||
DeleteMsgOnImap => Thread::Imap,
|
DeleteMsgOnImap => Thread::Imap,
|
||||||
|
ResyncFolders => Thread::Imap,
|
||||||
EmptyServer => Thread::Imap,
|
EmptyServer => Thread::Imap,
|
||||||
MarkseenMsgOnImap => Thread::Imap,
|
MarkseenMsgOnImap => Thread::Imap,
|
||||||
MoveMsg => 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 {
|
async fn empty_server(&mut self, context: &Context, imap: &mut Imap) -> Status {
|
||||||
if let Err(err) = imap.connect_configured(context).await {
|
if let Err(err) = imap.connect_configured(context).await {
|
||||||
warn!(context, "could not connect: {:?}", err);
|
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::EmptyServer => job.empty_server(context, connection.inbox()).await,
|
||||||
Action::DeleteMsgOnImap => job.delete_msg_on_imap(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::MarkseenMsgOnImap => job.markseen_msg_on_imap(context, connection.inbox()).await,
|
||||||
Action::MoveMsg => job.move_msg(context, connection.inbox()).await,
|
Action::MoveMsg => job.move_msg(context, connection.inbox()).await,
|
||||||
Action::Housekeeping => {
|
Action::Housekeeping => {
|
||||||
@@ -1043,6 +1087,7 @@ pub async fn add(context: &Context, job: Job) {
|
|||||||
Action::Housekeeping
|
Action::Housekeeping
|
||||||
| Action::EmptyServer
|
| Action::EmptyServer
|
||||||
| Action::DeleteMsgOnImap
|
| Action::DeleteMsgOnImap
|
||||||
|
| Action::ResyncFolders
|
||||||
| Action::MarkseenMsgOnImap
|
| Action::MarkseenMsgOnImap
|
||||||
| Action::MoveMsg => {
|
| Action::MoveMsg => {
|
||||||
info!(context, "interrupt: imap");
|
info!(context, "interrupt: imap");
|
||||||
|
|||||||
Reference in New Issue
Block a user