diff --git a/src/configure/mod.rs b/src/configure/mod.rs index f55187d77..13f94aa30 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -355,13 +355,14 @@ pub fn JobConfigureImap(context: &Context) { progress!(context, 900); let create_mvbox = context.get_config_bool(Config::MvboxWatch) || context.get_config_bool(Config::MvboxMove); - context - .inbox_thread - .read() - .unwrap() - .imap - .configure_folders(context, create_mvbox); - true + let imap = &context.inbox_thread.read().unwrap().imap; + if let Err(err) = imap.ensure_configured_folders(context, create_mvbox) { + error!(context, "configuring folders failed: {:?}", err); + false + } else { + // XXX call fetch_from_single_folder to set last_seen_uid + true + } } 17 => { progress!(context, 910); diff --git a/src/imap.rs b/src/imap.rs index 5813b0bcf..fc0ec27a4 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -318,19 +318,12 @@ impl Imap { // the trailing underscore is correct if self.connect(context, ¶m) { - if context - .sql - .get_raw_config_int(context, "folders_configured") - .unwrap_or_default() - < 3 - { - self.configure_folders(context, true); - } - return Ok(()); + self.ensure_configured_folders(context, true) + } else { + Err(Error::ImapConnectionFailed( + format!("{}", param).to_string(), + )) } - return Err(Error::ImapConnectionFailed( - format!("{}", param).to_string(), - )); } /// tries connecting to imap account using the specific login @@ -558,13 +551,11 @@ impl Imap { let config = self.config.read().await; let mailbox = config.selected_mailbox.as_ref().expect("just selected"); - ensure!( - mailbox.uid_validity.is_some(), - "Cannot get UIDVALIDITY for folder {:?}", - folder.as_ref() - ); + let new_uid_validity = match mailbox.uid_validity { + Some(v) => v, + None => bail!("Cannot get UIDVALIDITY for folder {:?}", folder.as_ref()), + }; - let new_uid_validity = mailbox.uid_validity.unwrap(); if new_uid_validity != uid_validity { if mailbox.exists == 0 { info!(context, "Folder \"{}\" is empty.", folder.as_ref()); @@ -579,35 +570,36 @@ impl Imap { // uid_validity has changed or is being set the first time. // find the last seen uid within the new uid_validity scope. - - let new_last_seen_uid = if mailbox.uid_next.is_none() { - warn!( - context, - "IMAP folder did not report uid_next, falling back to fetching" - ); - if let Some(ref mut session) = &mut *self.session.lock().await { - // `FETCH (UID)` - // note that we use fetch by sequence number - // and thus we only need to get exactly the - // last-index message. - let set = format!("{}", mailbox.exists); - match session.fetch(set, JUST_UID).await { - Ok(list) => list[0].uid.unwrap_or_else(|| 0), - Err(err) => { - bail!("fetch failed: {:?}", err); - } - } - } else { - return Err(Error::ImapNoConnection); + let new_last_seen_uid = match mailbox.uid_next { + Some(uid_next) => { + uid_next - 1 // XXX could uid_next be 0? + } + None => { + warn!( + context, + "IMAP folder has no uid_next, fall back to fetching" + ); + if let Some(ref mut session) = &mut *self.session.lock().await { + // note that we use fetch by sequence number + // and thus we only need to get exactly the + // last-index message. + let set = format!("{}", mailbox.exists); + match session.fetch(set, JUST_UID).await { + Ok(list) => list[0].uid.unwrap_or_default(), + Err(err) => { + bail!("fetch failed: {:?}", err); + } + } + } else { + return Err(Error::ImapNoConnection); + } } - } else { - max(0, mailbox.uid_next.unwrap() - 1) }; self.set_config_last_seen_uid(context, &folder, new_uid_validity, new_last_seen_uid); info!( context, - "uid change: new {}/{} current {}/{}", + "uid/validity change: new {}/{} current {}/{}", new_last_seen_uid, new_uid_validity, uid_validity, @@ -635,9 +627,9 @@ impl Imap { return Err(Error::ImapNoConnection); }; - // go through all mails in folder (this is typically _fast_ as we already have the whole list) + // prefetch info from all unknown mails in the folder for msg in &list { - let cur_uid = msg.uid.unwrap_or_else(|| 0); + let cur_uid = msg.uid.unwrap_or_default(); if cur_uid > last_seen_uid { read_cnt += 1; @@ -1239,19 +1231,35 @@ impl Imap { }) } - pub fn configure_folders(&self, context: &Context, create_mvbox: bool) { + pub fn ensure_configured_folders( + &self, + context: &Context, + create_mvbox: bool, + ) -> Result<(), Error> { + let folders_configured = context + .sql + .get_raw_config_int(context, "folders_configured"); + if folders_configured.unwrap_or_default() >= 3 { + // the "3" here we increase if we have future updates to + // to folder configuration + return Ok(()); + } + task::block_on(async move { - if !self.is_connected().await { - return; - } + ensure!( + self.is_connected().await, + "cannot configure folders: not connected" + ); info!(context, "Configuring IMAP-folders."); if let Some(ref mut session) = &mut *self.session.lock().await { - let folders = self - .list_folders(session, context) - .await - .expect("no folders found"); + let folders = match self.list_folders(session, context).await { + Some(f) => f, + None => { + bail!("could not obtain list of imap folders"); + } + }; let sentbox_folder = folders @@ -1282,7 +1290,7 @@ impl Imap { Err(err) => { warn!( context, - "Cannot create MVBOX-folder, using trying INBOX subfolder. ({})", + "Cannot create MVBOX-folder, trying to create INBOX subfolder. ({})", err ); @@ -1304,36 +1312,34 @@ impl Imap { // that may be used by other MUAs to list folders. // for the LIST command, the folder is always visible. if let Some(ref mvbox) = mvbox_folder { - // TODO: better error handling - session.subscribe(mvbox).await.expect("failed to subscribe"); + if let Err(err) = session.subscribe(mvbox).await { + warn!(context, "could not subscribe to {:?}: {:?}", mvbox, err); + } } } context .sql - .set_raw_config(context, "configured_inbox_folder", Some("INBOX")) - .ok(); + .set_raw_config(context, "configured_inbox_folder", Some("INBOX"))?; if let Some(ref mvbox_folder) = mvbox_folder { - context - .sql - .set_raw_config(context, "configured_mvbox_folder", Some(mvbox_folder)) - .ok(); + context.sql.set_raw_config( + context, + "configured_mvbox_folder", + Some(mvbox_folder), + )?; } if let Some(ref sentbox_folder) = sentbox_folder { - context - .sql - .set_raw_config( - context, - "configured_sentbox_folder", - Some(sentbox_folder.name()), - ) - .ok(); + context.sql.set_raw_config( + context, + "configured_sentbox_folder", + Some(sentbox_folder.name()), + )?; } context .sql - .set_raw_config_int(context, "folders_configured", 3) - .ok(); + .set_raw_config_int(context, "folders_configured", 3)?; } info!(context, "FINISHED configuring IMAP-folders."); + Ok(()) }) } diff --git a/src/job.rs b/src/job.rs index 7655da793..01c903f12 100644 --- a/src/job.rs +++ b/src/job.rs @@ -228,13 +228,10 @@ impl Job { let imap_inbox = &context.inbox_thread.read().unwrap().imap; if let Ok(msg) = Message::load_from_db(context, MsgId::new(self.foreign_id)) { - if context - .sql - .get_raw_config_int(context, "folders_configured") - .unwrap_or_default() - < 3 - { - imap_inbox.configure_folders(context, true); + if let Err(err) = imap_inbox.ensure_configured_folders(context, true) { + self.try_again_later(TryAgain::StandardDelay, None); + warn!(context, "could not configure folders: {:?}", err); + return; } let dest_folder = context .sql @@ -355,13 +352,10 @@ impl Job { return; } if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() { - if context - .sql - .get_raw_config_int(context, "folders_configured") - .unwrap_or_default() - < 3 - { - imap_inbox.configure_folders(context, true); + if let Err(err) = imap_inbox.ensure_configured_folders(context, true) { + self.try_again_later(TryAgain::StandardDelay, None); + warn!(context, "configuring folders failed: {:?}", err); + return; } let dest_folder = context .sql