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()); 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 + 3)..].replace('"', ""); return Some(format!("* MAILBOX {}\r", mailbox_name.trim())); } } if line.contains("OK") { if let Some(tag) = line.split("OK").next() { return Some(format!("{} OK MAILBOX Completed.\r", tag.trim()).to_string()); } } Some(line.to_string()) }) .collect(); format!("{}\n", lines.join("\n")).into_bytes() } }