fix the Status command on imapclient.Conn

it needs at least 1 attribute.
also make types for those attributes, so its harder to get them wrong.
nothing was using this function.
This commit is contained in:
Mechiel Lukkien 2024-03-11 15:22:41 +01:00
parent 4dea2de343
commit bcf737cbec
No known key found for this signature in database
9 changed files with 83 additions and 26 deletions

View file

@ -206,10 +206,15 @@ func (c *Conn) Namespace() (untagged []Untagged, result Result, rerr error) {
return c.Transactf("namespace") return c.Transactf("namespace")
} }
// Status requests information about a mailbox, such as number of messages, size, etc. // Status requests information about a mailbox, such as number of messages, size,
func (c *Conn) Status(mailbox string) (untagged []Untagged, result Result, rerr error) { // etc. At least one attribute required.
func (c *Conn) Status(mailbox string, attrs ...StatusAttr) (untagged []Untagged, result Result, rerr error) {
defer c.recover(&rerr) defer c.recover(&rerr)
return c.Transactf("status %s", astring(mailbox)) l := make([]string, len(attrs))
for i, a := range attrs {
l[i] = string(a)
}
return c.Transactf("status %s (%s)", astring(mailbox), strings.Join(l, " "))
} }
// Append adds message to mailbox with flags and optional receive time. // Append adds message to mailbox with flags and optional receive time.

View file

