Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion imapclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,14 +716,24 @@ func (c *Client) readResponseTagged(tag, typ string) (startTLS *startTLSCommand,
if !c.dec.ExpectAtom(&code) {
return nil, fmt.Errorf("in resp-text-code: %v", c.dec.Err())
}
// TODO: LONGENTRIES and MAXSIZE from METADATA
switch code {
case "CAPABILITY": // capability-data
caps, err := readCapabilities(c.dec)
if err != nil {
return nil, fmt.Errorf("in capability-data: %v", err)
}
c.setCaps(caps)
case "LONGENTRIES", "MAXSIZE": // METADATA response codes with size parameter
var size uint32
if !c.dec.ExpectSP() || !c.dec.ExpectNumber(&size) {
return nil, fmt.Errorf("in resp-code-metadata: %v", c.dec.Err())
}
if cmd, ok := cmd.(*GetMetadataCommand); ok {
if cmd.data.ResponseCodeData == nil {
cmd.data.ResponseCodeData = &imap.MetadataResponseCodeData{}
}
cmd.data.ResponseCodeData.Size = size
}
case "APPENDUID":
var (
uidValidity uint32
Expand Down
3 changes: 3 additions & 0 deletions imapclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ func newMemClientServerPair(t *testing.T) (net.Conn, io.Closer) {
Caps: imap.CapSet{
imap.CapIMAP4rev1: {},
imap.CapIMAP4rev2: {},
imap.CapCondStore: {},
imap.CapQResync: {},
imap.CapMetadata: {},
},
})

Expand Down
65 changes: 26 additions & 39 deletions imapclient/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,19 @@ package imapclient
import (
"fmt"

"github.com/emersion/go-imap/v2"
"github.com/emersion/go-imap/v2/internal/imapwire"
)

type GetMetadataDepth int

const (
GetMetadataDepthZero GetMetadataDepth = 0
GetMetadataDepthOne GetMetadataDepth = 1
GetMetadataDepthInfinity GetMetadataDepth = -1
)

func (depth GetMetadataDepth) String() string {
switch depth {
case GetMetadataDepthZero:
return "0"
case GetMetadataDepthOne:
return "1"
case GetMetadataDepthInfinity:
return "infinity"
default:
panic(fmt.Errorf("imapclient: unknown GETMETADATA depth %d", depth))
}
}

// GetMetadataOptions contains options for the GETMETADATA command.
type GetMetadataOptions struct {
MaxSize *uint32
Depth GetMetadataDepth
}

func (options *GetMetadataOptions) names() []string {
func getMetadataOptionNames(options *imap.GetMetadataOptions) []string {
if options == nil {
return nil
}
var l []string
if options.MaxSize != nil {
l = append(l, "MAXSIZE")
}
if options.Depth != GetMetadataDepthZero {
if options.Depth != imap.GetMetadataDepthZero {
l = append(l, "DEPTH")
}
return l
Expand All @@ -50,11 +24,20 @@ func (options *GetMetadataOptions) names() []string {
// GetMetadata sends a GETMETADATA command.
//
// This command requires support for the METADATA or METADATA-SERVER extension.
func (c *Client) GetMetadata(mailbox string, entries []string, options *GetMetadataOptions) *GetMetadataCommand {
func (c *Client) GetMetadata(mailbox string, entries []string, options *imap.GetMetadataOptions) *GetMetadataCommand {
// Validate entry names before sending to server
for _, entry := range entries {
if err := imap.ValidateMetadataEntry(entry); err != nil {
cmd := &GetMetadataCommand{mailbox: mailbox}
cmd.err = fmt.Errorf("invalid entry name %q: %w", entry, err)
return cmd
}
}

cmd := &GetMetadataCommand{mailbox: mailbox}
enc := c.beginCommand("GETMETADATA", cmd)
enc.SP().Mailbox(mailbox)
if opts := options.names(); len(opts) > 0 {
if opts := getMetadataOptionNames(options); len(opts) > 0 {
enc.SP().List(len(opts), func(i int) {
opt := opts[i]
enc.Atom(opt).SP()
Expand All @@ -81,6 +64,16 @@ func (c *Client) GetMetadata(mailbox string, entries []string, options *GetMetad
//
// This command requires support for the METADATA or METADATA-SERVER extension.
func (c *Client) SetMetadata(mailbox string, entries map[string]*[]byte) *Command {
// Validate entry names before sending to server
for entry := range entries {
if err := imap.ValidateMetadataEntry(entry); err != nil {
// Create command that will fail immediately
cmd := &Command{}
cmd.err = fmt.Errorf("invalid entry name %q: %w", entry, err)
return cmd
}
}

cmd := &Command{}
enc := c.beginCommand("SETMETADATA", cmd)
enc.SP().Mailbox(mailbox).SP().Special('(')
Expand Down Expand Up @@ -134,19 +127,13 @@ func (c *Client) handleMetadata() error {
type GetMetadataCommand struct {
commandBase
mailbox string
data GetMetadataData
data imap.GetMetadataData
}

func (cmd *GetMetadataCommand) Wait() (*GetMetadataData, error) {
func (cmd *GetMetadataCommand) Wait() (*imap.GetMetadataData, error) {
return &cmd.data, cmd.wait()
}

// GetMetadataData is the data returned by the GETMETADATA command.
type GetMetadataData struct {
Mailbox string
Entries map[string]*[]byte
}

type metadataResp struct {
Mailbox string
EntryList []string
Expand Down
Loading