package extensions import ( "fmt" "strings" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-imap/responses" "github.com/emersion/go-imap/utf7" ) // A LIST-STATUS client type ListStatusClient struct { c *client.Client } func NewListStatusClient(c *client.Client) *ListStatusClient { return &ListStatusClient{c} } // SupportListStatus checks if the server supports the LIST-STATUS extension. func (c *ListStatusClient) SupportListStatus() (bool, error) { return c.c.Support("LIST-STATUS") } // ListStatus performs a LIST-STATUS command, listing mailboxes and also // retrieving the requested status items. A nil channel can be passed in order // to only retrieve the STATUS responses func (c *ListStatusClient) ListStatus( ref string, name string, items []imap.StatusItem, ch chan *imap.MailboxInfo, ) ([]*imap.MailboxStatus, error) { if ch != nil { defer close(ch) } if c.c.State() != imap.AuthenticatedState && c.c.State() != imap.SelectedState { return nil, client.ErrNotLoggedIn } cmd := &ListStatusCommand{ Reference: ref, Mailbox: name, Items: items, } res := &ListStatusResponse{Mailboxes: ch} status, err := c.c.Execute(cmd, res) if err != nil { return nil, err } return res.Statuses, status.Err() } // ListStatusCommand is a LIST command, as defined in RFC 3501 section 6.3.8. If // Subscribed is set to true, LSUB will be used instead. Mailbox statuses will // be returned if Items is not nil type ListStatusCommand struct { Reference string Mailbox string Subscribed bool Items []imap.StatusItem } func (cmd *ListStatusCommand) Command() *imap.Command { name := "LIST" if cmd.Subscribed { name = "LSUB" } enc := utf7.Encoding.NewEncoder() ref, _ := enc.String(cmd.Reference) mailbox, _ := enc.String(cmd.Mailbox) items := make([]string, len(cmd.Items)) if cmd.Items != nil { for i, item := range cmd.Items { items[i] = string(item) } } args := fmt.Sprintf("RETURN (STATUS (%s))", strings.Join(items, " ")) return &imap.Command{ Name: name, Arguments: []interface{}{ref, mailbox, imap.RawString(args)}, } } // A LIST-STATUS response type ListStatusResponse struct { Mailboxes chan *imap.MailboxInfo Subscribed bool Statuses []*imap.MailboxStatus } func (r *ListStatusResponse) Name() string { if r.Subscribed { return "LSUB" } else { return "LIST" } } func (r *ListStatusResponse) Handle(resp imap.Resp) error { name, _, ok := imap.ParseNamedResp(resp) if !ok { return responses.ErrUnhandled } switch name { case "LIST": if r.Mailboxes == nil { return nil } res := responses.List{Mailboxes: r.Mailboxes} return res.Handle(resp) case "STATUS": res := responses.Status{ Mailbox: new(imap.MailboxStatus), } err := res.Handle(resp) if err != nil { return err } r.Statuses = append(r.Statuses, res.Mailbox) default: return responses.ErrUnhandled } return nil } func (r *ListStatusResponse) WriteTo(w *imap.Writer) error { respName := r.Name() for mbox := range r.Mailboxes { fields := []interface{}{imap.RawString(respName)} fields = append(fields, mbox.Format()...) resp := imap.NewUntaggedResp(fields) if err := resp.WriteTo(w); err != nil { return err } } return nil }