Files
crockery/internal/imap/server.go

98 lines
1.9 KiB
Go

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,
}
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
}