rework set_mdnsent and set_seen

This commit is contained in:
holger krekel
2019-09-30 23:26:21 +02:00
parent ab2ef1e1e4
commit e0932ba0cc
2 changed files with 114 additions and 141 deletions

View File

@@ -1197,8 +1197,8 @@ impl Imap {
}; };
if copied { if copied {
if self.add_flag(context, uid, "\\Deleted") == 0 { if !self.add_flag_finalized(context, uid, "\\Deleted") {
warn!(context, "Cannot mark message as \"Deleted\".",); warn!(context, "Giving up: cannot mark {} as \"Deleted\".", uid);
} }
self.config.write().unwrap().selected_folder_needs_expunge = true; self.config.write().unwrap().selected_folder_needs_expunge = true;
res = ImapResult::Success; res = ImapResult::Success;
@@ -1223,13 +1223,21 @@ impl Imap {
} }
} }
fn add_flag<S: AsRef<str>>(&self, context: &Context, server_uid: u32, flag: S) -> usize { fn add_flag_finalized(&self, context: &Context, server_uid: u32, flag: &str) -> bool {
// return true if we successfully set the flag or we otherwise
// think add_flag should not be retried: Disconnection during setting
// the flag, or other imap-errors, returns true as well.
//
// returning false means that the operation can be retried.
if server_uid == 0 { if server_uid == 0 {
return 0; return true; // might be moved but we don't want to have a stuck job
}
if self.should_reconnect() {
return false;
} }
if let Some(ref mut session) = &mut *self.session.lock().unwrap() { if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
let set = format!("{}", server_uid); let set = format!("{}", server_uid);
let query = format!("+FLAGS ({})", flag.as_ref()); let query = format!("+FLAGS ({})", flag);
match session.uid_store(&set, &query) { match session.uid_store(&set, &query) {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {
@@ -1239,76 +1247,59 @@ impl Imap {
); );
} }
} }
} return true; // we tried once, that's probably enough for setting flag
// All non-connection states are treated as success - the mail may
// already be deleted or moved away on the server.
if self.should_reconnect() {
0
} else { } else {
1 unreachable!();
} }
} }
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult { pub fn prepare_imap_operation_on_msg(
let mut res = ImapResult::RetryLater; &self,
context: &Context,
folder: &str,
uid: u32,
) -> Option<ImapResult> {
if uid == 0 { if uid == 0 {
res = ImapResult::Failed Some(ImapResult::Failed)
} else if self.is_connected() { } else if !self.is_connected() {
info!( Some(ImapResult::RetryLater)
context, } else if self.select_folder(context, Some(&folder)) == 0 {
"Marking message {}/{} as seen...",
folder.as_ref(),
uid,
);
if self.select_folder(context, Some(folder.as_ref())) == 0 {
warn!( warn!(
context, context,
"Cannot select folder {} for setting SEEN flag.", "Cannot select folder {} for preparing IMAP operation", folder
folder.as_ref(),
); );
} else if self.add_flag(context, uid, "\\Seen") == 0 { Some(ImapResult::RetryLater)
warn!(context, "Cannot mark message as seen.",);
} else { } else {
res = ImapResult::Success None
} }
} }
if res == ImapResult::RetryLater { pub fn set_seen(&self, context: &Context, folder: &str, uid: u32) -> ImapResult {
if self.should_reconnect() { if let Some(imapresult) = self.prepare_imap_operation_on_msg(context, folder, uid) {
ImapResult::RetryLater return imapresult;
}
// we are connected, and the folder is selected
info!(context, "Marking message {}/{} as seen...", folder, uid,);
if self.add_flag_finalized(context, uid, "\\Seen") {
ImapResult::Success
} else { } else {
warn!(
context,
"Cannot mark message {} in folder {} as seen, ignoring.", uid, folder
);
ImapResult::Failed ImapResult::Failed
} }
} else {
res
}
} }
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult { pub fn set_mdnsent(&self, context: &Context, folder: &str, uid: u32) -> ImapResult {
// returns 0=job should be retried later, 1=job done, 2=job done and flag just set if let Some(imapresult) = self.prepare_imap_operation_on_msg(context, folder, uid) {
let mut res = ImapResult::RetryLater; return imapresult;
}
// we are connected, and the folder is selected
let set = format!("{}", uid); let set = format!("{}", uid);
info!(context, "Marking message {}/{} as $MDNSent...", folder, uid,);
if uid == 0 {
res = ImapResult::Failed;
} else if self.is_connected() {
info!(
context,
"Marking message {}/{} as $MDNSent...",
folder.as_ref(),
uid,
);
if self.select_folder(context, Some(folder.as_ref())) == 0 {
warn!(
context,
"Cannot select folder {} for setting $MDNSent flag.",
folder.as_ref()
);
} else {
// Check if the folder can handle the `$MDNSent` flag (see RFC 3503). If so, and not // Check if the folder can handle the `$MDNSent` flag (see RFC 3503). If so, and not
// set: set the flags and return this information. // set: set the flags and return this information.
// If the folder cannot handle the `$MDNSent` flag, we risk duplicated MDNs; it's up // If the folder cannot handle the `$MDNSent` flag, we risk duplicated MDNs; it's up
@@ -1331,24 +1322,30 @@ impl Imap {
_ => false, _ => false,
}) })
.is_some() .is_some()
}) });
.expect("just selected folder");
if can_create_flag { match can_create_flag {
let fetched_msgs = None | Some(false) => {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() { warn!(
match session.uid_fetch(set, FETCH_FLAGS) { context,
Ok(res) => Some(res), "can't store $MDNSent flags in folder {}, ignoring.", folder
);
// return ImapResult::Failed;
}
Some(true) => {}
}
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(&set, FETCH_FLAGS) {
Ok(res) => res,
Err(err) => { Err(err) => {
eprintln!("fetch error: {:?}", err); warn!(context, "IMAP uid_fetch {:?} error: {}", set, err);
None return ImapResult::Failed;
} }
} }
} else { } else {
unreachable!(); unreachable!();
}; };
if let Some(msgs) = fetched_msgs {
let flag_set = msgs let flag_set = msgs
.first() .first()
.map(|msg| { .map(|msg| {
@@ -1361,40 +1358,16 @@ impl Imap {
.is_some() .is_some()
}) })
.unwrap_or_else(|| false); .unwrap_or_else(|| false);
if flag_set {
res = if flag_set { info!(context, "$MDNSent already set and MDN already sent.");
ImapResult::AlreadyDone ImapResult::AlreadyDone
} else if self.add_flag(context, uid, "$MDNSent") != 0 { } else if self.add_flag_finalized(context, uid, "$MDNSent") {
info!(context, "$MDNSent just set and MDN will be sent.");
ImapResult::Success ImapResult::Success
} else { } else {
res info!(context, "$MDNSent could not be set, ignoring");
};
if res == ImapResult::Success {
info!(context, "$MDNSent just set and MDN will be sent.");
} else {
info!(context, "$MDNSent already set and MDN already sent.");
}
}
} else {
res = ImapResult::Success;
info!(
context,
"Cannot store $MDNSent flags, risk sending duplicate MDN.",
);
}
}
}
if res == ImapResult::RetryLater {
if self.should_reconnect() {
ImapResult::RetryLater
} else {
ImapResult::Failed ImapResult::Failed
} }
} else {
res
}
} }
// only returns 0 on connection problems; we should try later again in this case * // only returns 0 on connection problems; we should try later again in this case *
@@ -1463,7 +1436,7 @@ impl Imap {
} }
// mark the message for deletion // mark the message for deletion
if self.add_flag(context, *server_uid, "\\Deleted") == 0 { if !self.add_flag_finalized(context, *server_uid, "\\Deleted") {
warn!(context, "Cannot mark message as \"Deleted\"."); warn!(context, "Cannot mark message as \"Deleted\".");
} else { } else {
self.config.write().unwrap().selected_folder_needs_expunge = true; self.config.write().unwrap().selected_folder_needs_expunge = true;

View File

@@ -317,7 +317,7 @@ impl Job {
{ {
let folder = msg.server_folder.as_ref().unwrap(); let folder = msg.server_folder.as_ref().unwrap();
match inbox.set_mdnsent(context, folder, msg.server_uid) { match inbox.set_mdnsent(&context, folder, msg.server_uid) {
ImapResult::RetryLater => { ImapResult::RetryLater => {
self.try_again_later(3i32, None); self.try_again_later(3i32, None);
} }