Files
go-imap/imapclient/capability.go
T
Simon Ser 3a66d70513 imapclient: avoid leaking cmd.Wait method
All commands embed a base struct previously called "cmd". The
"cmd" type exports a Wait method, intended to be used for simple
commands which don't return any additional data. Typical usage:

    type MyCommand {
        cmd
    }

Unfortunately, even if "cmd" is an unexported field, its Wait method
is accessible to other packages. This causes issues for commands
which need to read a bunch of untagged responses before completing.

Fix this by rejiggering the command types. Introduce a baseCommand
type which is embedded in all command types and doesn't export any
method. Add a Command type which exposes a single Wait method for
simple commands.

Closes: https://github.com/emersion/go-imap/issues/641
2024-09-28 15:24:11 +02:00

51 lines
1.0 KiB
Go

package imapclient
import (
"fmt"
"github.com/emersion/go-imap/v2"
"github.com/emersion/go-imap/v2/internal/imapwire"
)
// Capability sends a CAPABILITY command.
func (c *Client) Capability() *CapabilityCommand {
cmd := &CapabilityCommand{}
c.beginCommand("CAPABILITY", cmd).end()
return cmd
}
func (c *Client) handleCapability() error {
caps, err := readCapabilities(c.dec)
if err != nil {
return err
}
c.setCaps(caps)
if cmd := findPendingCmdByType[*CapabilityCommand](c); cmd != nil {
cmd.caps = caps
}
return nil
}
// CapabilityCommand is a CAPABILITY command.
type CapabilityCommand struct {
commandBase
caps imap.CapSet
}
func (cmd *CapabilityCommand) Wait() (imap.CapSet, error) {
err := cmd.wait()
return cmd.caps, err
}
func readCapabilities(dec *imapwire.Decoder) (imap.CapSet, error) {
caps := make(imap.CapSet)
for dec.SP() {
var name string
if !dec.ExpectAtom(&name) {
return caps, fmt.Errorf("in capability-data: %v", dec.Err())
}
caps[imap.Cap(name)] = struct{}{}
}
return caps, nil
}