]> git.r.bdr.sh - rbdr/olden-mail/blobdiff - src/middleware/find_mailboxes_compatibility.rs
Read the tag
[rbdr/olden-mail] / src / middleware / find_mailboxes_compatibility.rs
index f1a450b514e8b41856bbfc736a211153a22f6847..edf5f0694ef5876e6b58f8a1c5aa47ee3b4682de 100644 (file)
@@ -1,14 +1,71 @@
-use log::debug;
+use super::Middleware;
 
 
-/// MailDrop can't find folders to sync because it sends FIND MAILBOXES /*
+/// `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.
 /// which is not understood by modern servers. It instead replaces it with
 /// a LIST command.
-pub fn middleware(input: &[u8]) -> Vec<u8> {
-    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<String>,
+}
+
+impl FindMailboxesCompatibility {
+    pub fn new() -> Self {
+        FindMailboxesCompatibility { tags: vec![] }
+    }
+}
+
+impl Middleware for FindMailboxesCompatibility {
+    fn client_message(&mut self, input: &[u8]) -> Vec<u8> {
+        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<u8> {
+        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<String> = 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) = command.split("OK").next() {
+                        return Some(format!("{} OK MAILBOX Completed.\r", tag.trim()).to_string());
+                    }
+                }
+
+                Some(line.to_string())
+            })
+            .collect();
+
+        lines.join("\n").into_bytes()
     }
     }
-    return input.to_vec();
 }
 }