diff --git a/ui/account.go b/ui/account.go index 50f41e4..8353db3 100644 --- a/ui/account.go +++ b/ui/account.go @@ -26,6 +26,7 @@ func NewAccountTab(conf *config.AccountConfig) (*AccountTab, error) { } go work.Run() work.PostAction(types.Configure{Config: conf}) + work.PostAction(types.Connect{}) return &AccountTab{ Config: conf, Worker: work, diff --git a/worker/imap/worker.go b/worker/imap/worker.go index 080927d..6971f8c 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -1,20 +1,43 @@ package imap import ( - "time" + "fmt" + "net/url" + "strings" "git.sr.ht/~sircmpwn/aerc2/worker/types" + "github.com/emersion/go-imap" + "github.com/emersion/go-imap/client" + "github.com/emersion/go-imap-idle" ) +var errUnsupported = fmt.Errorf("unsupported command") + +type imapClient struct { + *client.Client + *idle.IdleClient +} + type IMAPWorker struct { messages chan types.WorkerMessage actions chan types.WorkerMessage + + config struct { + scheme string + insecure bool + addr string + user *url.Userinfo + } + + client *imapClient + updates chan client.Update } func NewIMAPWorker() *IMAPWorker { return &IMAPWorker{ messages: make(chan types.WorkerMessage, 50), actions: make(chan types.WorkerMessage, 50), + updates: make(chan client.Update, 50), } } @@ -26,27 +49,104 @@ func (w *IMAPWorker) PostAction(msg types.WorkerMessage) { w.actions <- msg } -func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) { +func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { switch msg := msg.(type) { case types.Ping: - w.messages <- types.Ack{ - Message: types.RespondTo(msg), + // No-op + case types.Configure: + u, err := url.Parse(msg.Config.Source) + if err != nil { + return err } + + w.config.scheme = u.Scheme + if strings.HasSuffix(w.config.scheme, "+insecure") { + w.config.scheme = strings.TrimSuffix(w.config.scheme, "+insecure") + w.config.insecure = true + } + + w.config.addr = u.Host + if !strings.ContainsRune(w.config.addr, ':') { + w.config.addr += ":" + u.Scheme + } + + w.config.scheme = u.Scheme + w.config.user = u.User + case types.Connect: + // TODO: populate TLS config + + var ( + c *client.Client + err error + ) + switch w.config.scheme { + case "imap": + c, err = client.Dial(w.config.addr) + if err != nil { + return err + } + + if !w.config.insecure { + if err := c.StartTLS(nil); err != nil { + return err + } + } + case "imaps": + c, err = client.DialTLS(w.config.addr, nil) + if err != nil { + return err + } + default: + return fmt.Errorf("Unknown IMAP scheme %s", w.config.scheme) + } + + if w.config.user != nil { + username := w.config.user.Username() + password, hasPassword := w.config.user.Password() + if !hasPassword { + // TODO: ask password + } + if err := c.Login(username, password); err != nil { + return err + } + } + + if _, err := c.Select(imap.InboxName, false); err != nil { + return err + } + + c.Updates = w.updates + w.client = &imapClient{c, idle.NewClient(c)} + + // TODO: don't idle right away + go w.client.IdleWithFallback(nil, 0) default: - w.messages <- types.Unsupported{ - Message: types.RespondTo(msg), - } + return errUnsupported } + return nil } func (w *IMAPWorker) Run() { - // TODO: IMAP shit for { select { case msg := <-w.actions: - w.handleMessage(msg) - default: - time.Sleep(100 * time.Millisecond) + fmt.Printf("<= %T\n", msg) + if err := w.handleMessage(msg); err == errUnsupported { + w.messages <- types.Unsupported{ + Message: types.RespondTo(msg), + } + } else if err != nil { + w.messages <- types.Error{ + Message: types.RespondTo(msg), + Error: err, + } + } else { + w.messages <- types.Ack{ + Message: types.RespondTo(msg), + } + } + case update := <-w.updates: + fmt.Printf("<= %T\n", update) } } } diff --git a/worker/types/messages.go b/worker/types/messages.go index a4d8ddb..e83bd6b 100644 --- a/worker/types/messages.go +++ b/worker/types/messages.go @@ -13,6 +13,7 @@ type Message struct { } // Meta-messages + type Ack struct { Message } @@ -27,6 +28,7 @@ type Unsupported struct { } // Commands + type Ping struct { Message }