Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imapserver: split command handling #554

Draft
wants to merge 1 commit into
base: v2
Choose a base branch
from
Draft
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
37 changes: 35 additions & 2 deletions imapserver/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,10 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {

// TODO: handle multiple commands concurrently
sendOK := true
var err error
var (
err error
cmd *command
)
switch name {
case "NOOP", "CHECK":
err = c.handleNoop(dec)
Expand Down Expand Up @@ -252,7 +255,7 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {
err = c.handleAppend(tag, dec)
sendOK = false
case "FETCH", "UID FETCH":
err = c.handleFetch(dec, numKind)
cmd, err = c.handleFetch(dec, numKind)
case "EXPUNGE":
err = c.handleExpunge(dec)
case "UID EXPUNGE":
Expand Down Expand Up @@ -282,6 +285,10 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {

dec.DiscardLine()

if err == nil && cmd != nil {
err = cmd.Wait()
}

var (
resp *imap.StatusResponse
imapErr *imap.Error
Expand Down Expand Up @@ -474,6 +481,32 @@ func (c *Conn) poll(cmd string) error {
return c.session.Poll(w, allowExpunge)
}

type command struct {
done chan struct{}
err error
}

func newCommand(f func() error) *command {
cmd := &command{done: make(chan struct{})}
go func() {
var err error
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("panic handling command: %v\n%s", v, debug.Stack())
}
cmd.err = err
close(cmd.done)
}()
err = f()
}()
return cmd
}

func (cmd *command) Wait() error {
<-cmd.done
return cmd.err
}

type responseEncoder struct {
*imapwire.Encoder
conn *Conn
Expand Down
21 changes: 10 additions & 11 deletions imapserver/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ type fetchWriterOptions struct {
obsolete map[*imap.FetchItemBodySection]string
}

func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) (*command, error) {
var seqSet imap.SeqSet
if !dec.ExpectSP() || !dec.ExpectSeqSet(&seqSet) || !dec.ExpectSP() {
return dec.Err()
return nil, dec.Err()
}

var options imap.FetchOptions
Expand All @@ -43,12 +43,12 @@ func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
return handleFetchAtt(dec, name, &options, &writerOptions)
})
if err != nil {
return err
return nil, err
}
if !isList {
name, err := readFetchAttName(dec)
if err != nil {
return err
return nil, err
}

// Handle macros
Expand All @@ -70,28 +70,27 @@ func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
handleFetchBodyStructure(&options, &writerOptions, false)
default:
if err := handleFetchAtt(dec, name, &options, &writerOptions); err != nil {
return err
return nil, err
}
}
}

if !dec.ExpectCRLF() {
return dec.Err()
return nil, dec.Err()
}

if err := c.checkState(imap.ConnStateSelected); err != nil {
return err
return nil, err
}

if numKind == NumKindUID {
options.UID = true
}

w := &FetchWriter{conn: c, options: writerOptions}
if err := c.session.Fetch(w, numKind, seqSet, &options); err != nil {
return err
}
return nil
return newCommand(func() error {
return c.session.Fetch(w, numKind, seqSet, &options)
}), nil
}

func handleFetchAtt(dec *imapwire.Decoder, attName string, options *imap.FetchOptions, writerOptions *fetchWriterOptions) error {
Expand Down