use log::debug; 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 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() } }