]> git.r.bdr.sh - rbdr/olden-mail/blob - src/middleware/find_mailboxes_compatibility.rs
Escape chars when debugging
[rbdr/olden-mail] / src / middleware / find_mailboxes_compatibility.rs
1 use log::debug;
2
3 use super::Middleware;
4
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
8 /// a LIST command.
9 pub struct FindMailboxesCompatibility {
10 tags: Vec<String>,
11 }
12
13 impl FindMailboxesCompatibility {
14 pub fn new() -> Self {
15 FindMailboxesCompatibility { tags: vec![] }
16 }
17 }
18
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
28 .replace('\n', "\\n")
29 .replace('\r', "\\r")
30 .replace('\t', "\\t");
31 debug!("### {debug_str}");
32 return replacement.into_bytes();
33 }
34 }
35 input.to_vec()
36 }
37
38 fn server_message(&mut self, input: &[u8]) -> Vec<u8> {
39 let command = String::from_utf8_lossy(input);
40 let contains_ok_completed = self
41 .tags
42 .iter()
43 .any(|tag| command.contains(&format!("{tag} OK Completed")));
44
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();
48 }
49
50 let lines: Vec<String> = command
51 .lines()
52 .filter_map(|line| {
53 // The IMAPv3 spec specifically says INBOX is excluded from MAILBOX
54 if line.starts_with("* LIST") && line.trim_end().ends_with("\"/\" INBOX") {
55 return None;
56 }
57
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 + 1)..].trim();
62 return Some(format!("* MAILBOX {mailbox_name}\r"));
63 }
64 }
65
66 Some(line.to_string())
67 })
68 .collect();
69
70 let replacement = lines.join("\n");
71 let debug_str = replacement
72 .replace('\n', "\\n")
73 .replace('\r', "\\r")
74 .replace('\t', "\\t");
75 debug!("### {debug_str}");
76
77 replacement.into_bytes()
78 }
79 }