Implement some part of SPECIAL-USE
This commit is contained in:
parent
0f227e44e4
commit
0cc38571f4
3 changed files with 47 additions and 17 deletions
|
@ -235,11 +235,19 @@ impl<'a> AuthenticatedContext<'a> {
|
||||||
.to_string()
|
.to_string()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| anyhow!("invalid mailbox name"))?;
|
.map_err(|_| anyhow!("invalid mailbox name"))?;
|
||||||
let mut items = vec![FlagNameAttribute::try_from(Atom::unvalidated(
|
let mut items = vec![FlagNameAttribute::from(Atom::unvalidated(
|
||||||
"Subscribed",
|
"Subscribed",
|
||||||
))?];
|
))];
|
||||||
if !*is_real {
|
if !*is_real {
|
||||||
items.push(FlagNameAttribute::Noselect);
|
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 {
|
if is_lsub {
|
||||||
ret.push(Data::Lsub {
|
ret.push(Data::Lsub {
|
||||||
|
|
|
@ -27,6 +27,19 @@ pub const MAILBOX_HIERARCHY_DELIMITER: char = '.';
|
||||||
/// INBOX), and we create a new empty mailbox for INBOX.
|
/// INBOX), and we create a new empty mailbox for INBOX.
|
||||||
pub const INBOX: &str = "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_PK: &str = "mailboxes";
|
||||||
const MAILBOX_LIST_SK: &str = "list";
|
const MAILBOX_LIST_SK: &str = "list";
|
||||||
|
|
||||||
|
@ -124,7 +137,7 @@ impl User {
|
||||||
|
|
||||||
let (mut list, ct) = self.load_mailbox_list().await?;
|
let (mut list, ct) = self.load_mailbox_list().await?;
|
||||||
if list.has_mailbox(name) {
|
if list.has_mailbox(name) {
|
||||||
// TODO: actually delete mailbox contents
|
//@TODO: actually delete mailbox contents
|
||||||
list.set_mailbox(name, None);
|
list.set_mailbox(name, None);
|
||||||
self.save_mailbox_list(&list, ct).await?;
|
self.save_mailbox_list(&list, ct).await?;
|
||||||
Ok(())
|
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))
|
Ok((list, row))
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,8 +155,8 @@ pub fn create_mailbox(imap: &mut TcpStream, mbx: Mailbox) -> Result<()> {
|
||||||
|
|
||||||
let mbx_str = match mbx {
|
let mbx_str = match mbx {
|
||||||
Mailbox::Inbox => "INBOX",
|
Mailbox::Inbox => "INBOX",
|
||||||
Mailbox::Archive => "Archive",
|
Mailbox::Archive => "ArchiveCustom",
|
||||||
Mailbox::Drafts => "Drafts",
|
Mailbox::Drafts => "DraftsCustom",
|
||||||
};
|
};
|
||||||
|
|
||||||
let cmd = format!("15 create {}\r\n", mbx_str);
|
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 {
|
let mbx_str = match mbx {
|
||||||
Mailbox::Inbox => "INBOX",
|
Mailbox::Inbox => "INBOX",
|
||||||
Mailbox::Archive => "Archive",
|
Mailbox::Archive => "ArchiveCustom",
|
||||||
Mailbox::Drafts => "Drafts",
|
Mailbox::Drafts => "DraftsCustom",
|
||||||
};
|
};
|
||||||
|
|
||||||
let mod_str = match modifier {
|
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> {
|
pub fn status(imap: &mut TcpStream, mbx: Mailbox, sk: StatusKind) -> Result<String> {
|
||||||
let mbx_str = match mbx {
|
let mbx_str = match mbx {
|
||||||
Mailbox::Inbox => "INBOX",
|
Mailbox::Inbox => "INBOX",
|
||||||
Mailbox::Archive => "Archive",
|
Mailbox::Archive => "ArchiveCustom",
|
||||||
Mailbox::Drafts => "Drafts",
|
Mailbox::Drafts => "DraftsCustom",
|
||||||
};
|
};
|
||||||
let sk_str = match sk {
|
let sk_str = match sk {
|
||||||
StatusKind::UidNext => "(UIDNEXT)",
|
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!(selection, Selection::FirstId));
|
||||||
assert!(matches!(to, Mailbox::Archive));
|
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)?;
|
let read = read_lines(imap, &mut buffer, None)?;
|
||||||
assert_eq!(&read[..5], &b"45 OK"[..]);
|
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!(from, Mailbox::Archive));
|
||||||
assert!(matches!(to, Mailbox::Drafts));
|
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 mut buffer: [u8; 1500] = [0; 1500];
|
||||||
let read = read_lines(imap, &mut buffer, None)?;
|
let read = read_lines(imap, &mut buffer, None)?;
|
||||||
assert_eq!(&read[..5], &b"70 OK"[..]);
|
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"[..])?;
|
imap.write(&b"71 list \"\" *\r\n"[..])?;
|
||||||
let read = read_lines(imap, &mut buffer, Some(&b"71 OK LIST"[..]))?;
|
let read = read_lines(imap, &mut buffer, Some(&b"71 OK LIST"[..]))?;
|
||||||
let srv_msg = std::str::from_utf8(read)?;
|
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(" INBOX\r\n"));
|
||||||
assert!(srv_msg.contains(" Drafts\r\n"));
|
assert!(srv_msg.contains(" DraftsCustom\r\n"));
|
||||||
|
|
||||||
Ok(())
|
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<()> {
|
pub fn delete_mailbox(imap: &mut TcpStream, mbx: Mailbox) -> Result<()> {
|
||||||
let mbx_str = match mbx {
|
let mbx_str = match mbx {
|
||||||
Mailbox::Inbox => "INBOX",
|
Mailbox::Inbox => "INBOX",
|
||||||
Mailbox::Archive => "Archive",
|
Mailbox::Archive => "ArchiveCustom",
|
||||||
Mailbox::Drafts => "Drafts",
|
Mailbox::Drafts => "DraftsCustom",
|
||||||
};
|
};
|
||||||
let cmd = format!("80 delete {}\r\n", mbx_str);
|
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!(to, Mailbox::Archive));
|
||||||
assert!(matches!(selection, Selection::FirstId));
|
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 read = read_lines(imap, &mut buffer, Some(&b"35 OK"[..]))?;
|
||||||
let srv_msg = std::str::from_utf8(read)?;
|
let srv_msg = std::str::from_utf8(read)?;
|
||||||
assert!(srv_msg.contains("* 1 EXPUNGE"));
|
assert!(srv_msg.contains("* 1 EXPUNGE"));
|
||||||
|
|
Loading…
Reference in a new issue