package imapclient import ( "fmt" "strings" "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/internal/imapwire" ) func statusItems(options *imap.StatusOptions) []string { m := map[string]bool{ "MESSAGES": options.NumMessages, "UIDNEXT": options.UIDNext, "UIDVALIDITY": options.UIDValidity, "UNSEEN": options.NumUnseen, "DELETED": options.NumDeleted, "SIZE": options.Size, "APPENDLIMIT": options.AppendLimit, "DELETED-STORAGE": options.DeletedStorage, "HIGHESTMODSEQ": options.HighestModSeq, } var l []string for k, req := range m { if req { l = append(l, k) } } return l } // Status sends a STATUS command. // // A nil options pointer is equivalent to a zero options value. func (c *Client) Status(mailbox string, options *imap.StatusOptions) *StatusCommand { if options == nil { options = new(imap.StatusOptions) } if options.NumRecent { panic("StatusOptions.NumRecent is not supported in imapclient") } cmd := &StatusCommand{mailbox: mailbox} enc := c.beginCommand("STATUS", cmd) enc.SP().Mailbox(mailbox).SP() items := statusItems(options) enc.List(len(items), func(i int) { enc.Atom(items[i]) }) enc.end() return cmd } func (c *Client) handleStatus() error { data, err := readStatus(c.dec) if err != nil { return fmt.Errorf("in status: %v", err) } cmd := c.findPendingCmdFunc(func(cmd command) bool { switch cmd := cmd.(type) { case *StatusCommand: return cmd.mailbox == data.Mailbox case *ListCommand: return cmd.returnStatus && cmd.pendingData != nil && cmd.pendingData.Mailbox == data.Mailbox default: return false } }) switch cmd := cmd.(type) { case *StatusCommand: cmd.data = *data case *ListCommand: cmd.pendingData.Status = data cmd.mailboxes <- cmd.pendingData cmd.pendingData = nil } return nil } // StatusCommand is a STATUS command. type StatusCommand struct { commandBase mailbox string data imap.StatusData } func (cmd *StatusCommand) Wait() (*imap.StatusData, error) { return &cmd.data, cmd.wait() } func readStatus(dec *imapwire.Decoder) (*imap.StatusData, error) { var data imap.StatusData if !dec.ExpectMailbox(&data.Mailbox) || !dec.ExpectSP() { return nil, dec.Err() } err := dec.ExpectList(func() error { if err := readStatusAttVal(dec, &data); err != nil { return fmt.Errorf("in status-att-val: %v", dec.Err()) } return nil }) return &data, err } func readStatusAttVal(dec *imapwire.Decoder, data *imap.StatusData) error { var name string if !dec.ExpectAtom(&name) || !dec.ExpectSP() { return dec.Err() } var ok bool switch strings.ToUpper(name) { case "MESSAGES": var num uint32 ok = dec.ExpectNumber(&num) data.NumMessages = &num case "UIDNEXT": var uidNext imap.UID ok = dec.ExpectUID(&uidNext) data.UIDNext = uidNext case "UIDVALIDITY": ok = dec.ExpectNumber(&data.UIDValidity) case "UNSEEN": var num uint32 ok = dec.ExpectNumber(&num) data.NumUnseen = &num case "DELETED": var num uint32 ok = dec.ExpectNumber(&num) data.NumDeleted = &num case "SIZE": var size int64 ok = dec.ExpectNumber64(&size) data.Size = &size case "APPENDLIMIT": var num uint32 if dec.Number(&num) { ok = true } else { ok = dec.ExpectNIL() num = ^uint32(0) } data.AppendLimit = &num case "DELETED-STORAGE": var storage int64 ok = dec.ExpectNumber64(&storage) data.DeletedStorage = &storage case "HIGHESTMODSEQ": ok = dec.ExpectModSeq(&data.HighestModSeq) default: if !dec.DiscardValue() { return dec.Err() } } if !ok { return dec.Err() } return nil }