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.to_string()); let replacement = format!("{} LIST \"\" \"*\"\r\n", tag.trim()); debug!("### {replacement}"); 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!("{} OK Completed", tag))); // 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 {}\r", mailbox_name)); } } return Some(line.to_string()); }) .collect(); return lines.join("\n").into_bytes(); } }