@ -363,14 +363,14 @@ func (c *Conn) xuntagged() Untagged {
mailbox := c.xastring() mailbox := c.xastring()
c.xspace() c.xspace()
c.xtake("(") c.xtake("(")
attrs := map[string]int64{} attrs := map[StatusAttr]int64{}
for !c.take(')') { for !c.take(')') {
if len(attrs) > 0 { if len(attrs) > 0 {
c.xspace() c.xspace()
} }
s := c.xatom() s := c.xatom()
c.xspace() c.xspace()
S := strings.ToUpper(s) S := StatusAttr(strings.ToUpper(s))
var num int64 var num int64
// ../rfc/9051:7059 // ../rfc/9051:7059
switch S { switch S {

View file

@ -224,8 +224,25 @@ type UntaggedSearchModSeq struct {
} }
type UntaggedStatus struct { type UntaggedStatus struct {
Mailbox string Mailbox string
Attrs map[string]int64 // Upper case status attributes. ../rfc/9051:7059 Attrs map[StatusAttr]int64 // Upper case status attributes.
} }
// ../rfc/9051:7059 ../9208:712
type StatusAttr string
const (
StatusMessages StatusAttr = "MESSAGES"
StatusUIDNext StatusAttr = "UIDNEXT"
StatusUIDValidity StatusAttr = "UIDVALIDITY"
StatusUnseen StatusAttr = "UNSEEN"
StatusDeleted StatusAttr = "DELETED"
StatusSize StatusAttr = "SIZE"
StatusRecent StatusAttr = "RECENT"
StatusAppendLimit StatusAttr = "APPENDLIMIT"
StatusHighestModSeq StatusAttr = "HIGHESTMODSEQ"
StatusDeletedStorage StatusAttr = "DELETED-STORAGE"
)
type UntaggedNamespace struct { type UntaggedNamespace struct {
Personal, Other, Shared []NamespaceDescr Personal, Other, Shared []NamespaceDescr
} }

View file

@ -42,7 +42,7 @@ func testCondstoreQresync(t *testing.T, qresync bool) {
// First some tests without any messages. // First some tests without any messages.
tc.transactf("ok", "Status inbox (Highestmodseq)") tc.transactf("ok", "Status inbox (Highestmodseq)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"HIGHESTMODSEQ": 1}}) tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusHighestModSeq: 1}})
// No messages, no matches. // No messages, no matches.
tc.transactf("ok", "Uid Fetch 1:* (Flags) (Changedsince 12345)") tc.transactf("ok", "Uid Fetch 1:* (Flags) (Changedsince 12345)")
@ -160,10 +160,10 @@ func testCondstoreQresync(t *testing.T, qresync bool) {
// Check highestmodseq for mailboxes. // Check highestmodseq for mailboxes.
tc.transactf("ok", "Status inbox (highestmodseq)") tc.transactf("ok", "Status inbox (highestmodseq)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"HIGHESTMODSEQ": clientModseq}}) tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusHighestModSeq: clientModseq}})
tc.transactf("ok", "Status otherbox (highestmodseq)") tc.transactf("ok", "Status otherbox (highestmodseq)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "otherbox", Attrs: map[string]int64{"HIGHESTMODSEQ": 3}}) tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "otherbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusHighestModSeq: 3}})
// Check highestmodseq when we select. // Check highestmodseq when we select.
tc.transactf("ok", "Examine otherbox") tc.transactf("ok", "Examine otherbox")
@ -297,7 +297,7 @@ func testCondstoreQresync(t *testing.T, qresync bool) {
// Again after expunge: status, select, conditional store/fetch/search // Again after expunge: status, select, conditional store/fetch/search
tc.transactf("ok", "Status inbox (Highestmodseq Messages Unseen Deleted)") tc.transactf("ok", "Status inbox (Highestmodseq Messages Unseen Deleted)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 4, "UNSEEN": 4, "DELETED": 0, "HIGHESTMODSEQ": clientModseq}}) tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 4, imapclient.StatusUnseen: 4, imapclient.StatusDeleted: 0, imapclient.StatusHighestModSeq: clientModseq}})
tc.transactf("ok", "Close") tc.transactf("ok", "Close")
tc.transactf("ok", "Select inbox") tc.transactf("ok", "Select inbox")

View file

@ -90,15 +90,15 @@ func TestListExtended(t *testing.T) {
} }
ustatus := func(name string) imapclient.UntaggedStatus { ustatus := func(name string) imapclient.UntaggedStatus {
attrs := map[string]int64{ attrs := map[imapclient.StatusAttr]int64{
"MESSAGES": 0, imapclient.StatusMessages: 0,
"UIDNEXT": 1, imapclient.StatusUIDNext: 1,
"UIDVALIDITY": int64(uidval(name)), imapclient.StatusUIDValidity: int64(uidval(name)),
"UNSEEN": 0, imapclient.StatusUnseen: 0,
"DELETED": 0, imapclient.StatusDeleted: 0,
"SIZE": 0, imapclient.StatusSize: 0,
"RECENT": 0, imapclient.StatusRecent: 0,
"APPENDLIMIT": 0, imapclient.StatusAppendLimit: 0,
} }
return imapclient.UntaggedStatus{Mailbox: name, Attrs: attrs} return imapclient.UntaggedStatus{Mailbox: name, Attrs: attrs}
} }

View file

@ -32,7 +32,7 @@ func TestQuota1(t *testing.T) {
// Check that we get a DELETED-STORAGE status attribute with value 0, also if // Check that we get a DELETED-STORAGE status attribute with value 0, also if
// messages are marked deleted. We don't go through the trouble. // messages are marked deleted. We don't go through the trouble.
tc.transactf("ok", "status inbox (DELETED-STORAGE)") tc.transactf("ok", "status inbox (DELETED-STORAGE)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"DELETED-STORAGE": 0}}) tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusDeletedStorage: 0}})
// tclimit does have a limit. // tclimit does have a limit.
tclimit := startArgs(t, false, false, true, true, "limit") tclimit := startArgs(t, false, false, true, true, "limit")
@ -50,5 +50,5 @@ func TestQuota1(t *testing.T) {
tclimit.xuntagged(imapclient.UntaggedQuota{Root: "", Resources: []imapclient.QuotaResource{{Name: imapclient.QuotaResourceStorage, Usage: 0, Limit: 1}}}) tclimit.xuntagged(imapclient.UntaggedQuota{Root: "", Resources: []imapclient.QuotaResource{{Name: imapclient.QuotaResourceStorage, Usage: 0, Limit: 1}}})
tclimit.transactf("ok", "status inbox (DELETED-STORAGE)") tclimit.transactf("ok", "status inbox (DELETED-STORAGE)")
tclimit.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"DELETED-STORAGE": 0}}) tclimit.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusDeletedStorage: 0}})
} }

View file

@ -692,7 +692,7 @@ func DisabledTestReference(t *testing.T) {
defer tc3.close() defer tc3.close()
tc3.client.Login("mjl@mox.example", password0) tc3.client.Login("mjl@mox.example", password0)
tc3.transactf("ok", `list "" "inbox" return (status (messages))`) tc3.transactf("ok", `list "" "inbox" return (status (messages))`)
tc3.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "Inbox"}, imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 0}}) tc3.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "Inbox"}, imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 0}})
tc2.transactf("ok", "fetch 1 rfc822.size") tc2.transactf("ok", "fetch 1 rfc822.size")
tc.xuntagged(imapclient.UntaggedFetch{Seq: 1, Attrs: []imapclient.FetchAttr{imapclient.FetchRFC822Size(len(exampleMsg))}}) tc.xuntagged(imapclient.UntaggedFetch{Seq: 1, Attrs: []imapclient.FetchAttr{imapclient.FetchRFC822Size(len(exampleMsg))}})

