Files
proton-bridge/internal/services/imapservice/service_sync.go
Leander Beernaert a187747c7c feat(GODT-2802): IMAP Serivce
Handles all IMAP related tasks. Unlike the previous iteration, this new
service automatically adds and removes users from Gluon by interfacing
with server manager.
2023-07-31 11:06:47 +02:00

127 lines
3.1 KiB
Go

// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package imapservice
import (
"context"
"fmt"
"github.com/ProtonMail/gluon/async"
"github.com/ProtonMail/gluon/imap"
)
type syncUpdate struct {
addrID string
update imap.Update
}
type syncUpdater struct {
ch chan syncUpdate
}
type syncUpdatePublisher struct {
addrID string
updater *syncUpdater
}
func (s *syncUpdatePublisher) publishUpdate(ctx context.Context, update imap.Update) {
select {
case <-ctx.Done():
update.Done(fmt.Errorf("not applied: %w", ctx.Err()))
return
case s.updater.ch <- syncUpdate{addrID: s.addrID, update: update}:
}
}
func newSyncUpdater() *syncUpdater {
return &syncUpdater{ch: make(chan syncUpdate)}
}
func (s *syncUpdater) createPublisher(addrID string) *syncUpdatePublisher {
return &syncUpdatePublisher{updater: s, addrID: addrID}
}
func (s *syncUpdater) Close() {
close(s.ch)
}
type syncHandler struct {
group *async.Group
updater *syncUpdater
syncFinishedCh chan error
}
func newSyncHandler(ctx context.Context, handler async.PanicHandler) *syncHandler {
return &syncHandler{
group: async.NewGroup(ctx, handler),
updater: newSyncUpdater(),
syncFinishedCh: make(chan error, 2),
}
}
func (s *syncHandler) Close() {
s.group.CancelAndWait()
close(s.syncFinishedCh)
}
func (s *syncHandler) CancelAndWait() {
s.group.CancelAndWait()
}
func (s *syncHandler) Cancel() {
s.group.Cancel()
}
func (s *syncHandler) OnSyncFinishedCH() <-chan error {
return s.syncFinishedCh
}
func (s *syncHandler) launch(service *Service) {
service.eventProvider.Pause()
labels := service.labels.GetLabelMap()
updaters := make(map[string]updatePublisher, len(service.connectors))
for _, c := range service.connectors {
updaters[c.addrID] = s.updater.createPublisher(c.addrID)
}
state := &syncJob{
client: service.client,
userID: service.identityState.UserID(),
labels: labels,
updaters: updaters,
addressMode: service.addressMode,
syncState: service.syncStateProvider,
eventPublisher: service.eventPublisher,
log: service.log,
// We make a copy of the identity state to avoid holding on to locks for a very long time.
identityState: service.identityState.Clone(),
panicHandler: service.panicHandler,
reporter: service.reporter,
maxSyncMemory: service.maxSyncMemory,
}
s.group.Once(func(ctx context.Context) {
err := state.run(ctx)
s.syncFinishedCh <- err
})
}