diff --git a/src/constants.rs b/src/constants.rs index a330d29eb..f6d752ff3 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -135,6 +135,11 @@ pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9; pub const DC_CREATE_MVBOX: usize = 1; +// Flags for empty server job + +pub const DC_EMPTY_MVBOX: u32 = 0x01; +pub const DC_EMPTY_INBOX: u32 = 0x02; + // Flags for configuring IMAP and SMTP servers. // These flags are optional // and may be set together with the username, password etc. diff --git a/src/imap.rs b/src/imap.rs index d8eeed3ac..de7880a53 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -32,6 +32,7 @@ pub enum ImapResult { const PREFETCH_FLAGS: &str = "(UID ENVELOPE)"; const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])"; +const SELECT_ALL: &str = "1:*"; #[derive(Debug)] pub struct Imap { @@ -1241,18 +1242,22 @@ impl Imap { if server_uid == 0 { return true; // might be moved but we don't want to have a stuck job } + let s = server_uid.to_string(); + self.add_flag_finalized_with_set(context, &s, flag) + } + + fn add_flag_finalized_with_set(&self, context: &Context, uid_set: &str, flag: &str) -> bool { if self.should_reconnect() { return false; } if let Some(ref mut session) = &mut *self.session.lock().unwrap() { - let set = format!("{}", server_uid); let query = format!("+FLAGS ({})", flag); - match session.uid_store(&set, &query) { + match session.uid_store(uid_set, &query) { Ok(_) => {} Err(err) => { warn!( context, - "IMAP failed to store: ({}, {}) {:?}", set, query, err + "IMAP failed to store: ({}, {}) {:?}", uid_set, query, err ); } } @@ -1488,6 +1493,30 @@ impl Imap { None } } + + pub fn empty_folder(&self, context: &Context, folder: &str) { + info!(context, "emptying folder {}", folder); + + if folder.is_empty() || self.select_folder(context, Some(&folder)) == 0 { + warn!(context, "Cannot select folder '{}' for emptying", folder); + return; + } + + if !self.add_flag_finalized_with_set(context, SELECT_ALL, "\\Deleted") { + warn!(context, "Cannot empty folder {}", folder); + } else { + // we now trigger expunge to actually delete messages + self.config.write().unwrap().selected_folder_needs_expunge = true; + if self.select_folder::(context, None) == 0 { + warn!( + context, + "could not perform expunge on empty folder {}", folder + ); + } else { + info!(context, "Emptying folder '{}' done.", folder); + } + } + } } /// Try to get the folder meaning by the name of the folder only used if the server does not support XLIST. diff --git a/src/job.rs b/src/job.rs index 34d614f60..22e3d9929 100644 --- a/src/job.rs +++ b/src/job.rs @@ -44,6 +44,7 @@ pub enum Action { // Jobs in the INBOX-thread, range from DC_IMAP_THREAD..DC_IMAP_THREAD+999 Housekeeping = 105, // low priority ... + EmptyServer = 107, DeleteMsgOnImap = 110, MarkseenMdnOnImap = 120, MarkseenMsgOnImap = 130, @@ -75,6 +76,7 @@ impl From for Thread { Housekeeping => Thread::Imap, DeleteMsgOnImap => Thread::Imap, + EmptyServer => Thread::Imap, MarkseenMdnOnImap => Thread::Imap, MarkseenMsgOnImap => Thread::Imap, MoveMsg => Thread::Imap, @@ -289,6 +291,22 @@ impl Job { } } + #[allow(non_snake_case)] + fn do_DC_JOB_EMPTY_SERVER(&mut self, context: &Context) { + let inbox = context.inbox.read().unwrap(); + if self.foreign_id & DC_EMPTY_MVBOX > 0 { + if let Some(mvbox_folder) = context + .sql + .get_raw_config(context, "configured_mvbox_folder") + { + inbox.empty_folder(context, &mvbox_folder); + } + } + if self.foreign_id & DC_EMPTY_INBOX > 0 { + inbox.empty_folder(context, "INBOX"); + } + } + #[allow(non_snake_case)] fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) { let inbox = context.inbox.read().unwrap(); @@ -776,6 +794,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) { warn!(context, "Unknown job id found"); } Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context), + Action::EmptyServer => job.do_DC_JOB_EMPTY_SERVER(context), Action::DeleteMsgOnImap => job.do_DC_JOB_DELETE_MSG_ON_IMAP(context), Action::MarkseenMsgOnImap => job.do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context), Action::MarkseenMdnOnImap => job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context),