aboutsummaryrefslogtreecommitdiffstats
path: root/worker/imap
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-10-17 15:31:09 +0200
committerRobin Jarry <robin@jarry.cc>2023-10-28 19:24:59 +0200
commit8464b373851142b0becaaa10db34df3559b2b62e (patch)
treedd785d6c717f1e620c63252348d8add991587a57 /worker/imap
parent57088312fdd8e602a084bd5736a0e22a34be9ec0 (diff)
downloadaerc-8464b373851142b0becaaa10db34df3559b2b62e.tar.gz
search: use a common api for all workers
Define a SearchCriteria structure. Update the FetchDirectoryContents, FetchDirectoryThreaded and SearchDirectory worker messages to include this SearchCriteria structure instead of a []string slice. Parse the search arguments in a single place into a SearchCriteria structure and use it to search/filter via the message store. Update all workers to use that new API. Clarify the man page indicating that notmuch supports searching with aerc's syntax and also with notmuch specific syntax. getopt is no longer needed, remove it from go.mod. NB: to support more complex search filters in JMAP, we need to use an email.Filter interface. Since GOB does not support encoding/decoding interfaces, store the raw SearchCriteria and []SortCriterion values in the cached FolderContents. Translate them to JMAP API objects when sending an email.Query request to the server. Signed-off-by: Robin Jarry <robin@jarry.cc> Reviewed-by: Koni Marti <koni.marti@gmail.com> Tested-by: Moritz Poldrack <moritz@poldrack.dev> Tested-by: Inwit <inwit@sindominio.net>
Diffstat (limited to 'worker/imap')
-rw-r--r--worker/imap/list.go6
-rw-r--r--worker/imap/open.go23
-rw-r--r--worker/imap/search.go115
3 files changed, 41 insertions, 103 deletions
diff --git a/worker/imap/list.go b/worker/imap/list.go
index fc1d7e94..5a97d5e2 100644
--- a/worker/imap/list.go
+++ b/worker/imap/list.go
@@ -115,11 +115,7 @@ func (imapw *IMAPWorker) handleSearchDirectory(msg *types.SearchDirectory) {
}
imapw.worker.Tracef("Executing search")
- criteria, err := parseSearch(msg.Argv)
- if err != nil {
- emitError(err)
- return
- }
+ criteria := translateSearch(msg.Criteria)
if msg.Context.Err() != nil {
imapw.worker.PostMessage(&types.Cancelled{
diff --git a/worker/imap/open.go b/worker/imap/open.go
index 693d93a9..355709a7 100644
--- a/worker/imap/open.go
+++ b/worker/imap/open.go
@@ -39,17 +39,11 @@ func (imapw *IMAPWorker) handleFetchDirectoryContents(
}
imapw.worker.Tracef("Fetching UID list")
- searchCriteria, err := parseSearch(msg.FilterCriteria)
- if err != nil {
- imapw.worker.PostMessage(&types.Error{
- Message: types.RespondTo(msg),
- Error: err,
- }, nil)
- return
- }
+ searchCriteria := translateSearch(msg.Filter)
sortCriteria := translateSortCriterions(msg.SortCriteria)
hasSortCriteria := len(sortCriteria) > 0
+ var err error
var uids []uint32
// If the server supports the SORT extension, do the sorting server side
@@ -87,7 +81,7 @@ func (imapw *IMAPWorker) handleFetchDirectoryContents(
return
}
imapw.worker.Tracef("Found %d UIDs", len(uids))
- if len(msg.FilterCriteria) == 1 {
+ if msg.Filter == nil {
// Only initialize if we are not filtering
imapw.seqMap.Initialize(uids)
}
@@ -134,14 +128,7 @@ func (imapw *IMAPWorker) handleDirectoryThreaded(
}
imapw.worker.Tracef("Fetching threaded UID list")
- searchCriteria, err := parseSearch(msg.FilterCriteria)
- if err != nil {
- imapw.worker.PostMessage(&types.Error{
- Message: types.RespondTo(msg),
- Error: err,
- }, nil)
- return
- }
+ searchCriteria := translateSearch(msg.Filter)
threads, err := imapw.client.thread.UidThread(imapw.threadAlgorithm,
searchCriteria)
if err != nil {
@@ -154,7 +141,7 @@ func (imapw *IMAPWorker) handleDirectoryThreaded(
aercThreads, count := convertThreads(threads, nil)
sort.Sort(types.ByUID(aercThreads))
imapw.worker.Tracef("Found %d threaded messages", count)
- if len(msg.FilterCriteria) == 1 {
+ if msg.Filter == nil {
// Only initialize if we are not filtering
var uids []uint32
for i := len(aercThreads) - 1; i >= 0; i-- {
diff --git a/worker/imap/search.go b/worker/imap/search.go
index e9238190..0305a80f 100644
--- a/worker/imap/search.go
+++ b/worker/imap/search.go
@@ -1,95 +1,50 @@
package imap
import (
- "errors"
- "strings"
-
"github.com/emersion/go-imap"
- "git.sr.ht/~rjarry/aerc/lib/parse"
- "git.sr.ht/~rjarry/aerc/log"
- "git.sr.ht/~sircmpwn/getopt"
+ "git.sr.ht/~rjarry/aerc/worker/types"
+ "git.sr.ht/~rjarry/go-opt"
)
-func parseSearch(args []string) (*imap.SearchCriteria, error) {
+func translateSearch(c *types.SearchCriteria) *imap.SearchCriteria {
criteria := imap.NewSearchCriteria()
- if len(args) == 0 {
- return criteria, nil
+ if c == nil {
+ return criteria
}
+ criteria.WithFlags = translateFlags(c.WithFlags)
+ criteria.WithoutFlags = translateFlags(c.WithoutFlags)
- opts, optind, err := getopt.Getopts(args, "rubax:X:t:H:f:c:d:")
- if err != nil {
- return nil, err
+ if !c.StartDate.IsZero() {
+ criteria.SentSince = c.StartDate
}
- body := false
- text := false
- for _, opt := range opts {
- switch opt.Option {
- case 'r':
- criteria.WithFlags = append(criteria.WithFlags, imap.SeenFlag)
- case 'u':
- criteria.WithoutFlags = append(criteria.WithoutFlags, imap.SeenFlag)
- case 'x':
- if f, err := getParsedFlag(opt.Value); err == nil {
- criteria.WithFlags = append(criteria.WithFlags, f)
- }
- case 'X':
- if f, err := getParsedFlag(opt.Value); err == nil {
- criteria.WithoutFlags = append(criteria.WithoutFlags, f)
- }
- case 'H':
- if strings.Contains(opt.Value, ": ") {
- HeaderValue := strings.SplitN(opt.Value, ": ", 2)
- criteria.Header.Add(HeaderValue[0], HeaderValue[1])
- } else {
- log.Errorf("Header is not given properly, must be given in format `Header: Value`")
- continue
- }
- case 'f':
- criteria.Header.Add("From", opt.Value)
- case 't':
- criteria.Header.Add("To", opt.Value)
- case 'c':
- criteria.Header.Add("Cc", opt.Value)
- case 'b':
- body = true
- case 'a':
- text = true
- case 'd':
- start, end, err := parse.DateRange(opt.Value)
- if err != nil {
- log.Errorf("failed to parse start date: %v", err)
- continue
- }
- if !start.IsZero() {
- criteria.SentSince = start
- }
- if !end.IsZero() {
- criteria.SentBefore = end
- }
- }
+ if !c.StartDate.IsZero() {
+ criteria.SentBefore = c.EndDate
}
- switch {
- case text:
- criteria.Text = args[optind:]
- case body:
- criteria.Body = args[optind:]
- default:
- for _, arg := range args[optind:] {
- criteria.Header.Add("Subject", arg)
- }
+ for k, v := range c.Headers {
+ criteria.Header[k] = v
}
- return criteria, nil
-}
-
-func getParsedFlag(name string) (string, error) {
- switch strings.ToLower(name) {
- case "seen":
- return imap.SeenFlag, nil
- case "flagged":
- return imap.FlaggedFlag, nil
- case "answered":
- return imap.AnsweredFlag, nil
+ for _, f := range c.From {
+ criteria.Header.Add("From", f)
+ }
+ for _, t := range c.To {
+ criteria.Header.Add("To", t)
+ }
+ for _, c := range c.Cc {
+ criteria.Header.Add("Cc", c)
+ }
+ terms := opt.LexArgs(c.Terms)
+ if terms.Count() > 0 {
+ switch {
+ case c.SearchAll:
+ criteria.Text = terms.Args()
+ case c.SearchBody:
+ criteria.Body = terms.Args()
+ default:
+ for _, term := range terms.Args() {
+ criteria.Header.Add("Subject", term)
+ }
+ }
}
- return imap.FlaggedFlag, errors.New("Flag not suppored")
+ return criteria
}