Files
proton-bridge/internal/user/events.go
2022-11-16 12:26:08 +01:00

231 lines
5.5 KiB
Go

package user
import (
"context"
"github.com/ProtonMail/gluon/imap"
"github.com/ProtonMail/proton-bridge/v2/internal/events"
"github.com/bradenaw/juniper/xslices"
"gitlab.protontech.ch/go/liteapi"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// handleAPIEvent handles the given liteapi.Event.
func (user *User) handleAPIEvent(event liteapi.Event) error {
if event.User != nil {
if err := user.handleUserEvent(*event.User); err != nil {
return err
}
}
if len(event.Addresses) > 0 {
if err := user.handleAddressEvents(event.Addresses); err != nil {
return err
}
}
if event.MailSettings != nil {
if err := user.handleMailSettingsEvent(*event.MailSettings); err != nil {
return err
}
}
if len(event.Labels) > 0 {
if err := user.handleLabelEvents(event.Labels); err != nil {
return err
}
}
if len(event.Messages) > 0 {
if err := user.handleMessageEvents(event.Messages); err != nil {
return err
}
}
return nil
}
// handleUserEvent handles the given user event.
func (user *User) handleUserEvent(userEvent liteapi.User) error {
userKR, err := userEvent.Keys.Unlock(user.vault.KeyPass(), nil)
if err != nil {
return err
}
user.apiUser = userEvent
user.userKR = userKR
user.notifyCh <- events.UserChanged{
UserID: user.ID(),
}
return nil
}
// handleAddressEvents handles the given address events.
// TODO: If split address mode, need to signal back to bridge to update the addresses!
func (user *User) handleAddressEvents(addressEvents []liteapi.AddressEvent) error {
for _, event := range addressEvents {
switch event.Action {
case liteapi.EventDelete:
address, err := user.deleteAddress(event.ID)
if err != nil {
return err
}
// TODO: This is not the same as addressChangedLogout event!
// That was only relevant in split mode. This is used differently now.
user.notifyCh <- events.UserAddressDeleted{
UserID: user.ID(),
Address: address.Email,
}
case liteapi.EventCreate:
if err := user.createAddress(event.Address); err != nil {
return err
}
user.notifyCh <- events.UserAddressCreated{
UserID: user.ID(),
Address: event.Address.Email,
}
case liteapi.EventUpdate:
if err := user.updateAddress(event.Address); err != nil {
return err
}
user.notifyCh <- events.UserAddressChanged{
UserID: user.ID(),
Address: event.Address.Email,
}
}
}
return nil
}
// createAddress creates the given address.
func (user *User) createAddress(address liteapi.Address) error {
addrKR, err := address.Keys.Unlock(user.vault.KeyPass(), user.userKR)
if err != nil {
return err
}
if user.imapConn != nil {
user.imapConn.addAddress(address.Email)
}
user.addresses = append(user.addresses, address)
user.addrKRs[address.ID] = addrKR
return nil
}
// updateAddress updates the given address.
func (user *User) updateAddress(address liteapi.Address) error {
if _, err := user.deleteAddress(address.ID); err != nil {
return err
}
return user.createAddress(address)
}
// deleteAddress deletes the given address.
func (user *User) deleteAddress(addressID string) (liteapi.Address, error) {
idx := xslices.IndexFunc(user.addresses, func(address liteapi.Address) bool {
return address.ID == addressID
})
if idx < 0 {
return liteapi.Address{}, ErrNoSuchAddress
}
if user.imapConn != nil {
user.imapConn.remAddress(user.addresses[idx].Email)
}
var address liteapi.Address
address, user.addresses = user.addresses[idx], append(user.addresses[:idx], user.addresses[idx+1:]...)
delete(user.addrKRs, addressID)
return address, nil
}
// handleMailSettingsEvent handles the given mail settings event.
func (user *User) handleMailSettingsEvent(mailSettingsEvent liteapi.MailSettings) error {
user.settings = mailSettingsEvent
return nil
}
// handleLabelEvents handles the given label events.
func (user *User) handleLabelEvents(labelEvents []liteapi.LabelEvent) error {
for _, event := range labelEvents {
switch event.Action {
case liteapi.EventDelete:
user.updateCh <- imap.NewMailboxDeleted(imap.LabelID(event.ID))
case liteapi.EventCreate:
user.updateCh <- newMailboxCreatedUpdate(imap.LabelID(event.ID), getMailboxName(event.Label))
case liteapi.EventUpdate, liteapi.EventUpdateFlags:
user.updateCh <- imap.NewMailboxUpdated(imap.LabelID(event.ID), getMailboxName(event.Label))
}
}
return nil
}
// handleMessageEvents handles the given message events.
func (user *User) handleMessageEvents(messageEvents []liteapi.MessageEvent) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for _, event := range messageEvents {
switch event.Action {
case liteapi.EventDelete:
return ErrNotImplemented
case liteapi.EventCreate:
messages, err := user.builder.ProcessAll(ctx, []request{{event.ID, user.addrKRs[event.Message.AddressID]}})
if err != nil {
return err
}
user.updateCh <- imap.NewMessagesCreated(maps.Values(messages)...)
case liteapi.EventUpdate, liteapi.EventUpdateFlags:
user.updateCh <- imap.NewMessageLabelsUpdated(
imap.MessageID(event.ID),
imapLabelIDs(filterLabelIDs(event.Message.LabelIDs)),
bool(!event.Message.Unread),
slices.Contains(event.Message.LabelIDs, liteapi.StarredLabel),
)
}
}
return nil
}
func getMailboxName(label liteapi.Label) []string {
var name []string
switch label.Type {
case liteapi.LabelTypeFolder:
name = []string{folderPrefix, label.Name}
case liteapi.LabelTypeLabel:
name = []string{labelPrefix, label.Name}
default:
name = []string{label.Name}
}
return name
}