Skip to content

Commit 3d9aee1

Browse files
committed
feat: Remove "jobs" from imap_markseen if folder doesn't exist (#5870)
Add a `create` param to `select_with_uidvalidity()` instead of always trying to create the folder and return `Ok(false)` from it if the folder doesn't exist and shouldn't be created, and handle this in `store_seen_flags_on_imap()` by just removing "jobs" from the `imap_markseen` table. Also don't create the folder in other code paths where it's not necessary.
1 parent f1302c3 commit 3d9aee1

File tree

6 files changed

+132
-67
lines changed

6 files changed

+132
-67
lines changed

src/configure.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,9 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Configure
452452
imap.configure_folders(ctx, &mut imap_session, create_mvbox)
453453
.await?;
454454

455+
let create = true;
455456
imap_session
456-
.select_with_uidvalidity(ctx, "INBOX")
457+
.select_with_uidvalidity(ctx, "INBOX", create)
457458
.await
458459
.context("could not read INBOX status")?;
459460

src/download.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::cmp::max;
44
use std::collections::BTreeMap;
55

6-
use anyhow::{anyhow, bail, Result};
6+
use anyhow::{anyhow, bail, ensure, Result};
77
use deltachat_derive::{FromSql, ToSql};
88
use serde::{Deserialize, Serialize};
99

@@ -201,7 +201,11 @@ impl Session {
201201
bail!("Attempt to fetch UID 0");
202202
}
203203

204-
self.select_with_uidvalidity(context, folder).await?;
204+
let create = false;
205+
let folder_exists = self
206+
.select_with_uidvalidity(context, folder, create)
207+
.await?;
208+
ensure!(folder_exists, "No folder {folder}");
205209

206210
// we are connected, and the folder is selected
207211
info!(context, "Downloading message {}/{} fully...", folder, uid);

src/imap.rs

Lines changed: 96 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::{
1313
time::{Duration, UNIX_EPOCH},
1414
};
1515

16-
use anyhow::{bail, format_err, Context as _, Result};
16+
use anyhow::{bail, ensure, format_err, Context as _, Result};
1717
use async_channel::Receiver;
1818
use async_imap::types::{Fetch, Flag, Name, NameAttribute, UnsolicitedResponse};
1919
use deltachat_contact_tools::ContactAddress;
@@ -540,10 +540,14 @@ impl Imap {
540540
return Ok(false);
541541
}
542542

543-
session
544-
.select_with_uidvalidity(context, folder)
543+
let create = false;
544+
let folder_exists = session
545+
.select_with_uidvalidity(context, folder, create)
545546
.await
546547
.with_context(|| format!("Failed to select folder {folder:?}"))?;
548+
if !folder_exists {
549+
return Ok(false);
550+
}
547551

