X-Git-Url: https://git.r.bdr.sh/rbdr/olden-mail/blobdiff_plain/573aaf2a8ccdb6c8c917b2d88a39c9c8103f64ef..661adbfbd282568dca144bf7ec39c3fbbda80d90:/src/middleware/find_mailboxes_compatibility.rs?ds=inline diff --git a/src/middleware/find_mailboxes_compatibility.rs b/src/middleware/find_mailboxes_compatibility.rs index f1a450b..ddc12f1 100644 --- a/src/middleware/find_mailboxes_compatibility.rs +++ b/src/middleware/find_mailboxes_compatibility.rs @@ -1,14 +1,79 @@ use log::debug; -/// MailDrop can't find folders to sync because it sends FIND MAILBOXES /* +use super::Middleware; + +/// `MailDrop` can't find folders to sync because it implements `IMAPv3` and +/// sends FIND MAILBOXES /*, which does not exist in `IMAPv4`. /// which is not understood by modern servers. It instead replaces it with /// a LIST command. -pub fn middleware(input: &[u8]) -> Vec { - let command = String::from_utf8_lossy(input); - if let Some(tag) = command.split("FIND MAILBOXES /*").next() { - let replacement = format!("{} LIST \"/INBOX\" \"*\"\r\n", tag.trim()); - debug!("### {replacement}"); - return replacement.into_bytes(); +pub struct FindMailboxesCompatibility { + tags: Vec, +} + +impl FindMailboxesCompatibility { + pub fn new() -> Self { + FindMailboxesCompatibility { tags: vec![] } + } +} + +impl Middleware for FindMailboxesCompatibility { + fn client_message(&mut self, input: &[u8]) -> Vec { + let command = String::from_utf8_lossy(input); + if command.contains("FIND MAILBOXES /*") { + if let Some(tag) = command.split("FIND MAILBOXES /*").next() { + // We'll need to convert the LIST to a FIND + self.tags.push(tag.trim().to_string()); + let replacement = format!("{} LIST \"\" \"*\"\r\n", tag.trim()); + let debug_str = replacement + .replace('\n', "\\n") + .replace('\r', "\\r") + .replace('\t', "\\t"); + debug!("### {debug_str}"); + return replacement.into_bytes(); + } + } + input.to_vec() + } + + fn server_message(&mut self, input: &[u8]) -> Vec { + let command = String::from_utf8_lossy(input); + let contains_ok_completed = self + .tags + .iter() + .any(|tag| command.contains(&format!("{tag} OK Completed"))); + + // We want to only modify responses that were a result of a MAILBOX call. + if !contains_ok_completed { + return input.to_vec(); + } + + let lines: Vec = command + .lines() + .filter_map(|line| { + // The IMAPv3 spec specifically says INBOX is excluded from MAILBOX + if line.starts_with("* LIST") && line.trim_end().ends_with("\"/\" INBOX") { + return None; + } + + // Transform IMAPv4 "* LIST" lines to IMAPv3 "* MAILBOX" + if line.starts_with("* LIST") { + if let Some(last_slash_pos) = line.rfind('/') { + let mailbox_name = line[(last_slash_pos + 1)..].trim(); + return Some(format!("* MAILBOX {mailbox_name}\r")); + } + } + + Some(line.to_string()) + }) + .collect(); + + let replacement = lines.join("\n"); + let debug_str = replacement + .replace('\n', "\\n") + .replace('\r', "\\r") + .replace('\t', "\\t"); + debug!("### {debug_str}"); + + replacement.into_bytes() } - return input.to_vec(); }