aboutsummaryrefslogtreecommitdiffstats
path: root/worker/jmap/worker.go
diff options
context:
space:
mode:
Diffstat (limited to 'worker/jmap/worker.go')
-rw-r--r--worker/jmap/worker.go195
1 files changed, 195 insertions, 0 deletions
diff --git a/worker/jmap/worker.go b/worker/jmap/worker.go
new file mode 100644
index 00000000..a538f60c
--- /dev/null
+++ b/worker/jmap/worker.go
@@ -0,0 +1,195 @@
+package jmap
+
+import (
+ "errors"
+ "net/url"
+ "time"
+
+ "git.sr.ht/~rjarry/aerc/config"
+ "git.sr.ht/~rjarry/aerc/lib/uidstore"
+ "git.sr.ht/~rjarry/aerc/models"
+ "git.sr.ht/~rjarry/aerc/worker/handlers"
+ "git.sr.ht/~rjarry/aerc/worker/jmap/cache"
+ "git.sr.ht/~rjarry/aerc/worker/types"
+ "git.sr.ht/~rockorager/go-jmap"
+ "git.sr.ht/~rockorager/go-jmap/mail/identity"
+ "git.sr.ht/~rockorager/go-jmap/mail/mailbox"
+)
+
+func init() {
+ handlers.RegisterWorkerFactory("jmap", NewJMAPWorker)
+}
+
+var errUnsupported = errors.New("unsupported")
+
+type JMAPWorker struct {
+ config struct {
+ account *config.AccountConfig
+ endpoint string
+ oauth bool
+ user *url.Userinfo
+ cacheState bool
+ cacheBlobs bool
+ serverPing time.Duration
+ useLabels bool
+ allMail string
+ }
+
+ w *types.Worker
+ client *jmap.Client
+ cache *cache.JMAPCache
+ accountId jmap.ID
+
+ selectedMbox jmap.ID
+ dir2mbox map[string]jmap.ID
+ mbox2dir map[jmap.ID]string
+ roles map[mailbox.Role]jmap.ID
+ identities map[string]*identity.Identity
+ uidStore *uidstore.Store
+
+ changes chan jmap.TypeState
+ stop chan struct{}
+}
+
+func NewJMAPWorker(worker *types.Worker) (types.Backend, error) {
+ return &JMAPWorker{
+ w: worker,
+ uidStore: uidstore.NewStore(),
+ roles: make(map[mailbox.Role]jmap.ID),
+ dir2mbox: make(map[string]jmap.ID),
+ mbox2dir: make(map[jmap.ID]string),
+ identities: make(map[string]*identity.Identity),
+ changes: make(chan jmap.TypeState),
+ }, nil
+}
+
+func (w *JMAPWorker) addMbox(mbox *mailbox.Mailbox, dir string) {
+ w.mbox2dir[mbox.ID] = dir
+ w.dir2mbox[dir] = mbox.ID
+ w.roles[mbox.Role] = mbox.ID
+}
+
+func (w *JMAPWorker) deleteMbox(id jmap.ID) {
+ var dir string
+ var role mailbox.Role
+
+ delete(w.mbox2dir, id)
+ for d, i := range w.dir2mbox {
+ if i == id {
+ dir = d
+ break
+ }
+ }
+ delete(w.dir2mbox, dir)
+ for r, i := range w.roles {
+ if i == id {
+ role = r
+ break
+ }
+ }
+ delete(w.roles, role)
+}
+
+var capas = models.Capabilities{Sort: true, Thread: false}
+
+func (w *JMAPWorker) Capabilities() *models.Capabilities {
+ return &capas
+}
+
+func (w *JMAPWorker) PathSeparator() string {
+ return "/"
+}
+
+func (w *JMAPWorker) handleMessage(msg types.WorkerMessage) error {
+ switch msg := msg.(type) {
+ case *types.Unsupported:
+ // No-op
+ break
+ case *types.Configure:
+ return w.handleConfigure(msg)
+ case *types.Connect:
+ if w.stop != nil {
+ return errors.New("already connected")
+ }
+ return w.handleConnect(msg)
+ case *types.Reconnect:
+ if w.stop == nil {
+ return errors.New("not connected")
+ }
+ close(w.stop)
+ return w.handleConnect(&types.Connect{Message: msg.Message})
+ case *types.Disconnect:
+ if w.stop == nil {
+ return errors.New("not connected")
+ }
+ close(w.stop)
+ return nil
+ case *types.ListDirectories:
+ return w.handleListDirectories(msg)
+ case *types.OpenDirectory:
+ return w.handleOpenDirectory(msg)
+ case *types.FetchDirectoryContents:
+ return w.handleFetchDirectoryContents(msg)
+ case *types.SearchDirectory:
+ return w.handleSearchDirectory(msg)
+ case *types.CreateDirectory:
+ return w.handleCreateDirectory(msg)
+ case *types.RemoveDirectory:
+ return w.handleRemoveDirectory(msg)
+ case *types.FetchMessageHeaders:
+ return w.handleFetchMessageHeaders(msg)
+ case *types.FetchMessageBodyPart:
+ return w.handleFetchMessageBodyPart(msg)
+ case *types.FetchFullMessages:
+ return w.handleFetchFullMessages(msg)
+ case *types.FlagMessages:
+ return w.updateFlags(msg.Uids, msg.Flags, msg.Enable)
+ case *types.AnsweredMessages:
+ return w.updateFlags(msg.Uids, models.AnsweredFlag, msg.Answered)
+ case *types.DeleteMessages:
+ return w.moveCopy(msg.Uids, "", true)
+ case *types.CopyMessages:
+ return w.moveCopy(msg.Uids, msg.Destination, false)
+ case *types.MoveMessages:
+ return w.moveCopy(msg.Uids, msg.Destination, true)
+ case *types.ModifyLabels:
+ if w.config.useLabels {
+ return w.handleModifyLabels(msg)
+ }
+ case *types.AppendMessage:
+ return w.handleAppendMessage(msg)
+ case *types.StartSendingMessage:
+ return w.handleStartSend(msg)
+ }
+ return errUnsupported
+}
+
+func (w *JMAPWorker) Run() {
+ for {
+ select {
+ case change := <-w.changes:
+ err := w.refresh(change)
+ if err != nil {
+ w.w.Errorf("refresh: %s", err)
+ }
+ case msg := <-w.w.Actions:
+ msg = w.w.ProcessAction(msg)
+ err := w.handleMessage(msg)
+ switch {
+ case errors.Is(err, errUnsupported):
+ w.w.PostMessage(&types.Unsupported{
+ Message: types.RespondTo(msg),
+ }, nil)
+ case err != nil:
+ w.w.PostMessage(&types.Error{
+ Message: types.RespondTo(msg),
+ Error: err,
+ }, nil)
+ default:
+ w.w.PostMessage(&types.Done{
+ Message: types.RespondTo(msg),
+ }, nil)
+ }
+ }
+ }
+}