548552
if !session.new_mail && !fetch_existing_msgs {
549553
info!(context, "No new emails in folder {folder:?}.");
@@ -835,44 +839,51 @@ impl Session {
835839
folder: &str,
836840
folder_meaning: FolderMeaning,
837841
) -> Result<()> {
842+
let uid_validity;
838843
// Collect pairs of UID and Message-ID.
839844
let mut msgs = BTreeMap::new();
840845

841-
self.select_with_uidvalidity(context, folder).await?;
846+
let create = false;
847+
let folder_exists = self
848+
.select_with_uidvalidity(context, folder, create)
849+
.await?;
850+
if folder_exists {
851+
let mut list = self
852+
.uid_fetch("1:*", RFC724MID_UID)
853+
.await
854+
.with_context(|| format!("Can't resync folder {folder}"))?;
855+
while let Some(fetch) = list.try_next().await? {
856+
let headers = match get_fetch_headers(&fetch) {
857+
Ok(headers) => headers,
858+
Err(err) => {
859+
warn!(context, "Failed to parse FETCH headers: {}", err);
860+
continue;
861+
}
862+
};
863+
let message_id = prefetch_get_message_id(&headers);
842864

843-
let mut list = self
844-
.uid_fetch("1:*", RFC724MID_UID)
845-
.await
846-
.with_context(|| format!("can't resync folder {folder}"))?;
847-
while let Some(fetch) = list.try_next().await? {
848-
let headers = match get_fetch_headers(&fetch) {
849-
Ok(headers) => headers,
850-
Err(err) => {
851-
warn!(context, "Failed to parse FETCH headers: {}", err);
852-
continue;
865+
if let (Some(uid), Some(rfc724_mid)) = (fetch.uid, message_id) {
866+
msgs.insert(
867+
uid,
868+
(
869+
rfc724_mid,
870+
target_folder(context, folder, folder_meaning, &headers).await?,
871+
),
872+
);
853873
}
854-
};
855-
let message_id = prefetch_get_message_id(&headers);
856-
857-
if let (Some(uid), Some(rfc724_mid)) = (fetch.uid, message_id) {
858-
msgs.insert(
859-
uid,
860-
(
861-
rfc724_mid,
862-
target_folder(context, folder, folder_meaning, &headers).await?,
863-
),
864-
);
865874
}
866-
}
867875

868-
info!(
869-
context,
870-
"Resync: collected {} message IDs in folder {}",
871-
msgs.len(),
872-
folder,
873-
);
876+
info!(
877+
context,
878+
"resync_folder_uids: Collected {} message IDs in {folder}.",
879+
msgs.len(),
880+
);
874881

875-
let uid_validity = get_uidvalidity(context, folder).await?;
882+
uid_validity = get_uidvalidity(context, folder).await?;
883+
} else {
884+
warn!(context, "resync_folder_uids: No folder {folder}.");
885+
uid_validity = 0;
886+
}
876887

877888
// Write collected UIDs to SQLite database.
878889
context
@@ -1039,7 +1050,11 @@ impl Session {
10391050
// MOVE/DELETE operations. This does not result in multiple SELECT commands
10401051
// being sent because `select_folder()` does nothing if the folder is already
10411052
// selected.
1042-
self.select_with_uidvalidity(context, folder).await?;
1053+
let create = false;
1054+
let folder_exists = self
1055+
.select_with_uidvalidity(context, folder, create)
1056+
.await?;
1057+
ensure!(folder_exists, "No folder {folder}");
10431058

10441059
// Empty target folder name means messages should be deleted.
10451060
if target.is_empty() {
@@ -1133,30 +1148,40 @@ impl Session {
11331148
.await?;
11341149

11351150
for (folder, rowid_set, uid_set) in UidGrouper::from(rows) {
1136-
if let Err(err) = self.select_with_uidvalidity(context, &folder).await {
1137-
warn!(context, "store_seen_flags_on_imap: Failed to select {folder}, will retry later: {err:#}.");
1151+
let create = false;
1152+
let folder_exists = match self.select_with_uidvalidity(context, &folder, create).await {
1153+
Err(err) => {
1154+
warn!(
1155+
context,
1156+
"store_seen_flags_on_imap: Failed to select {folder}, will retry later: {err:#}.");
1157+
continue;
1158+
}
1159+
Ok(folder_exists) => folder_exists,
1160+
};
1161+
if !folder_exists {
1162+
warn!(context, "store_seen_flags_on_imap: No folder {folder}.");
11381163
} else if let Err(err) = self.add_flag_finalized_with_set(&uid_set, "\\Seen").await {
11391164
warn!(
11401165
context,
11411166
"Cannot mark messages {uid_set} in {folder} as seen, will retry later: {err:#}.");
1167+
continue;
11421168
} else {
11431169
info!(
11441170
context,
11451171
"Marked messages {} in folder {} as seen.", uid_set, folder
11461172
);
1147-
context
1148-
.sql
1149-
.transaction(|transaction| {
1150-
let mut stmt =
1151-
transaction.prepare("DELETE FROM imap_markseen WHERE id = ?")?;
1152-
for rowid in rowid_set {
1153-
stmt.execute((rowid,))?;
1154-
}
1155-
Ok(())
1156-
})
1157-
.await
1158-
.context("Cannot remove messages marked as seen from imap_markseen table")?;
11591173
}
1174+
context
1175+
.sql
1176+
.transaction(|transaction| {
1177+
let mut stmt = transaction.prepare("DELETE FROM imap_markseen WHERE id = ?")?;
1178+
for rowid in rowid_set {
1179+
stmt.execute((rowid,))?;
1180+
}
1181+
Ok(())
1182+
})
1183+
.await
1184+
.context("Cannot remove messages marked as seen from imap_markseen table")?;
11601185
}
11611186

11621187
Ok(())
@@ -1172,9 +1197,14 @@ impl Session {
11721197
return Ok(());
11731198
}
11741199

1175-
self.select_with_uidvalidity(context, folder)
1200+
let create = false;
1201+
let folder_exists = self
1202+
.select_with_uidvalidity(context, folder, create)
11761203
.await
1177-
.context("failed to select folder")?;
1204+
.context("Failed to select folder")?;
1205+
if !folder_exists {
1206+
return Ok(());
1207+
}
11781208

11791209
let mailbox = self
11801210
.selected_mailbox
@@ -1630,7 +1660,7 @@ fn format_setmetadata(folder: &str, device_token: &str) -> String {
16301660
impl Session {
16311661
/// Returns success if we successfully set the flag or we otherwise
16321662
/// think add_flag should not be retried: Disconnection during setting
1633-
/// the flag, or other imap-errors, returns true as well.
1663+
/// the flag, or other imap-errors, returns Ok as well.
16341664
///
16351665
/// Returning error means that the operation can be retried.
16361666
async fn add_flag_finalized_with_set(&mut self, uid_set: &str, flag: &str) -> Result<()> {
@@ -1677,7 +1707,11 @@ impl Session {
16771707
self.close().await?;
16781708
// Before moving emails to the mvbox we need to remember its UIDVALIDITY, otherwise
16791709
// emails moved before that wouldn't be fetched but considered "old" instead.
1680-
self.select_with_uidvalidity(context, folder).await?;
1710+
let create = false;
1711+
let folder_exists = self
1712+
.select_with_uidvalidity(context, folder, create)
1713+
.await?;
1714+
ensure!(folder_exists, "No MVBOX folder {:?}??", &folder);
16811715
return Ok(Some(folder));
16821716
}
16831717
}
@@ -1688,7 +1722,10 @@ impl Session {
16881722
// Some servers require namespace-style folder names like "INBOX.DeltaChat", so we try all
16891723
// the variants here.
16901724
for folder in folders {
1691-
match self.select_with_uidvalidity(context, folder).await {
1725+
match self
1726+
.select_with_uidvalidity(context, folder, create_mvbox)
1727+
.await
1728+
{
16921729
Ok(_) => {
16931730
info!(context, "MVBOX-folder {} created.", folder);
16941731
return Ok(Some(folder));
@@ -2539,10 +2576,14 @@ async fn add_all_recipients_as_contacts(
25392576
);
25402577
return Ok(());
25412578
};
2542-
session
2543-
.select_with_uidvalidity(context, &mailbox)
2579+
let create = false;
2580+
let folder_exists = session
2581+
.select_with_uidvalidity(context, &mailbox, create)
25442582
.await
25452583
.with_context(|| format!("could not select {mailbox}"))?;
2584+
if !folder_exists {
2585+
return Ok(());
2586+
}
25462587

25472588
let recipients = session
25482589
.get_all_recipients(context)

src/imap/idle.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ impl Session {
2929
) -> Result<Self> {
3030
use futures::future::FutureExt;
3131

32-
self.select_with_uidvalidity(context, folder).await?;
32+
let create = true;
33+
self.select_with_uidvalidity(context, folder, create)
34+
.await?;
3335

3436
if self.drain_unsolicited_responses(context)? {
3537
self.new_mail = true;

src/imap/scan_folders.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ impl Imap {
3434
let watched_folders = get_watched_folders(context).await?;
3535

3636
let mut folder_configs = BTreeMap::new();
37+
let mut folder_names = Vec::new();
3738

3839
for folder in folders {
3940
let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
@@ -44,6 +45,7 @@ impl Imap {
4445
// already been moved and left it in the inbox.
4546
continue;
4647
}
48+
folder_names.push(folder.name().to_string());
4749
let folder_name_meaning = get_folder_meaning_by_name(folder.name());
4850

4951
if let Some(config) = folder_meaning.to_config() {
@@ -91,6 +93,7 @@ impl Imap {
9193
}
9294
}
9395

96+
info!(context, "Found folders: {folder_names:?}.");
9497
last_scan.replace(tools::Time::now());
9598
Ok(true)
9699
}

src/imap/select_folder.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ impl ImapSession {
111111
}
112112
}
113113

114-
/// Selects a folder and takes care of UIDVALIDITY changes.
114+
/// Selects a folder optionally creating it and takes care of UIDVALIDITY changes. Returns false
115+
/// iff `folder` doesn't exist.
115116
///
116117
/// When selecting a folder for the first time, sets the uid_next to the current
117118
/// mailbox.uid_next so that no old emails are fetched.
@@ -123,11 +124,24 @@ impl ImapSession {
123124
&mut self,
124125
context: &Context,
125126
folder: &str,
126-
) -> Result<()> {
127-
let newly_selected = self
128-
.select_or_create_folder(context, folder)
129-
.await
130-
.with_context(|| format!("failed to select or create folder {folder}"))?;
127+
create: bool,
128+
) -> Result<bool> {
129+
let newly_selected = if create {
130+
self.select_or_create_folder(context, folder)
131+
.await
132+
.with_context(|| format!("failed to select or create folder {folder}"))?
133+
} else {
134+
match self.select_folder(context, folder).await {
135+
Ok(newly_selected) => newly_selected,
136+
Err(err) => match err {
137+
Error::NoFolder(..) => return Ok(false),
138+
_ => {
139+
return Err(err)
140+
.with_context(|| format!("failed to select folder {folder}"))?
141+
}
142+
},
143+
}
144+
};
131145
let mailbox = self
132146
.selected_mailbox
133147
.as_mut()
@@ -199,7 +213,7 @@ impl ImapSession {
199213
}
200214
}
201215

202-
return Ok(());
216+
return Ok(true);
203217
}
204218

205219
// UIDVALIDITY is modified, reset highest seen MODSEQ.
@@ -233,7 +247,7 @@ impl ImapSession {
233247
old_uid_next,
234248
old_uid_validity,
235249
);
236-
Ok(())
250+
Ok(true)
237251
}
238252
}
239253

0 commit comments

Comments
 (0)