aboutsummaryrefslogtreecommitdiffstats
path: root/worker/middleware/gmailworker.go
blob: 4f5f54562478e5d75063c6f638156701fc502351 (plain) (blame)
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)
}