View file

@ -20,15 +20,50 @@ func TestStatus(t *testing.T) {
tc.transactf("bad", "status inbox (unknown)") // Unknown attribute. tc.transactf("bad", "status inbox (unknown)") // Unknown attribute.
tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)") tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 0, "UIDVALIDITY": 1, "UIDNEXT": 1, "UNSEEN": 0, "DELETED": 0, "SIZE": 0, "RECENT": 0, "APPENDLIMIT": 0}}) tc.xuntagged(imapclient.UntaggedStatus{
Mailbox: "Inbox",
Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 0,
imapclient.StatusUIDValidity: 1,
imapclient.StatusUIDNext: 1,
imapclient.StatusUnseen: 0,
imapclient.StatusDeleted: 0,
imapclient.StatusSize: 0,
imapclient.StatusRecent: 0,
imapclient.StatusAppendLimit: 0,
},
})
// Again, now with a message in the mailbox. // Again, now with a message in the mailbox.
tc.transactf("ok", "append inbox {4+}\r\ntest") tc.transactf("ok", "append inbox {4+}\r\ntest")
tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)") tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 1, "UIDVALIDITY": 1, "UIDNEXT": 2, "UNSEEN": 1, "DELETED": 0, "SIZE": 4, "RECENT": 0, "APPENDLIMIT": 0}})
tc.xuntagged(imapclient.UntaggedStatus{
Mailbox: "Inbox",
Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 1,
imapclient.StatusUIDValidity: 1,
imapclient.StatusUIDNext: 2,
imapclient.StatusUnseen: 1,
imapclient.StatusDeleted: 0,
imapclient.StatusSize: 4,
imapclient.StatusRecent: 0,
imapclient.StatusAppendLimit: 0,
},
})
tc.client.Select("inbox") tc.client.Select("inbox")
tc.client.StoreFlagsSet("1", true, `\Deleted`) tc.client.StoreFlagsSet("1", true, `\Deleted`)
tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)") tc.transactf("ok", "status inbox (messages uidnext uidvalidity unseen deleted size recent appendlimit)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 1, "UIDVALIDITY": 1, "UIDNEXT": 2, "UNSEEN": 1, "DELETED": 1, "SIZE": 4, "RECENT": 0, "APPENDLIMIT": 0}}) tc.xuntagged(imapclient.UntaggedStatus{
Mailbox: "Inbox",
Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 1,
imapclient.StatusUIDValidity: 1,
imapclient.StatusUIDNext: 2,
imapclient.StatusUnseen: 1,
imapclient.StatusDeleted: 1,
imapclient.StatusSize: 4,
imapclient.StatusRecent: 0,
imapclient.StatusAppendLimit: 0,
},
})
} }

View file

@ -22,5 +22,5 @@ func TestUnselect(t *testing.T) {
tc.client.StoreFlagsAdd("1", true, `\Deleted`) tc.client.StoreFlagsAdd("1", true, `\Deleted`)
tc.transactf("ok", "unselect") tc.transactf("ok", "unselect")
tc.transactf("ok", "status inbox (messages)") tc.transactf("ok", "status inbox (messages)")
tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[string]int64{"MESSAGES": 1}}) // Message not removed. tc.xuntagged(imapclient.UntaggedStatus{Mailbox: "Inbox", Attrs: map[imapclient.StatusAttr]int64{imapclient.StatusMessages: 1}}) // Message not removed.
} }