1
0
Fork 0
mirror of https://git.sr.ht/~rjarry/aerc synced 2026-03-02 04:04:26 +01:00
aerc/worker/maildir/search.go
Robin Jarry c8aa9da956 worker: add explicit directory to action messages
Previously, worker action messages did not specify which directory they
targeted. Workers implicitly assumed operations applied to the currently
selected folder. This assumption breaks when the UI needs to perform
operations on a folder different from the one currently open, such as
when fetching messages from a background account or handling move/copy
operations across folders.

Add Directory field to FetchDirectoryContents, FetchDirectoryThreaded,
SearchDirectory, FetchMessageHeaders, FetchFullMessages,
FetchMessageBodyPart, FetchMessageFlags, DeleteMessages, FlagMessages,
AnsweredMessages, and ForwardedMessages. Add Source field to
CopyMessages and MoveMessages to specify the origin folder.

The message store now populates these fields with its directory name.
Each worker backend validates the directory and switches context when
necessary. For IMAP, this means issuing a SELECT command when the
requested directory differs from the currently selected mailbox.

Update the foldermapper middleware to translate the new Directory and
Source fields between external and internal folder names in both
ProcessAction and PostMessage.

In the maildir backend, pass the resolved directory to search and
msgInfoFromUid instead of relying on the selected folder state. This
ensures FetchMessageHeaders and SearchDirectory operate on the correct
directory when it differs from the currently selected one.

Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
2026-02-09 14:46:27 +01:00

72 lines
1.7 KiB
Go

package maildir
import (
"context"
"runtime"
"sync"
"github.com/emersion/go-maildir"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/lib"
"git.sr.ht/~rjarry/aerc/worker/types"
)
func (w *Worker) search(
ctx context.Context, dir maildir.Dir, criteria *types.SearchCriteria,
) ([]models.UID, error) {
criteria.PrepareHeader()
requiredParts := lib.GetRequiredParts(criteria)
w.worker.Debugf("Required parts bitmask for search: %b", requiredParts)
keys, err := w.c.UIDs(dir)
if err != nil {
return nil, err
}
var matchedUids []models.UID
mu := sync.Mutex{}
wg := sync.WaitGroup{}
// Hard limit at 2x CPU cores
max := runtime.NumCPU() * 2
limit := make(chan struct{}, max)
for _, key := range keys {
select {
case <-ctx.Done():
return nil, context.Canceled
default:
limit <- struct{}{}
wg.Add(1)
go func(key models.UID) {
defer log.PanicHandler()
defer wg.Done()
success, err := w.searchKey(dir, key, criteria, requiredParts)
if err != nil {
// don't return early so that we can still get some results
w.worker.Errorf("Failed to search key %d: %v", key, err)
} else if success {
mu.Lock()
matchedUids = append(matchedUids, key)
mu.Unlock()
}
<-limit
}(key)
}
}
wg.Wait()
return matchedUids, nil
}
// Execute the search criteria for the given key, returns true if search succeeded
func (w *Worker) searchKey(
dir maildir.Dir, key models.UID, criteria *types.SearchCriteria,
parts lib.MsgParts,
) (bool, error) {
message, err := w.c.Message(dir, key)
if err != nil {
return false, err
}
return lib.SearchMessage(message, criteria, parts)
}