introduce a select_with_uidvalidity function that helps us during configuration to know about last_seen_uid

This commit is contained in:
holger krekel
2019-11-30 12:58:58 +01:00
parent ebf6c10dd7
commit 8cefbd227e
2 changed files with 77 additions and 58 deletions

View File

@@ -360,8 +360,13 @@ pub fn JobConfigureImap(context: &Context) {
error!(context, "configuring folders failed: {:?}", err); error!(context, "configuring folders failed: {:?}", err);
false false
} else { } else {
// XXX call fetch_from_single_folder to set last_seen_uid let res = imap.select_with_uidvalidity(context, "INBOX");
true if let Err(err) = res {
error!(context, "could not read INBOX status: {:?}", err);
false
} else {
true
}
} }
} }
17 => { 17 => {

View File

@@ -418,14 +418,14 @@ impl Imap {
.fetch_from_single_folder(context, &watch_folder) .fetch_from_single_folder(context, &watch_folder)
.await? .await?
{ {
// During the fetch commands new messages may arrive. So we fetch until we do not // We fetch until no more new messages are there.
// get any more. If IDLE is called directly after, there is only a small chance that
// messages are missed and delayed until the next IDLE call
} }
Ok(()) Ok(())
}) })
} }
/// select a folder, possibly update uid_validity and, if needed,
/// expunge the folder to remove delete-marked messages.
async fn select_folder<S: AsRef<str>>( async fn select_folder<S: AsRef<str>>(
&self, &self,
context: &Context, context: &Context,
@@ -532,40 +532,41 @@ impl Imap {
} }
} }
async fn fetch_from_single_folder<S: AsRef<str>>( /// return Result with (uid_validity, last_seen_uid) tuple.
pub(crate) fn select_with_uidvalidity(
&self, &self,
context: &Context, context: &Context,
folder: S, folder: &str,
) -> Result<bool, Error> { ) -> Result<(u32, u32), Error> {
if let Err(err) = self.select_folder(context, Some(&folder)).await { task::block_on(async move {
bail!( if let Err(err) = self.select_folder(context, Some(folder)).await {
"Cannot select folder {:?} for fetching: {}", bail!("could not select folder {:?}: {:?}", folder, err);
folder.as_ref(), }
err
);
}
// compare last seen UIDVALIDITY against the current one // compare last seen UIDVALIDITY against the current one
let (mut uid_validity, mut last_seen_uid) = self.get_config_last_seen_uid(context, &folder); let (uid_validity, last_seen_uid) = self.get_config_last_seen_uid(context, &folder);
let config = self.config.read().await; let config = self.config.read().await;
let mailbox = config.selected_mailbox.as_ref().expect("just selected"); let mailbox = config.selected_mailbox.as_ref().expect("just selected");
let new_uid_validity = match mailbox.uid_validity { let new_uid_validity = match mailbox.uid_validity {
Some(v) => v, Some(v) => v,
None => bail!("Cannot get UIDVALIDITY for folder {:?}", folder.as_ref()), None => bail!("Cannot get UIDVALIDITY for folder {:?}", folder),
}; };
if new_uid_validity == uid_validity {
return Ok((uid_validity, last_seen_uid));
}
if new_uid_validity != uid_validity {
if mailbox.exists == 0 { if mailbox.exists == 0 {
info!(context, "Folder \"{}\" is empty.", folder.as_ref()); info!(context, "Folder \"{}\" is empty.", folder);
// set lastseenuid=0 for empty folders. // set lastseenuid=0 for empty folders.
// id we do not do this here, we'll miss the first message // id we do not do this here, we'll miss the first message
// as we will get in here again and fetch from lastseenuid+1 then // as we will get in here again and fetch from lastseenuid+1 then
self.set_config_last_seen_uid(context, &folder, new_uid_validity, 0); self.set_config_last_seen_uid(context, &folder, new_uid_validity, 0);
return Ok(false); return Ok((new_uid_validity, 0));
} }
// uid_validity has changed or is being set the first time. // uid_validity has changed or is being set the first time.
@@ -605,15 +606,21 @@ impl Imap {
uid_validity, uid_validity,
last_seen_uid last_seen_uid
); );
uid_validity = new_uid_validity; Ok((new_uid_validity, new_last_seen_uid))
last_seen_uid = new_last_seen_uid; })
} }
async fn fetch_from_single_folder<S: AsRef<str>>(
&self,
context: &Context,
folder: S,
) -> Result<bool, Error> {
let (uid_validity, last_seen_uid) =
self.select_with_uidvalidity(context, folder.as_ref())?;
let mut read_cnt = 0; let mut read_cnt = 0;
let mut read_errors = 0;
let mut new_last_seen_uid = 0;
let list = if let Some(ref mut session) = &mut *self.session.lock().await { let mut list = if let Some(ref mut session) = &mut *self.session.lock().await {
// fetch messages with larger UID than the last one seen // fetch messages with larger UID than the last one seen
// (`UID FETCH lastseenuid+1:*)`, see RFC 4549 // (`UID FETCH lastseenuid+1:*)`, see RFC 4549
let set = format!("{}:*", last_seen_uid + 1); let set = format!("{}:*", last_seen_uid + 1);
@@ -627,44 +634,51 @@ impl Imap {
return Err(Error::ImapNoConnection); return Err(Error::ImapNoConnection);
}; };
// prefetch info from all unknown mails in the folder // prefetch info from all unfetched mails
let mut new_last_seen_uid = last_seen_uid;
let mut read_errors = 0;
list.sort_unstable_by_key(|msg| msg.uid.unwrap_or_default());
for msg in &list { for msg in &list {
let cur_uid = msg.uid.unwrap_or_default(); let cur_uid = msg.uid.unwrap_or_default();
if cur_uid > last_seen_uid { if cur_uid <= last_seen_uid {
read_cnt += 1; warn!(
context,
"wrong uid {}, last seen was {}", cur_uid, last_seen_uid
);
continue;
}
read_cnt += 1;
let message_id = prefetch_get_message_id(msg).unwrap_or_default(); let message_id = prefetch_get_message_id(msg).unwrap_or_default();
if !precheck_imf(context, &message_id, folder.as_ref(), cur_uid) { if !precheck_imf(context, &message_id, folder.as_ref(), cur_uid) {
// check passed, go fetch the rest // check passed, go fetch the rest
if self.fetch_single_msg(context, &folder, cur_uid).await == 0 { if self.fetch_single_msg(context, &folder, cur_uid).await == 0 {
info!(
context,
"Read error for message {} from \"{}\", trying over later.",
message_id,
folder.as_ref()
);
read_errors += 1;
}
} else {
// check failed
info!( info!(
context, context,
"Skipping message {} from \"{}\" by precheck.", "Read error for message {} from \"{}\", trying over later.",
message_id, message_id,
folder.as_ref(), folder.as_ref()
); );
read_errors += 1;
} }
if cur_uid > new_last_seen_uid { } else {
new_last_seen_uid = cur_uid // we know the message-id already or don't want the message otherwise.
} info!(
context,
"Skipping message {} from \"{}\" by precheck.",
message_id,
folder.as_ref(),
);
}
if read_errors == 0 {
new_last_seen_uid = cur_uid;
} }
} }
if 0 == read_errors && new_last_seen_uid > 0 { if new_last_seen_uid > last_seen_uid {
// TODO: it might be better to increase the lastseenuid also on partial errors.
// however, this requires to sort the list before going through it above.
self.set_config_last_seen_uid(context, &folder, uid_validity, new_last_seen_uid); self.set_config_last_seen_uid(context, &folder, uid_validity, new_last_seen_uid);
} }