101 lines
2.1 KiB
Go
101 lines
2.1 KiB
Go
package imapclient
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/emersion/go-sasl"
|
|
|
|
"github.com/emersion/go-imap/v2"
|
|
"github.com/emersion/go-imap/v2/internal"
|
|
)
|
|
|
|
// Authenticate sends an AUTHENTICATE command.
|
|
//
|
|
// Unlike other commands, this method blocks until the SASL exchange completes.
|
|
func (c *Client) Authenticate(saslClient sasl.Client) error {
|
|
mech, initialResp, err := saslClient.Start()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// c.Caps may send a CAPABILITY command, so check it before c.beginCommand
|
|
var hasSASLIR bool
|
|
if initialResp != nil {
|
|
hasSASLIR = c.Caps().Has(imap.CapSASLIR)
|
|
}
|
|
|
|
cmd := &authenticateCommand{}
|
|
contReq := c.registerContReq(cmd)
|
|
enc := c.beginCommand("AUTHENTICATE", cmd)
|
|
enc.SP().Atom(mech)
|
|
if initialResp != nil && hasSASLIR {
|
|
enc.SP().Atom(internal.EncodeSASL(initialResp))
|
|
initialResp = nil
|
|
}
|
|
enc.flush()
|
|
defer enc.end()
|
|
|
|
for {
|
|
challengeStr, err := contReq.Wait()
|
|
if err != nil {
|
|
return cmd.wait()
|
|
}
|
|
|
|
if challengeStr == "" {
|
|
if initialResp == nil {
|
|
return fmt.Errorf("imapclient: server requested SASL initial response, but we don't have one")
|
|
}
|
|
|
|
contReq = c.registerContReq(cmd)
|
|
if err := c.writeSASLResp(initialResp); err != nil {
|
|
return err
|
|
}
|
|
initialResp = nil
|
|
continue
|
|
}
|
|
|
|
challenge, err := internal.DecodeSASL(challengeStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := saslClient.Next(challenge)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
contReq = c.registerContReq(cmd)
|
|
if err := c.writeSASLResp(resp); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
type authenticateCommand struct {
|
|
commandBase
|
|
}
|
|
|
|
func (c *Client) writeSASLResp(resp []byte) error {
|
|
respStr := internal.EncodeSASL(resp)
|
|
if _, err := c.bw.WriteString(respStr + "\r\n"); err != nil {
|
|
return err
|
|
}
|
|
if err := c.bw.Flush(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Unauthenticate sends an UNAUTHENTICATE command.
|
|
//
|
|
// This command requires support for the UNAUTHENTICATE extension.
|
|
func (c *Client) Unauthenticate() *Command {
|
|
cmd := &unauthenticateCommand{}
|
|
c.beginCommand("UNAUTHENTICATE", cmd).end()
|
|
return &cmd.Command
|
|
}
|
|
|
|
type unauthenticateCommand struct {
|
|
Command
|
|
}
|