1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
package middleware
import (
"strconv"
"strings"
"sync"
"git.sr.ht/~rjarry/aerc/worker/imap/extensions/xgmext"
"git.sr.ht/~rjarry/aerc/worker/types"
"github.com/emersion/go-imap/client"
)
type gmailWorker struct {
types.WorkerInteractor
mu sync.Mutex
client *client.Client
}
// NewGmailWorker returns an IMAP middleware for the X-GM-EXT-1 extension
func NewGmailWorker(base types.WorkerInteractor, c *client.Client,
) types.WorkerInteractor {
base.Infof("loading worker middleware: X-GM-EXT-1")
// avoid double wrapping; unwrap and check for another gmail handler
for iter := base; iter != nil; iter = iter.Unwrap() {
if g, ok := iter.(*gmailWorker); ok {
base.Infof("already loaded; resetting")
err := g.reset(c)
if err != nil {
base.Errorf("reset failed: %v", err)
}
return base
}
}
return &gmailWorker{WorkerInteractor: base, client: c}
}
func (g *gmailWorker) Unwrap() types.WorkerInteractor {
return g.WorkerInteractor
}
func (g *gmailWorker) reset(c *client.Client) error {
g.mu.Lock()
defer g.mu.Unlock()
g.client = c
return nil
}
func (g *gmailWorker) ProcessAction(msg types.WorkerMessage) types.WorkerMessage {
switch msg := msg.(type) {
case *types.FetchMessageHeaders:
handler := xgmext.NewHandler(g.client)
g.mu.Lock()
uids, err := handler.FetchEntireThreads(msg.Uids)
g.mu.Unlock()
if err != nil {
g.Warnf("failed to fetch entire threads: %v", err)
}
if len(uids) > 0 {
msg.Uids = uids
}
case *types.FetchDirectoryContents:
if msg.Filter == nil || (msg.Filter != nil &&
len(msg.Filter.Terms) == 0) {
break
}
if !msg.Filter.UseExtension {
g.Debugf("use regular imap filter instead of X-GM-EXT1: " +
"extension flag not set")
break
}
search := strings.Join(msg.Filter.Terms, " ")
g.Debugf("X-GM-EXT1 filter term: '%s'", search)
handler := xgmext.NewHandler(g.client)
g.mu.Lock()
uids, err := handler.RawSearch(strconv.Quote(search))
g.mu.Unlock()
if err != nil {
g.Errorf("X-GM-EXT1 filter failed: %v", err)
g.Warnf("falling back to imap filtering")
break
}
g.PostMessage(&types.DirectoryContents{
Message: types.RespondTo(msg),
Uids: uids,
}, nil)
g.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
return &types.Unsupported{}
case *types.SearchDirectory:
if msg.Criteria == nil || (msg.Criteria != nil &&
len(msg.Criteria.Terms) == 0) {
break
}
if !msg.Criteria.UseExtension {
g.Debugf("use regular imap search instead of X-GM-EXT1: " +
"extension flag not set")
break
}
search := strings.Join(msg.Criteria.Terms, " ")
g.Debugf("X-GM-EXT1 search term: '%s'", search)
handler := xgmext.NewHandler(g.client)
g.mu.Lock()
uids, err := handler.RawSearch(strconv.Quote(search))
g.mu.Unlock()
if err != nil {
g.Errorf("X-GM-EXT1 search failed: %v", err)
g.Warnf("falling back to regular imap search.")
break
}
g.PostMessage(&types.SearchResults{
Message: types.RespondTo(msg),
Uids: uids,
}, nil)
g.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
return &types.Unsupported{}
}
return g.WorkerInteractor.ProcessAction(msg)
}
|