From 3da4aec99851683f0ad3db7b9c28a6d4e46560f3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 Aug 2024 13:16:07 +0200 Subject: [PATCH] Add support for LIST-METADATA --- imapclient/list.go | 20 +++++++++++++----- imapclient/metadata.go | 46 ++++++++++++++++++++++++++++++------------ list.go | 2 ++ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/imapclient/list.go b/imapclient/list.go index eacda45d..d9d04ffe 100644 --- a/imapclient/list.go +++ b/imapclient/list.go @@ -49,6 +49,9 @@ func getReturnOpts(options *imap.ListOptions) []string { if options.ReturnSpecialUse { l = append(l, "SPECIAL-USE") } + if options.ReturnMetadata { + l = append(l, "METADATA") + } return l } @@ -63,8 +66,9 @@ func getReturnOpts(options *imap.ListOptions) []string { // extension. func (c *Client) List(ref, pattern string, options *imap.ListOptions) *ListCommand { cmd := &ListCommand{ - mailboxes: make(chan *imap.ListData, 64), - returnStatus: options != nil && options.ReturnStatus != nil, + mailboxes: make(chan *imap.ListData, 64), + returnStatus: options != nil && options.ReturnStatus != nil, + returnMetadata: options != nil && options.ReturnMetadata != nil, } enc := c.beginCommand("LIST", cmd) if selectOpts := getSelectOpts(options); len(selectOpts) > 0 { @@ -77,11 +81,16 @@ func (c *Client) List(ref, pattern string, options *imap.ListOptions) *ListComma enc.SP().Atom("RETURN").SP().List(len(returnOpts), func(i int) { opt := returnOpts[i] enc.Atom(opt) - if opt == "STATUS" { + switch opt { + case "STATUS": returnStatus := statusItems(options.ReturnStatus) enc.SP().List(len(returnStatus), func(j int) { enc.Atom(returnStatus[j]) }) + case "METADATA": + enc.SP().List(len(options.ReturnMetadata), func(j int) { + enc.String(options.ReturnMetadata[j]) + }) } }) } @@ -127,8 +136,9 @@ type ListCommand struct { cmd mailboxes chan *imap.ListData - returnStatus bool - pendingData *imap.ListData + returnStatus bool + returnMetadata bool + pendingData *imap.ListData } // Next advances to the next mailbox. diff --git a/imapclient/metadata.go b/imapclient/metadata.go index 40633b6c..622684fe 100644 --- a/imapclient/metadata.go +++ b/imapclient/metadata.go @@ -108,28 +108,48 @@ func (c *Client) handleMetadata() error { return fmt.Errorf("in metadata-resp: %v", err) } - cmd := c.findPendingCmdFunc(func(anyCmd command) bool { - cmd, ok := anyCmd.(*GetMetadataCommand) - return ok && cmd.mailbox == data.Mailbox - }) - if cmd != nil && len(data.EntryValues) > 0 { - cmd := cmd.(*GetMetadataCommand) - cmd.data.Mailbox = data.Mailbox - if cmd.data.Entries == nil { - cmd.data.Entries = make(map[string]*[]byte) + cmd := c.findPendingCmdFunc(func(cmd command) bool { + switch cmd := cmd.(type) { + case *GetMetadataCommand: + return cmd.mailbox == data.Mailbox + case *ListCommand: + return cmd.returnMetadata && cmd.pendingData != nil && cmd.pendingData.Mailbox == data.Mailbox + default: + return false } + }) + if len(data.EntryValues) == 0 { + cmd = nil // Response is unsolicited + } + switch cmd := cmd.(type) { + case *GetMetadataCommand: // The server might send multiple METADATA responses for a single // METADATA command - for k, v := range data.EntryValues { - cmd.data.Entries[k] = v + populateMetadata(cmd.data, data) + case *ListCommand: + // TODO: populateMetadata(cmd.pendingData.Metadata, data) + // TODO: send to chan + default: + if handler := c.options.unilateralDataHandler().Metadata; handler != nil && len(data.EntryList) > 0 { + handler(data.Mailbox, data.EntryList) } - } else if handler := c.options.unilateralDataHandler().Metadata; handler != nil && len(data.EntryList) > 0 { - handler(data.Mailbox, data.EntryList) } return nil } +func populateMetadata(dst, src *GetMetadataData) { + cmd.data.Mailbox = data.Mailbox + if cmd.data.Entries == nil { + cmd.data.Entries = make(map[string]*[]byte) + } + // The server might send multiple METADATA responses for a single + // METADATA command + for k, v := range data.EntryValues { + cmd.data.Entries[k] = v + } +} + // GetMetadataCommand is a GETMETADATA command. type GetMetadataCommand struct { cmd diff --git a/list.go b/list.go index a3103a60..d2803b88 100644 --- a/list.go +++ b/list.go @@ -11,6 +11,7 @@ type ListOptions struct { ReturnChildren bool ReturnStatus *StatusOptions // requires IMAP4rev2 or LIST-STATUS ReturnSpecialUse bool // requires SPECIAL-USE + ReturnMetadata []string // requires LIST-METADATA } // ListData is the mailbox data returned by a LIST command. @@ -23,6 +24,7 @@ type ListData struct { ChildInfo *ListDataChildInfo OldName string Status *StatusData + Metadata struct{} // TODO: *GetMetadataData } type ListDataChildInfo struct {