package jmap import ( "fmt" "path" "git.sr.ht/~rjarry/aerc/worker/types" "git.sr.ht/~rockorager/go-jmap" "git.sr.ht/~rockorager/go-jmap/mail/email" "git.sr.ht/~rockorager/go-jmap/mail/mailbox" ) func (w *JMAPWorker) handleOpenDirectory(msg *types.OpenDirectory) error { id, ok := w.dir2mbox[msg.Directory] if !ok { return fmt.Errorf("unknown directory: %s", msg.Directory) } w.selectedMbox = id return nil } func (w *JMAPWorker) handleFetchDirectoryContents(msg *types.FetchDirectoryContents) error { var req jmap.Request selected, ok := w.mboxes[w.selectedMbox] if !ok { return fmt.Errorf("no selected mailbox") } filter, err := parseSearch(msg.FilterCriteria) if err != nil { return err } filter.InMailbox = w.selectedMbox selected.filter = filter selected.sort = translateSort(msg.SortCriteria) req.Invoke(&email.Query{ Account: w.accountId(), Filter: filter, Sort: selected.sort, }) resp, err := w.client.Do(&req) if err != nil { return err } for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *email.QueryResponse: var uids []uint32 for _, id := range r.IDs { uids = append(uids, w.uidStore.GetOrInsert(string(id))) } w.w.PostMessage(&types.DirectoryContents{ Message: types.RespondTo(msg), Uids: uids, }, nil) selected.uids = uids selected.queryState = r.QueryState } } return nil } func (w *JMAPWorker) handleSearchDirectory(msg *types.SearchDirectory) error { var req jmap.Request filter, err := parseSearch(msg.Argv) if err != nil { return err } filter.InMailbox = w.selectedMbox req.Invoke(&email.Query{ Account: w.accountId(), Filter: filter, }) resp, err := w.client.Do(&req) if err != nil { return err } for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *email.QueryResponse: var uids []uint32 for _, id := range r.IDs { uids = append(uids, w.uidStore.GetOrInsert(string(id))) } w.w.PostMessage(&types.SearchResults{ Message: types.RespondTo(msg), Uids: uids, }, nil) } } return nil } func (w *JMAPWorker) handleCreateDirectory(msg *types.CreateDirectory) error { var req jmap.Request var parentId, id jmap.ID if parent := path.Dir(msg.Directory); parent != "" && parent != "." { var ok bool if parentId, ok = w.dir2mbox[parent]; !ok { return fmt.Errorf( "parent mailbox %q does not exist", parent) } } name := path.Base(msg.Directory) req.Invoke(&mailbox.Set{ Account: w.accountId(), Create: map[jmap.ID]*mailbox.Mailbox{ id: { ID: id, ParentID: parentId, Name: name, }, }, }) resp, err := w.client.Do(&req) if err != nil { return err } if msg.Quiet { return nil } for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *mailbox.SetResponse: if err, _ := r.NotCreated[id]; err != nil { var s string if err.Description != nil { s = *err.Description } return fmt.Errorf( "mailbox creation failed: %s", s) } } } return nil } func (w *JMAPWorker) handleRemoveDirectory(msg *types.RemoveDirectory) error { var req jmap.Request id, ok := w.dir2mbox[msg.Directory] if !ok { return fmt.Errorf("unknown mailbox: %s", msg.Directory) } req.Invoke(&mailbox.Set{ Account: w.accountId(), Destroy: []jmap.ID{id}, OnDestroyRemoveEmails: msg.Quiet, }) resp, err := w.client.Do(&req) if err != nil { return err } for _, inv := range resp.Responses { switch r := inv.Args.(type) { case *mailbox.SetResponse: if err, _ := r.NotDestroyed[id]; err != nil { var s string if err.Description != nil { s = *err.Description } return fmt.Errorf( "mailbox destruction failed: %s", s) } } } return nil } func translateSort(criteria []*types.SortCriterion) []*email.SortComparator { sort := make([]*email.SortComparator, 0, len(criteria)) if len(criteria) == 0 { criteria = []*types.SortCriterion{ {Field: types.SortArrival, Reverse: true}, {Field: types.SortSubject, Reverse: false}, } } for _, s := range criteria { var cmp email.SortComparator switch s.Field { case types.SortArrival: cmp.Property = "receivedAt" case types.SortCc: cmp.Property = "cc" case types.SortDate: cmp.Property = "receivedAt" case types.SortFrom: cmp.Property = "from" case types.SortRead: cmp.Keyword = "$seen" case types.SortSize: cmp.Property = "size" case types.SortSubject: cmp.Property = "subject" case types.SortTo: cmp.Property = "to" default: continue } cmp.IsAscending = !s.Reverse sort = append(sort, &cmp) } return sort }