package imap import ( "context" "fmt" "io" "log" "sync/atomic" imapbackend "github.com/emersion/go-imap/backend" imapserver "github.com/emersion/go-imap/server" "ur.gs/crockery/internal/store" ) type Server interface { Run() io.Closer } func NewServer(cancel context.CancelFunc, datastore store.Interface, starttls bool) Server { out := &concrete{ cancel: cancel, store: datastore, starttls: starttls, } out.server = imapserver.New(out) out.server.TLSConfig = out.store.TLSConfig() if starttls { out.name = "imap-starttls" out.server.Addr = ":143" } else { out.name = "imap-tls" out.server.Addr = ":993" } return out } type concrete struct { name string cancel context.CancelFunc store store.Interface server *imapserver.Server starttls bool // Session IDs sid uint64 } func (c *concrete) Run() { var err error if c.starttls { err = c.server.ListenAndServe() } else { err = c.server.ListenAndServeTLS() } if err != nil { log.Printf("Error serving IMAP %s: %v", c.server.Addr, err) } else { log.Printf("Stopped listening on IMAP %s", c.server.Addr) } c.cancel() } // backend implementation for go-imap func (c *concrete) Login(user, pass string) (imapbackend.User, error) { account, err := c.store.FindAccountWithPassword(user, pass) if err != nil { // Lo the real error, but don't show it to the end user log.Printf("Opening %s session for %s failed: %s", c.name, user, err) return nil, fmt.Errorf("Login failed") } session := &Session{ ID: atomic.AddUint64(&c.sid, uint64(1)), Account: account, ServiceName: c.name, store: c.store, } log.Printf("Beginning %s session %d for %s", c.name, session.ID, user) // FIXME: TODO: Track ongoing sessions for termination or notifications return session, nil } func (c *concrete) Close() error { c.cancel() // FIXME: this doesn't touch the server return nil }