mirror of
https://git.sr.ht/~rjarry/aerc
synced 2026-03-02 04:04:26 +01:00
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>
72 lines
1.7 KiB
Go
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)
|
|
}
|