From c0f4b2632045b53f08f1c9f6e1b0e56713ec7ba4 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Wed, 7 Jun 2023 00:57:40 +0200 Subject: wip Signed-off-by: Robin Jarry --- worker/jmap/configure.go | 18 +++++++++++----- worker/jmap/connect.go | 10 ++++----- worker/jmap/directories.go | 3 +++ worker/jmap/list.go | 53 ++++++++++++++++++++++++++++++++++++++-------- worker/jmap/mboxstate.go | 3 +++ worker/jmap/set.go | 42 +++++++++++++++++++++++++++++------- worker/jmap/worker.go | 17 +++++++++------ 7 files changed, 113 insertions(+), 33 deletions(-) diff --git a/worker/jmap/configure.go b/worker/jmap/configure.go index 889ef06e..f423ce4b 100644 --- a/worker/jmap/configure.go +++ b/worker/jmap/configure.go @@ -15,8 +15,8 @@ func (w *JMAPWorker) handleConfigure(msg *types.Configure) error { } if strings.HasSuffix(u.Scheme, "+oauthbearer") { - w.oauth = true - w.query = u.Query() + w.config.oauth = true + w.config.query = u.Query() } else { if u.User == nil { return fmt.Errorf("user:password not specified") @@ -29,12 +29,20 @@ func (w *JMAPWorker) handleConfigure(msg *types.Configure) error { u.RawQuery = "" u.Fragment = "" - w.user = u.User + w.config.user = u.User u.User = nil u.Scheme = "https" - w.endpoint = u.String() - w.account = msg.Config + w.config.endpoint = u.String() + w.config.account = msg.Config + switch strings.ToLower(msg.Config.Params["use-labels"]) { + case "1", "t", "true", "yes", "y", "on": + w.config.useLabels = true + } + w.config.allMailName = msg.Config.Params["all-mail-name"] + if w.config.allMailName == "" { + w.config.allMailName = "All mail" + } return nil } diff --git a/worker/jmap/connect.go b/worker/jmap/connect.go index 740dd251..b4ce45ec 100644 --- a/worker/jmap/connect.go +++ b/worker/jmap/connect.go @@ -8,14 +8,14 @@ import ( func (w *JMAPWorker) handleConnect(msg *types.Connect) error { w.w.Debugf("connecting") - client := &jmap.Client{SessionEndpoint: w.endpoint} + client := &jmap.Client{SessionEndpoint: w.config.endpoint} - if w.oauth { - pass, _ := w.user.Password() + if w.config.oauth { + pass, _ := w.config.user.Password() client.WithAccessToken(pass) } else { - user := w.user.Username() - pass, _ := w.user.Password() + user := w.config.user.Username() + pass, _ := w.config.user.Password() client.WithBasicAuth(user, pass) } diff --git a/worker/jmap/directories.go b/worker/jmap/directories.go index dbbb521a..71c0a838 100644 --- a/worker/jmap/directories.go +++ b/worker/jmap/directories.go @@ -130,6 +130,9 @@ func (w *JMAPWorker) handleCreateDirectory(msg *types.CreateDirectory) error { if err != nil { return err } + if msg.Quiet { + return nil + } for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *mailbox.SetResponse: diff --git a/worker/jmap/list.go b/worker/jmap/list.go index cba3db91..cd11707d 100644 --- a/worker/jmap/list.go +++ b/worker/jmap/list.go @@ -11,6 +11,7 @@ import ( func (w *JMAPWorker) handleListDirectories(msg *types.ListDirectories) error { var req jmap.Request + var ids []jmap.ID w.w.Debugf("listing directories") @@ -23,8 +24,17 @@ func (w *JMAPWorker) handleListDirectories(msg *types.ListDirectories) error { for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *mailbox.GetResponse: + ids = make([]jmap.ID, 0, len(r.List)) for _, mbox := range r.List { w.mboxes[mbox.ID] = &MailboxState{mbox: mbox} + if mbox.Role == "archive" && w.config.useLabels { + w.mboxes[""] = &MailboxState{ + dir: w.config.allMailName, + } + ids = append(ids, "") + } else { + ids = append(ids, mbox.ID) + } } w.mboxState = r.State } @@ -34,17 +44,42 @@ func (w *JMAPWorker) handleListDirectories(msg *types.ListDirectories) error { } for _, m := range w.mboxes { + if m.mbox == nil { + w.dir2mbox[m.dir] = "" + continue + } m.dir = m.FullPath(w.mboxes) w.dir2mbox[m.dir] = m.mbox.ID - w.w.PostMessage(&types.Directory{ - Message: types.RespondTo(msg), - Dir: &models.Directory{ - Name: m.dir, - Exists: int(m.mbox.TotalEmails), - Unseen: int(m.mbox.UnreadEmails), - Role: jmapRole2aerc[m.mbox.Role], - }, - }, nil) + } + + for _, id := range ids { + m := w.mboxes[id] + if m.mbox == nil { + w.w.PostMessage(&types.Directory{ + Message: types.RespondTo(msg), + Dir: &models.Directory{ + Name: m.dir, + Role: models.AllRole, + }, + }, nil) + } else { + role := jmapRole2aerc[m.mbox.Role] + switch role { + case models.ArchiveRole: + w.archiveMbox = m.mbox.ID + case models.TrashRole: + w.trashMbox = m.mbox.ID + } + w.w.PostMessage(&types.Directory{ + Message: types.RespondTo(msg), + Dir: &models.Directory{ + Name: m.dir, + Exists: int(m.mbox.TotalEmails), + Unseen: int(m.mbox.UnreadEmails), + Role: role, + }, + }, nil) + } } go w.monitorChanges() diff --git a/worker/jmap/mboxstate.go b/worker/jmap/mboxstate.go index 0ee8bde3..975d18ed 100644 --- a/worker/jmap/mboxstate.go +++ b/worker/jmap/mboxstate.go @@ -17,6 +17,9 @@ type MailboxState struct { } func (s *MailboxState) FullPath(all map[jmap.ID]*MailboxState) string { + if s.mbox == nil { + return s.dir + } if s.mbox.ParentID == "" { return s.mbox.Name } diff --git a/worker/jmap/set.go b/worker/jmap/set.go index d5d4990e..1661f5b1 100644 --- a/worker/jmap/set.go +++ b/worker/jmap/set.go @@ -59,36 +59,50 @@ func (w *JMAPWorker) updateFlags(uids []uint32, flags models.Flags, enable bool) return nil } -func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, move bool) error { +func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, deleteSrc bool) error { var req jmap.Request - var dest jmap.ID + var destMbox jmap.ID + var destroy []jmap.ID var ok bool - update := make(map[jmap.ID]jmap.Patch) + patches := make(map[jmap.ID]jmap.Patch) - dest, ok = w.dir2mbox[destDir] + destMbox, ok = w.dir2mbox[destDir] if !ok && destDir != "" { return fmt.Errorf("unknown destination mailbox") } for _, uid := range uids { + dest := destMbox id, ok := w.uidStore.GetKey(uid) if !ok { return fmt.Errorf("bug: unknown uid %d", uid) } + mail, ok := w.emails[jmap.ID(id)] + if !ok { + return fmt.Errorf("bug: unknown message id %s", id) + } patch := jmap.Patch{} - if dest != "" { + if dest == "" { + dest = w.fallbackMbox(mail) + } + if dest != "" && dest != w.selectedMbox { patch[fmt.Sprintf("mailboxIds/%s", dest)] = true } - if move { + if deleteSrc { + if len(patch) == 0 { + destroy = append(destroy, mail.ID) + continue + } patch[fmt.Sprintf("mailboxIds/%s", w.selectedMbox)] = nil } - update[jmap.ID(id)] = patch + patches[jmap.ID(id)] = patch } req.Invoke(&email.Set{ Account: w.accountId(), - Update: update, + Update: patches, + Destroy: destroy, }) resp, err := w.client.Do(&req) @@ -116,3 +130,15 @@ func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, move bool) error { return nil } + +func (w *JMAPWorker) fallbackMbox(mail *email.Email) jmap.ID { + switch { + case len(mail.MailboxIDs) > 1: + return "" + case w.config.useLabels && w.archiveMbox != "": + return w.archiveMbox + case w.trashMbox != "": + return w.trashMbox + } + return "" +} diff --git a/worker/jmap/worker.go b/worker/jmap/worker.go index f0b3de81..6adc0289 100644 --- a/worker/jmap/worker.go +++ b/worker/jmap/worker.go @@ -21,17 +21,22 @@ func init() { } type JMAPWorker struct { - // config - account *config.AccountConfig - endpoint string - oauth bool - user *url.Userinfo - query url.Values + config struct { + account *config.AccountConfig + endpoint string + oauth bool + user *url.Userinfo + query url.Values + useLabels bool + allMailName string + } w *types.Worker client *jmap.Client selectedMbox jmap.ID + archiveMbox jmap.ID + trashMbox jmap.ID mboxes map[jmap.ID]*MailboxState dir2mbox map[string]jmap.ID uidStore *uidstore.Store -- cgit v1.2.3-54-g00ecf