diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2022-05-30 07:34:18 -0500 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-05-31 14:32:51 +0200 |
commit | 2551dd1bfa2c68a6ba8644a0c45b24fce8874674 (patch) | |
tree | ed752720e1a08708505fd9574b49629d5df84997 /worker | |
parent | 30d57889741cfa8284eec9b32b29144fe01002a2 (diff) | |
download | aerc-2551dd1bfa2c68a6ba8644a0c45b24fce8874674.tar.gz |
feat: add background mail polling option for all workers
Check for new mail (recent, unseen, exists counts) with an external
command, or for imap with the STATUS command, at start or on
reconnection and every X time duration
IMAP:
The selected folder is skipped, per specification. Additional config
options are included for including/excluding folders explicitly.
Maildir/Notmuch:
An external command will be run in the background to check for new mail.
An optional timeout can be used with maildir/notmuch. Default is 10s
New account options:
check-mail
check-mail-cmd (maildir/notmuch only)
check-mail-timeout (maildir/notmuch only), default 10s
check-mail-include (IMAP only)
check-mail-exclude (IMAP only)
If unset, or set less than or equal to 0, check-mail will be ignored
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Moritz Poldrack <moritz@poldrack.dev>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'worker')
-rw-r--r-- | worker/imap/checkmail.go | 40 | ||||
-rw-r--r-- | worker/imap/worker.go | 2 | ||||
-rw-r--r-- | worker/maildir/worker.go | 53 | ||||
-rw-r--r-- | worker/notmuch/worker.go | 26 | ||||
-rw-r--r-- | worker/types/messages.go | 10 |
5 files changed, 119 insertions, 12 deletions
diff --git a/worker/imap/checkmail.go b/worker/imap/checkmail.go new file mode 100644 index 00000000..d9dcfd33 --- /dev/null +++ b/worker/imap/checkmail.go @@ -0,0 +1,40 @@ +package imap + +import ( + "git.sr.ht/~rjarry/aerc/models" + "git.sr.ht/~rjarry/aerc/worker/types" + "github.com/emersion/go-imap" +) + +func (w *IMAPWorker) handleCheckMailMessage(msg *types.CheckMail) { + items := []imap.StatusItem{ + imap.StatusMessages, + imap.StatusRecent, + imap.StatusUnseen, + } + for _, dir := range msg.Directories { + w.worker.Logger.Printf("Getting status of directory %s", dir) + status, err := w.client.Status(dir, items) + if err != nil { + w.worker.PostMessage(&types.Error{ + Message: types.RespondTo(msg), + Error: err, + }, nil) + } else { + w.worker.PostMessage(&types.DirectoryInfo{ + Info: &models.DirectoryInfo{ + Flags: status.Flags, + Name: status.Name, + ReadOnly: status.ReadOnly, + AccurateCounts: true, + + Exists: int(status.Messages), + Recent: int(status.Recent), + Unseen: int(status.Unseen), + }, + SkipSort: true, + }, nil) + } + } + w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil) +} diff --git a/worker/imap/worker.go b/worker/imap/worker.go index eabaae02..da0716e1 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -190,6 +190,8 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { w.handleAppendMessage(msg) case *types.SearchDirectory: w.handleSearchDirectory(msg) + case *types.CheckMail: + w.handleCheckMailMessage(msg) default: reterr = errUnsupported } diff --git a/worker/maildir/worker.go b/worker/maildir/worker.go index 4a2a19b8..d3de8447 100644 --- a/worker/maildir/worker.go +++ b/worker/maildir/worker.go @@ -2,12 +2,14 @@ package maildir import ( "bytes" + "context" "errors" "fmt" "io" "io/ioutil" "net/url" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -60,17 +62,25 @@ func (w *Worker) Run() { func (w *Worker) handleAction(action types.WorkerMessage) { msg := w.worker.ProcessAction(action) - if err := w.handleMessage(msg); err == errUnsupported { - w.worker.PostMessage(&types.Unsupported{ - Message: types.RespondTo(msg), - }, nil) - } else if err != nil { - w.worker.PostMessage(&types.Error{ - Message: types.RespondTo(msg), - Error: err, - }, nil) - } else { - w.done(msg) + switch msg := msg.(type) { + // Explicitly handle all asynchronous actions. Async actions are + // responsible for posting their own Done message + case *types.CheckMail: + go w.handleCheckMail(msg) + default: + // Default handling, will be performed synchronously + if err := w.handleMessage(msg); err == errUnsupported { + w.worker.PostMessage(&types.Unsupported{ + Message: types.RespondTo(msg), + }, nil) + } else if err != nil { + w.worker.PostMessage(&types.Error{ + Message: types.RespondTo(msg), + Error: err, + }, nil) + } else { + w.done(msg) + } } } @@ -672,3 +682,24 @@ func (w *Worker) msgInfoFromUid(uid uint32) (*models.MessageInfo, error) { } return info, nil } + +func (w *Worker) handleCheckMail(msg *types.CheckMail) { + ctx, cancel := context.WithTimeout(context.Background(), msg.Timeout) + defer cancel() + cmd := exec.CommandContext(ctx, "sh", "-c", msg.Command) + ch := make(chan error) + go func() { + err := cmd.Run() + ch <- err + }() + select { + case <-ctx.Done(): + w.err(msg, fmt.Errorf("checkmail: timed out")) + case err := <-ch: + if err != nil { + w.err(msg, fmt.Errorf("checkmail: error running command: %v", err)) + } else { + w.done(msg) + } + } +} diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go index 4091ea43..c1426f28 100644 --- a/worker/notmuch/worker.go +++ b/worker/notmuch/worker.go @@ -6,10 +6,12 @@ package notmuch import ( "bufio" "bytes" + "context" "fmt" "io/ioutil" "net/url" "os" + "os/exec" "path/filepath" "strings" "time" @@ -128,6 +130,9 @@ func (w *worker) handleMessage(msg types.WorkerMessage) error { return w.handleSearchDirectory(msg) case *types.ModifyLabels: return w.handleModifyLabels(msg) + case *types.CheckMail: + go w.handleCheckMail(msg) + return nil // not implemented, they are generally not used // in a notmuch based workflow @@ -616,3 +621,24 @@ func (w *worker) sort(uids []uint32, } return sortedUids, nil } + +func (w *worker) handleCheckMail(msg *types.CheckMail) { + ctx, cancel := context.WithTimeout(context.Background(), msg.Timeout) + defer cancel() + cmd := exec.CommandContext(ctx, "sh", "-c", msg.Command) + ch := make(chan error) + go func() { + err := cmd.Run() + ch <- err + }() + select { + case <-ctx.Done(): + w.err(msg, fmt.Errorf("checkmail: timed out")) + case err := <-ch: + if err != nil { + w.err(msg, fmt.Errorf("checkmail: error running command: %v", err)) + } else { + w.done(msg) + } + } +} diff --git a/worker/types/messages.go b/worker/types/messages.go index d8f1f568..5cd3768c 100644 --- a/worker/types/messages.go +++ b/worker/types/messages.go @@ -167,6 +167,13 @@ type AppendMessage struct { Length int } +type CheckMail struct { + Message + Directories []string + Command string + Timeout time.Duration +} + // Messages type Directory struct { @@ -176,7 +183,8 @@ type Directory struct { type DirectoryInfo struct { Message - Info *models.DirectoryInfo + Info *models.DirectoryInfo + SkipSort bool } type DirectoryContents struct { |