Skip to content

Commit 3bc38e3

Browse files
Support NIL hierarchy delimiter
Closes: #329
1 parent f9bd228 commit 3bc38e3

File tree

3 files changed

+84
-5
lines changed

3 files changed

+84
-5
lines changed

mailbox.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ func (info *MailboxInfo) Parse(fields []interface{}) error {
6161

6262
var ok bool
6363
if info.Delimiter, ok = fields[1].(string); !ok {
64-
return errors.New("Mailbox delimiter must be a string")
64+
// The delimiter may be specified as NIL, which gets converted to a nil interface.
65+
if fields[1] != nil {
66+
return errors.New("Mailbox delimiter must be a string")
67+
}
68+
info.Delimiter = ""
6569
}
6670

6771
if name, err := ParseString(fields[2]); err != nil {
@@ -82,8 +86,17 @@ func (info *MailboxInfo) Format() []interface{} {
8286
for i, attr := range info.Attributes {
8387
attrs[i] = RawString(attr)
8488
}
89+
90+
// If the delimiter is NIL, we need to treat it specially by inserting
91+
// a nil field (so that it's later converted to an unquoted NIL atom).
92+
var del interface{}
93+
94+
if info.Delimiter != "" {
95+
del = info.Delimiter
96+
}
97+
8598
// Thunderbird doesn't understand delimiters if not quoted
86-
return []interface{}{attrs, info.Delimiter, FormatMailboxName(name)}
99+
return []interface{}{attrs, del, FormatMailboxName(name)}
87100
}
88101

89102
// TODO: optimize this
@@ -123,12 +136,12 @@ func (info *MailboxInfo) match(name, pattern string) bool {
123136
func (info *MailboxInfo) Match(reference, pattern string) bool {
124137
name := info.Name
125138

126-
if strings.HasPrefix(pattern, info.Delimiter) {
139+
if info.Delimiter != "" && strings.HasPrefix(pattern, info.Delimiter) {
127140
reference = ""
128141
pattern = strings.TrimPrefix(pattern, info.Delimiter)
129142
}
130143
if reference != "" {
131-
if !strings.HasSuffix(reference, info.Delimiter) {
144+
if info.Delimiter != "" && !strings.HasSuffix(reference, info.Delimiter) {
132145
reference += info.Delimiter
133146
}
134147
if !strings.HasPrefix(name, reference) {

read.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@ func (r *Reader) ReadAtom() (interface{}, error) {
217217

218218
r.UnreadRune()
219219

220-
if atom == "NIL" {
220+
if atom == nilAtom {
221221
return nil, nil
222222
}
223+
223224
return atom, nil
224225
}
225226

responses/list_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package responses
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/emersion/go-imap"
8+
)
9+
10+
func TestListSlashDelimiter(t *testing.T) {
11+
mbox := &imap.MailboxInfo{}
12+
13+
if err := mbox.Parse([]interface{}{
14+
[]interface{}{"\\Unseen"},
15+
"/",
16+
"INBOX",
17+
}); err != nil {
18+
t.Error(err)
19+
t.FailNow()
20+
}
21+
22+
if response := getListResponse(t, mbox); response != `* LIST (\Unseen) "/" INBOX`+"\r\n" {
23+
t.Error("Unexpected response:", response)
24+
}
25+
}
26+
27+
func TestListNILDelimiter(t *testing.T) {
28+
mbox := &imap.MailboxInfo{}
29+
30+
if err := mbox.Parse([]interface{}{
31+
[]interface{}{"\\Unseen"},
32+
nil,
33+
"INBOX",
34+
}); err != nil {
35+
t.Error(err)
36+
t.FailNow()
37+
}
38+
39+
if response := getListResponse(t, mbox); response != `* LIST (\Unseen) NIL INBOX`+"\r\n" {
40+
t.Error("Unexpected response:", response)
41+
}
42+
}
43+
44+
func newListResponse(mbox *imap.MailboxInfo) (l *List) {
45+
l = &List{Mailboxes: make(chan *imap.MailboxInfo)}
46+
47+
go func() {
48+
l.Mailboxes <- mbox
49+
close(l.Mailboxes)
50+
}()
51+
52+
return
53+
}
54+
55+
func getListResponse(t *testing.T, mbox *imap.MailboxInfo) string {
56+
b := &bytes.Buffer{}
57+
w := imap.NewWriter(b)
58+
59+
if err := newListResponse(mbox).WriteTo(w); err != nil {
60+
t.Error(err)
61+
t.FailNow()
62+
}
63+
64+
return b.String()
65+
}

0 commit comments

Comments
 (0)