updated layout for webmail
This commit is contained in:
@@ -174,7 +174,7 @@ func TestIMAPListAndSelectAdditionalFolder(t *testing.T) {
|
||||
if _, err := store.StoreMessage(mailboxID, "INBOX", []byte("Subject: normal\r\n\r\nhi"), "<a@example.com>", "a@example.com", "normal"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.StoreMessage(mailboxID, "Spam", []byte("Subject: junk\r\n\r\nspam"), "<b@example.com>", "b@example.com", "junk"); err != nil {
|
||||
if _, err := store.StoreMessage(mailboxID, "Junk", []byte("Subject: junk\r\n\r\nspam"), "<b@example.com>", "b@example.com", "junk"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -201,12 +201,46 @@ func TestIMAPListAndSelectAdditionalFolder(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("list: %v", err)
|
||||
}
|
||||
var names []string
|
||||
// LIST must report every standard folder (INBOX, Junk, Sent, Drafts, Trash) even
|
||||
// though only INBOX and Junk actually hold a message here — a desktop client that
|
||||
// only sees folders with existing mail never learns Trash/Drafts/Sent exist. See
|
||||
// db.AllFoldersForMailbox / imapserver.Session.List.
|
||||
byName := map[string]*imap.ListData{}
|
||||
for _, m := range mailboxes {
|
||||
names = append(names, m.Mailbox)
|
||||
byName[m.Mailbox] = m
|
||||
}
|
||||
if len(names) != 2 {
|
||||
t.Fatalf("expected 2 folders (INBOX, Spam), got %v", names)
|
||||
wantFolders := []string{"INBOX", "Junk", "Sent", "Drafts", "Trash"}
|
||||
if len(mailboxes) != len(wantFolders) {
|
||||
names := make([]string, 0, len(mailboxes))
|
||||
for _, m := range mailboxes {
|
||||
names = append(names, m.Mailbox)
|
||||
}
|
||||
t.Fatalf("expected folders %v, got %v", wantFolders, names)
|
||||
}
|
||||
for _, name := range wantFolders {
|
||||
if _, ok := byName[name]; !ok {
|
||||
t.Errorf("LIST is missing folder %q", name)
|
||||
}
|
||||
}
|
||||
// Trash/Junk/Sent/Drafts should each carry their RFC 6154 SPECIAL-USE attribute
|
||||
// so a desktop client (Thunderbird, Apple Mail, etc.) recognizes them regardless
|
||||
// of the exact folder name.
|
||||
wantAttrs := map[string]imap.MailboxAttr{
|
||||
"Trash": imap.MailboxAttrTrash,
|
||||
"Junk": imap.MailboxAttrJunk,
|
||||
"Sent": imap.MailboxAttrSent,
|
||||
"Drafts": imap.MailboxAttrDrafts,
|
||||
}
|
||||
for name, attr := range wantAttrs {
|
||||
found := false
|
||||
for _, a := range byName[name].Attrs {
|
||||
if a == attr {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("folder %q missing SPECIAL-USE attr %q, got %v", name, attr, byName[name].Attrs)
|
||||
}
|
||||
}
|
||||
|
||||
inboxData, err := client.Select("INBOX", nil).Wait()
|
||||
@@ -217,20 +251,20 @@ func TestIMAPListAndSelectAdditionalFolder(t *testing.T) {
|
||||
t.Fatalf("INBOX NumMessages = %d, want 1", inboxData.NumMessages)
|
||||
}
|
||||
|
||||
spamData, err := client.Select("Spam", nil).Wait()
|
||||
spamData, err := client.Select("Junk", nil).Wait()
|
||||
if err != nil {
|
||||
t.Fatalf("select Spam: %v", err)
|
||||
t.Fatalf("select Junk: %v", err)
|
||||
}
|
||||
if spamData.NumMessages != 1 {
|
||||
t.Fatalf("Spam NumMessages = %d, want 1", spamData.NumMessages)
|
||||
t.Fatalf("Junk NumMessages = %d, want 1", spamData.NumMessages)
|
||||
}
|
||||
|
||||
msgs, err := client.Fetch(imap.SeqSetNum(1), &imap.FetchOptions{Envelope: true}).Collect()
|
||||
if err != nil {
|
||||
t.Fatalf("fetch in Spam: %v", err)
|
||||
t.Fatalf("fetch in Junk: %v", err)
|
||||
}
|
||||
if len(msgs) != 1 || msgs[0].Envelope.Subject != "junk" {
|
||||
t.Fatalf("expected the Spam-folder message (subject %q), got %+v", "junk", msgs)
|
||||
t.Fatalf("expected the Junk-folder message (subject %q), got %+v", "junk", msgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,14 +177,37 @@ func (s *Session) Unsubscribe(mailbox string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List reports every folder that actually has mail (plus INBOX, always) — a filter
|
||||
// rule's move_to_folder action is what creates a second folder; there's no IMAP
|
||||
// CREATE/manual folder management.
|
||||
// specialUseAttrs tags a folder with its RFC 6154 SPECIAL-USE attribute, if any —
|
||||
// desktop IMAP clients (Thunderbird, Apple Mail, K-9, etc.) use this to recognize
|
||||
// Trash/Junk/Sent/Drafts regardless of the exact folder name, though in practice not
|
||||
// every client honors SPECIAL-USE reliably, which is why the folder itself is named
|
||||
// "Junk" (see db.StandardMailboxFolders) rather than relying on this attribute alone.
|
||||
func specialUseAttrs(folder string) []imap.MailboxAttr {
|
||||
switch folder {
|
||||
case "Trash":
|
||||
return []imap.MailboxAttr{imap.MailboxAttrTrash}
|
||||
case "Junk":
|
||||
return []imap.MailboxAttr{imap.MailboxAttrJunk}
|
||||
case "Sent":
|
||||
return []imap.MailboxAttr{imap.MailboxAttrSent}
|
||||
case "Drafts":
|
||||
return []imap.MailboxAttr{imap.MailboxAttrDrafts}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// List reports every folder the mailbox has: the standard folders (INBOX, Junk, Sent,
|
||||
// Drafts, Trash — always, even empty) plus any custom folder a filter rule's
|
||||
// move_to_folder action or explicit webmail folder creation has produced — the same
|
||||
// full list db.AllFoldersForMailbox gives the webmail UI, so a desktop IMAP client
|
||||
// sees exactly the same folders webmail does instead of only ones that happen to
|
||||
// already hold a message (DistinctFoldersForMailbox alone).
|
||||
func (s *Session) List(w *goimapserver.ListWriter, ref string, patterns []string, options *imap.ListOptions) error {
|
||||
if err := s.requireAuth(); err != nil {
|
||||
return err
|
||||
}
|
||||
folders, err := s.backend.DB.DistinctFoldersForMailbox(s.mailbox.ID)
|
||||
folders, err := s.backend.DB.AllFoldersForMailbox(s.mailbox.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -199,7 +222,7 @@ func (s *Session) List(w *goimapserver.ListWriter, ref string, patterns []string
|
||||
if !goimapserver.MatchList(folder, '/', ref, pattern) {
|
||||
continue
|
||||
}
|
||||
if err := w.WriteList(&imap.ListData{Mailbox: folder, Delim: '/'}); err != nil {
|
||||
if err := w.WriteList(&imap.ListData{Mailbox: folder, Delim: '/', Attrs: specialUseAttrs(folder)}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user