aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-01-19 16:47:20 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-01-19 16:47:20 +0100
commit0cc38571f494a40170dc9913eddad5012337574a (patch)
tree4f5bc045d33b97ed096775818d30a77f50c89df2
parent0f227e44e4996e54d2e55ed5c7a07f5458c4db4f (diff)
downloadaerogramme-0cc38571f494a40170dc9913eddad5012337574a.tar.gz
aerogramme-0cc38571f494a40170dc9913eddad5012337574a.zip
Implement some part of SPECIAL-USE
-rw-r--r--src/imap/command/authenticated.rs12
-rw-r--r--src/mail/user.rs26
-rw-r--r--tests/common/fragments.rs26
3 files changed, 47 insertions, 17 deletions
diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs
index 3fd132f..c5f2d00 100644
--- a/src/imap/command/authenticated.rs
+++ b/src/imap/command/authenticated.rs
@@ -235,11 +235,19 @@ impl<'a> AuthenticatedContext<'a> {
.to_string()
.try_into()
.map_err(|_| anyhow!("invalid mailbox name"))?;
- let mut items = vec![FlagNameAttribute::try_from(Atom::unvalidated(
+ let mut items = vec![FlagNameAttribute::from(Atom::unvalidated(
"Subscribed",
- ))?];
+ ))];
if !*is_real {
items.push(FlagNameAttribute::Noselect);
+ } else {
+ match *mb {
+ "Drafts" => items.push(Atom::unvalidated("Drafts").into()),
+ "Archive" => items.push(Atom::unvalidated("Archive").into()),
+ "Sent" => items.push(Atom::unvalidated("Sent").into()),
+ "Trash" => items.push(Atom::unvalidated("Trash").into()),
+ _ => (),
+ };
}
if is_lsub {
ret.push(Data::Lsub {
diff --git a/src/mail/user.rs b/src/mail/user.rs
index 5af86d0..4767a93 100644
--- a/src/mail/user.rs
+++ b/src/mail/user.rs
@@ -27,6 +27,19 @@ pub const MAILBOX_HIERARCHY_DELIMITER: char = '.';
/// INBOX), and we create a new empty mailbox for INBOX.
pub const INBOX: &str = "INBOX";
+/// For convenience purpose, we also create some special mailbox
+/// that are described in RFC6154 SPECIAL-USE
+/// @FIXME maybe it should be a configuration parameter
+/// @FIXME maybe we should have a per-mailbox flag mechanism, either an enum or a string, so we
+/// track which mailbox is used for what.
+/// @FIXME Junk could be useful but we don't have any antispam solution yet so...
+/// @FIXME IMAP supports virtual mailbox. \All or \Flagged are intended to be virtual mailboxes.
+/// \Trash might be one, or not one. I don't know what we should do there.
+pub const DRAFTS: &str = "Drafts";
+pub const ARCHIVE: &str = "Archive";
+pub const SENT: &str = "Sent";
+pub const TRASH: &str = "Trash";
+
const MAILBOX_LIST_PK: &str = "mailboxes";
const MAILBOX_LIST_SK: &str = "list";
@@ -124,7 +137,7 @@ impl User {
let (mut list, ct) = self.load_mailbox_list().await?;
if list.has_mailbox(name) {
- // TODO: actually delete mailbox contents
+ //@TODO: actually delete mailbox contents
list.set_mailbox(name, None);
self.save_mailbox_list(&list, ct).await?;
Ok(())
@@ -256,7 +269,16 @@ impl User {
}
};
- self.ensure_inbox_exists(&mut list, &row).await?;
+ let is_default_mbx_missing = [ DRAFTS, ARCHIVE, SENT, TRASH ]
+ .iter()
+ .map(|mbx| list.create_mailbox(mbx))
+ .fold(false, |acc, r| acc || matches!(r, CreatedMailbox::Created(..)));
+ let is_inbox_missing = self.ensure_inbox_exists(&mut list, &row).await?;
+ if is_default_mbx_missing && !is_inbox_missing {
+ // It's the only case where we created some mailboxes and not saved them
+ // So we save them!
+ self.save_mailbox_list(&list, row.clone()).await?;
+ }
Ok((list, row))
}
diff --git a/tests/common/fragments.rs b/tests/common/fragments.rs
index 7f7967a..11e0bb7 100644
--- a/tests/common/fragments.rs
+++ b/tests/common/fragments.rs
@@ -155,8 +155,8 @@ pub fn create_mailbox(imap: &mut TcpStream, mbx: Mailbox) -> Result<()> {
let mbx_str = match mbx {
Mailbox::Inbox => "INBOX",
- Mailbox::Archive => "Archive",
- Mailbox::Drafts => "Drafts",
+ Mailbox::Archive => "ArchiveCustom",
+ Mailbox::Drafts => "DraftsCustom",
};
let cmd = format!("15 create {}\r\n", mbx_str);
@@ -172,8 +172,8 @@ pub fn select(imap: &mut TcpStream, mbx: Mailbox, modifier: SelectMod) -> Result
let mbx_str = match mbx {
Mailbox::Inbox => "INBOX",
- Mailbox::Archive => "Archive",
- Mailbox::Drafts => "Drafts",
+ Mailbox::Archive => "ArchiveCustom",
+ Mailbox::Drafts => "DraftsCustom",
};
let mod_str = match modifier {
@@ -209,8 +209,8 @@ pub fn check(imap: &mut TcpStream) -> Result<()> {
pub fn status(imap: &mut TcpStream, mbx: Mailbox, sk: StatusKind) -> Result<String> {
let mbx_str = match mbx {
Mailbox::Inbox => "INBOX",
- Mailbox::Archive => "Archive",
- Mailbox::Drafts => "Drafts",
+ Mailbox::Archive => "ArchiveCustom",
+ Mailbox::Drafts => "DraftsCustom",
};
let sk_str = match sk {
StatusKind::UidNext => "(UIDNEXT)",
@@ -325,7 +325,7 @@ pub fn copy(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result<(
assert!(matches!(selection, Selection::FirstId));
assert!(matches!(to, Mailbox::Archive));
- imap.write(&b"45 copy 1 Archive\r\n"[..])?;
+ imap.write(&b"45 copy 1 ArchiveCustom\r\n"[..])?;
let read = read_lines(imap, &mut buffer, None)?;
assert_eq!(&read[..5], &b"45 OK"[..]);
@@ -421,7 +421,7 @@ pub fn rename_mailbox(imap: &mut TcpStream, from: Mailbox, to: Mailbox) -> Resul
assert!(matches!(from, Mailbox::Archive));
assert!(matches!(to, Mailbox::Drafts));
- imap.write(&b"70 rename Archive Drafts\r\n"[..])?;
+ imap.write(&b"70 rename ArchiveCustom DraftsCustom\r\n"[..])?;
let mut buffer: [u8; 1500] = [0; 1500];
let read = read_lines(imap, &mut buffer, None)?;
assert_eq!(&read[..5], &b"70 OK"[..]);
@@ -429,9 +429,9 @@ pub fn rename_mailbox(imap: &mut TcpStream, from: Mailbox, to: Mailbox) -> Resul
imap.write(&b"71 list \"\" *\r\n"[..])?;
let read = read_lines(imap, &mut buffer, Some(&b"71 OK LIST"[..]))?;
let srv_msg = std::str::from_utf8(read)?;
- assert!(!srv_msg.contains(" Archive\r\n"));
+ assert!(!srv_msg.contains(" ArchiveCustom\r\n"));
assert!(srv_msg.contains(" INBOX\r\n"));
- assert!(srv_msg.contains(" Drafts\r\n"));
+ assert!(srv_msg.contains(" DraftsCustom\r\n"));
Ok(())
}
@@ -439,8 +439,8 @@ pub fn rename_mailbox(imap: &mut TcpStream, from: Mailbox, to: Mailbox) -> Resul
pub fn delete_mailbox(imap: &mut TcpStream, mbx: Mailbox) -> Result<()> {
let mbx_str = match mbx {
Mailbox::Inbox => "INBOX",
- Mailbox::Archive => "Archive",
- Mailbox::Drafts => "Drafts",
+ Mailbox::Archive => "ArchiveCustom",
+ Mailbox::Drafts => "DraftsCustom",
};
let cmd = format!("80 delete {}\r\n", mbx_str);
@@ -471,7 +471,7 @@ pub fn r#move(imap: &mut TcpStream, selection: Selection, to: Mailbox) -> Result
assert!(matches!(to, Mailbox::Archive));
assert!(matches!(selection, Selection::FirstId));
- imap.write(&b"35 move 1 Archive\r\n"[..])?;
+ imap.write(&b"35 move 1 ArchiveCustom\r\n"[..])?;
let read = read_lines(imap, &mut buffer, Some(&b"35 OK"[..]))?;
let srv_msg = std::str::from_utf8(read)?;
assert!(srv_msg.contains("* 1 EXPUNGE"));