Skip to content

Commit

Permalink
client: expose handler replies to third-parties
Browse files Browse the repository at this point in the history
Client.replies was previously unexported, making it impossible to implement
AUTHENTICATE-like responses (e.g. IDLE) outside of the client package.

This commit adds responses.Replier and makes the replies channel per-command.
  • Loading branch information
emersion committed May 19, 2019
1 parent 652a96c commit fba45c5
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 11 deletions.
9 changes: 6 additions & 3 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ type Client struct {
conn *imap.Conn
isTLS bool

replies chan []byte
loggedOut chan struct{}
upgrading bool

Expand Down Expand Up @@ -184,6 +183,11 @@ func (c *Client) execute(cmdr imap.Commander, h responses.Handler) (*imap.Status
cmd := cmdr.Command()
cmd.Tag = generateTag()

var replies <-chan []byte
if replier, ok := h.(responses.Replier); ok {
replies = replier.Replies()
}

if c.Timeout > 0 {
err := c.conn.SetDeadline(time.Now().Add(c.Timeout))
if err != nil {
Expand Down Expand Up @@ -255,7 +259,7 @@ func (c *Client) execute(cmdr imap.Commander, h responses.Handler) (*imap.Status

for {
select {
case reply := <-c.replies:
case reply := <-replies:
// Response handler needs to send a reply (Used for AUTHENTICATE)
if err := c.writeReply(reply); err != nil {
close(unregister)
Expand Down Expand Up @@ -562,7 +566,6 @@ func New(conn net.Conn) (*Client, error) {

c := &Client{
conn: imap.NewConn(conn, r, w),
replies: make(chan []byte),
loggedOut: make(chan struct{}),
state: imap.ConnectingState,
ErrorLog: log.New(os.Stderr, "imap/client: ", log.LstdFlags),
Expand Down
5 changes: 1 addition & 4 deletions client/cmd_noauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,7 @@ func (c *Client) Authenticate(auth sasl.Client) error {
res := &responses.Authenticate{
Mechanism: auth,
InitialResponse: ir,
AuthReply: func(reply []byte) error {
c.replies <- reply
return nil
},
RepliesCh: make(chan []byte, 10),
}

status, err := c.execute(cmd, res)
Expand Down
12 changes: 8 additions & 4 deletions responses/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ import (
"github.com/emersion/go-sasl"
)

type AuthReplyFunc func(reply []byte) error

// An AUTHENTICATE response.
type Authenticate struct {
Mechanism sasl.Client
InitialResponse []byte
AuthReply AuthReplyFunc
RepliesCh chan []byte
}

// Implements
func (r *Authenticate) Replies() <-chan []byte {
return r.RepliesCh
}

func (r *Authenticate) writeLine(l string) error {
return r.AuthReply([]byte(l + "\r\n"))
r.RepliesCh <- []byte(l + "\r\n")
return nil
}

func (r *Authenticate) cancel() error {
Expand Down
7 changes: 7 additions & 0 deletions responses/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ type HandlerFunc func(resp imap.Resp) error
func (f HandlerFunc) Handle(resp imap.Resp) error {
return f(resp)
}

// Replier is a Handler that needs to send raw data (for instance
// AUTHENTICATE).
type Replier interface {
Handler
Replies() <-chan []byte
}

0 comments on commit fba45c5

Please sign in to comment.