5 /// `MailDrop` can't find folders to sync because it implements `IMAPv3` and
6 /// sends FIND MAILBOXES /*, which does not exist in `IMAPv4`.
7 /// which is not understood by modern servers. It instead replaces it with
9 pub struct FindMailboxesCompatibility {
13 impl FindMailboxesCompatibility {
14 pub fn new() -> Self {
15 FindMailboxesCompatibility { tags: vec![] }
19 impl Middleware for FindMailboxesCompatibility {
20 fn client_message(&mut self, input: &[u8]) -> Vec<u8> {
21 let command = String::from_utf8_lossy(input);
22 if command.contains("FIND MAILBOXES /*") {
23 if let Some(tag) = command.split("FIND MAILBOXES /*").next() {
24 // We'll need to convert the LIST to a FIND
25 self.tags.push(tag.trim().to_string());
26 let replacement = format!("{} LIST \"\" \"*\"\r\n", tag.trim());
27 let debug_str = replacement
30 .replace('\t', "\\t");
31 debug!("### {debug_str}");
32 return replacement.into_bytes();
38 fn server_message(&mut self, input: &[u8]) -> Vec<u8> {
39 let command = String::from_utf8_lossy(input);
40 let contains_ok_completed = self
43 .any(|tag| command.contains(&format!("{tag} OK Completed")));
45 // We want to only modify responses that were a result of a MAILBOX call.
46 if !contains_ok_completed {
47 return input.to_vec();
50 let lines: Vec<String> = command
53 // The IMAPv3 spec specifically says INBOX is excluded from MAILBOX
54 if line.starts_with("* LIST") && line.trim_end().ends_with("\"/\" INBOX") {
58 // Transform IMAPv4 "* LIST" lines to IMAPv3 "* MAILBOX"
59 if line.starts_with("* LIST") {
60 if let Some(last_slash_pos) = line.rfind('/') {
61 let mailbox_name = line[(last_slash_pos + 2)..].trim();
62 return Some(format!("* MAILBOX {mailbox_name}\r"));
66 Some(line.to_string())
70 let replacement = lines.join("\n");
71 let debug_str = replacement
74 .replace('\t', "\\t");
75 debug!("### {debug_str}");
77 replacement.into_bytes()