Skip to content

Commit 3c25e4b

Browse files
committed
api: add clear_all_relay_storage API
1 parent 8cd06bb commit 3c25e4b

4 files changed

Lines changed: 108 additions & 4 deletions

File tree

deltachat-jsonrpc/src/api.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,15 @@ impl CommandApi {
318318
Ok(())
319319
}
320320

321+
/// Requests to clear storage on all chatmail relays.
322+
///
323+
/// I/O must be started for this request to take effect.
324+
async fn clear_all_relay_storage(&self, account_id: u32) -> Result<()> {
325+
let ctx = self.get_context(account_id).await?;
326+
ctx.clear_all_relay_storage().await?;
327+
Ok(())
328+
}
329+
321330
/// Get top-level info for an account.
322331
async fn get_account_info(&self, account_id: u32) -> Result<Account> {
323332
let context_option = self.accounts.read().await.get_account(account_id);

src/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,15 @@ impl Context {
568568
}
569569
}
570570

571+
/// Requests deletion of all messages from chatmail relays.
572+
///
573+
/// Non-chatmail relays are excluded
574+
/// to avoid accidentally deleting emails
575+
/// from shared inboxes.
576+
pub async fn clear_all_relay_storage(&self) -> Result<()> {
577+
self.scheduler.clear_all_relay_storage().await
578+
}
579+
571580
/// Restarts the IO scheduler if it was running before
572581
/// when it is not running this is an no-op
573582
pub async fn restart_io_if_running(&self) {

src/imap.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,29 @@ impl Session {
945945
Ok(())
946946
}
947947

948+
/// Deletes all messages from IMAP folder.
949+
pub(crate) async fn delete_all_messages(
950+
&mut self,
951+
context: &Context,
952+
folder: &str,
953+
) -> Result<()> {
954+
let transport_id = self.transport_id();
955+
956+
if self.select_with_uidvalidity(context, folder).await? {
957+
self.add_flag_finalized_with_set("1:*", "\\Deleted").await?;
958+
self.selected_folder_needs_expunge = true;
959+
context
960+
.sql
961+
.execute(
962+
"DELETE FROM imap WHERE transport_id=? AND folder=?",
963+
(transport_id, folder),
964+
)
965+
.await?;
966+
}
967+
968+
Ok(())
969+
}
970+
948971
/// Moves batch of messages identified by their UID from the currently
949972
/// selected folder to the target folder.
950973
async fn move_message_batch(

src/scheduler.rs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,16 @@ impl SchedulerState {
250250
}
251251
}
252252

253+
pub(crate) async fn clear_all_relay_storage(&self) -> Result<()> {
254+
let inner = self.inner.read().await;
255+
if let InnerSchedulerState::Started(ref scheduler) = *inner {
256+
scheduler.clear_all_relay_storage();
257+
Ok(())
258+
} else {
259+
bail!("IO is not started");
260+
}
261+
}
262+
253263
pub(crate) async fn interrupt_smtp(&self) {
254264
let inner = self.inner.read().await;
255265
if let InnerSchedulerState::Started(ref scheduler) = *inner {
@@ -348,6 +358,7 @@ async fn inbox_loop(
348358
let ImapConnectionHandlers {
349359
mut connection,
350360
stop_token,
361+
clear_storage_request_receiver,
351362
} = inbox_handlers;
352363

353364
let transport_id = connection.transport_id();
@@ -386,7 +397,14 @@ async fn inbox_loop(
386397
}
387398
};
388399

389-
match inbox_fetch_idle(&ctx, &mut connection, session).await {
400+
match inbox_fetch_idle(
401+
&ctx,
402+
&mut connection,
403+
session,
404+
&clear_storage_request_receiver,
405+
)
406+
.await
407+
{
390408
Err(err) => warn!(
391409
ctx,
392410
"Transport {transport_id}: Failed inbox fetch_idle: {err:#}."
@@ -407,11 +425,29 @@ async fn inbox_loop(
407425
.await;
408426
}
409427

410-
async fn inbox_fetch_idle(ctx: &Context, imap: &mut Imap, mut session: Session) -> Result<Session> {
428+
async fn inbox_fetch_idle(
429+
ctx: &Context,
430+
imap: &mut Imap,
431+
mut session: Session,
432+
clear_storage_request_receiver: &Receiver<()>,
433+
) -> Result<Session> {
411434
let transport_id = session.transport_id();
412435

436+
// Clear IMAP storage on request.
437+
//
438+
// Only doing this for chatmail relays to avoid
439+
// accidentally deleting all emails in a shared mailbox.
440+
let should_clear_imap_storage =
441+
clear_storage_request_receiver.try_recv().is_ok() && session.is_chatmail();
442+
if should_clear_imap_storage {
443+
info!(ctx, "Transport {transport_id}: Clearing IMAP storage.");
444+
session.delete_all_messages(ctx, &imap.folder).await?;
445+
}
446+
413447
// Update quota no more than once a minute.
414-
if ctx.quota_needs_update(session.transport_id(), 60).await
448+
//
449+
// Always update if we just cleared IMAP storage.
450+
if (ctx.quota_needs_update(session.transport_id(), 60).await || should_clear_imap_storage)
415451
&& let Err(err) = ctx.update_recent_quota(&mut session, &imap.folder).await
416452
{
417453
warn!(
@@ -737,6 +773,12 @@ impl Scheduler {
737773
}
738774
}
739775

776+
fn clear_all_relay_storage(&self) {
777+
for b in &self.inboxes {
778+
b.conn_state.clear_relay_storage();
779+
}
780+
}
781+
740782
fn interrupt_smtp(&self) {
741783
self.smtp.interrupt();
742784
}
@@ -870,6 +912,13 @@ struct SmtpConnectionHandlers {
870912
#[derive(Debug)]
871913
pub(crate) struct ImapConnectionState {
872914
state: ConnectionState,
915+
916+
/// Channel to request clearing the folder.
917+
///
918+
/// IMAP loop receiving this should clear the folder
919+
/// on the next iteration if IMAP server is a chatmail relay
920+
/// and otherwise ignore the request.
921+
clear_storage_request_sender: Sender<()>,
873922
}
874923

875924
impl ImapConnectionState {
@@ -881,11 +930,13 @@ impl ImapConnectionState {
881930
) -> Result<(Self, ImapConnectionHandlers)> {
882931
let stop_token = CancellationToken::new();
883932
let (idle_interrupt_sender, idle_interrupt_receiver) = channel::bounded(1);
933+
let (clear_storage_request_sender, clear_storage_request_receiver) = channel::bounded(1);
884934

885935
let handlers = ImapConnectionHandlers {
886936
connection: Imap::new(context, transport_id, login_param, idle_interrupt_receiver)
887937
.await?,
888938
stop_token: stop_token.clone(),
939+
clear_storage_request_receiver,
889940
};
890941

891942
let state = ConnectionState {
@@ -894,7 +945,10 @@ impl ImapConnectionState {
894945
connectivity: handlers.connection.connectivity.clone(),
895946
};
896947

897-
let conn = ImapConnectionState { state };
948+
let conn = ImapConnectionState {
949+
state,
950+
clear_storage_request_sender,
951+
};
898952

899953
Ok((conn, handlers))
900954
}
@@ -908,10 +962,19 @@ impl ImapConnectionState {
908962
fn stop(&self) {
909963
self.state.stop();
910964
}
965+
966+
/// Requests clearing relay storage and interrupts the inbox.
967+
fn clear_relay_storage(&self) {
968+
self.clear_storage_request_sender.try_send(()).ok();
969+
self.state.interrupt();
970+
}
911971
}
912972

913973
#[derive(Debug)]
914974
struct ImapConnectionHandlers {
915975
connection: Imap,
916976
stop_token: CancellationToken,
977+
978+
/// Channel receiver to get requests to clear IMAP storage.
979+
pub(crate) clear_storage_request_receiver: Receiver<()>,
917980
}

0 commit comments

Comments
 (0)