forked from Silverfish/proton-bridge
GODT-2202: Report update errors from Gluon
For every update sent to gluon wait and check the error code to see if an error occurred. Note: Updates can't be inspect on the call site as it can lead to deadlocks.
This commit is contained in:
@ -294,27 +294,43 @@ func (user *User) handleLabelEvents(ctx context.Context, labelEvents []proton.La
|
||||
for _, event := range labelEvents {
|
||||
switch event.Action {
|
||||
case proton.EventCreate:
|
||||
if err := user.handleCreateLabelEvent(ctx, event); err != nil {
|
||||
updates, err := user.handleCreateLabelEvent(ctx, event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle create label event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case proton.EventUpdate, proton.EventUpdateFlags:
|
||||
if err := user.handleUpdateLabelEvent(ctx, event); err != nil {
|
||||
updates, err := user.handleUpdateLabelEvent(ctx, event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle update label event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case proton.EventDelete:
|
||||
if err := user.handleDeleteLabelEvent(ctx, event); err != nil {
|
||||
updates, err := user.handleDeleteLabelEvent(ctx, event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to handle delete label event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) handleCreateLabelEvent(_ context.Context, event proton.LabelEvent) error { //nolint:unparam
|
||||
return safe.LockRet(func() error {
|
||||
func (user *User) handleCreateLabelEvent(ctx context.Context, event proton.LabelEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.LockRetErr(func() ([]imap.Update, error) {
|
||||
var updates []imap.Update
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"labelID": event.ID,
|
||||
"name": logging.Sensitive(event.Label.Name),
|
||||
@ -325,7 +341,9 @@ func (user *User) handleCreateLabelEvent(_ context.Context, event proton.LabelEv
|
||||
}
|
||||
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
updateCh.Enqueue(newMailboxCreatedUpdate(imap.MailboxID(event.ID), getMailboxName(event.Label)))
|
||||
update := newMailboxCreatedUpdate(imap.MailboxID(event.ID), getMailboxName(event.Label))
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelCreated{
|
||||
@ -334,12 +352,13 @@ func (user *User) handleCreateLabelEvent(_ context.Context, event proton.LabelEv
|
||||
Name: event.Label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
return updates, nil
|
||||
}, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
func (user *User) handleUpdateLabelEvent(_ context.Context, event proton.LabelEvent) error { //nolint:unparam
|
||||
return safe.LockRet(func() error {
|
||||
func (user *User) handleUpdateLabelEvent(ctx context.Context, event proton.LabelEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.LockRetErr(func() ([]imap.Update, error) {
|
||||
var updates []imap.Update
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"labelID": event.ID,
|
||||
"name": logging.Sensitive(event.Label.Name),
|
||||
@ -350,10 +369,12 @@ func (user *User) handleUpdateLabelEvent(_ context.Context, event proton.LabelEv
|
||||
}
|
||||
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
updateCh.Enqueue(imap.NewMailboxUpdated(
|
||||
update := imap.NewMailboxUpdated(
|
||||
imap.MailboxID(event.ID),
|
||||
getMailboxName(event.Label),
|
||||
))
|
||||
)
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelUpdated{
|
||||
@ -362,12 +383,14 @@ func (user *User) handleUpdateLabelEvent(_ context.Context, event proton.LabelEv
|
||||
Name: event.Label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
return updates, nil
|
||||
}, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
func (user *User) handleDeleteLabelEvent(_ context.Context, event proton.LabelEvent) error { //nolint:unparam
|
||||
return safe.LockRet(func() error {
|
||||
func (user *User) handleDeleteLabelEvent(ctx context.Context, event proton.LabelEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.LockRetErr(func() ([]imap.Update, error) {
|
||||
var updates []imap.Update
|
||||
|
||||
user.log.WithField("labelID", event.ID).Info("Handling label deleted event")
|
||||
|
||||
label, ok := user.apiLabels[event.ID]
|
||||
@ -376,7 +399,9 @@ func (user *User) handleDeleteLabelEvent(_ context.Context, event proton.LabelEv
|
||||
}
|
||||
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
updateCh.Enqueue(imap.NewMailboxDeleted(imap.MailboxID(event.ID)))
|
||||
update := imap.NewMailboxDeleted(imap.MailboxID(event.ID))
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelDeleted{
|
||||
@ -385,7 +410,7 @@ func (user *User) handleDeleteLabelEvent(_ context.Context, event proton.LabelEv
|
||||
Name: label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
return updates, nil
|
||||
}, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
@ -396,26 +421,32 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto
|
||||
|
||||
switch event.Action {
|
||||
case proton.EventCreate:
|
||||
if err := user.handleCreateMessageEvent(
|
||||
updates, err := user.handleCreateMessageEvent(
|
||||
logging.WithLogrusField(ctx, "action", "create message"),
|
||||
event,
|
||||
); err != nil {
|
||||
event)
|
||||
if err != nil {
|
||||
if rerr := user.reporter.ReportMessageWithContext("Failed to apply create message event", reporter.Context{
|
||||
"error": err,
|
||||
}); rerr != nil {
|
||||
user.log.WithError(err).Error("Failed to report create message event error")
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to handle create message event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case proton.EventUpdate, proton.EventUpdateFlags:
|
||||
// Draft update means to completely remove old message and upload the new data again, but we should
|
||||
// only do this if the event is of type EventUpdate otherwise label switch operations will not work.
|
||||
if event.Message.IsDraft() && event.Action == proton.EventUpdate {
|
||||
if err := user.handleUpdateDraftEvent(
|
||||
updates, err := user.handleUpdateDraftEvent(
|
||||
logging.WithLogrusField(ctx, "action", "update draft"),
|
||||
event,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
if rerr := user.reporter.ReportMessageWithContext("Failed to apply update draft message event", reporter.Context{
|
||||
"error": err,
|
||||
}); rerr != nil {
|
||||
@ -424,6 +455,10 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto
|
||||
return fmt.Errorf("failed to handle update draft event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -431,10 +466,11 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto
|
||||
// whether the flags, labels or read only data (header+body) has been changed. This requires fixing proton
|
||||
// first so that it correctly reports those cases.
|
||||
// Issue regular update to handle mailboxes and flag changes.
|
||||
if err := user.handleUpdateMessageEvent(
|
||||
updates, err := user.handleUpdateMessageEvent(
|
||||
logging.WithLogrusField(ctx, "action", "update message"),
|
||||
event,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
if rerr := user.reporter.ReportMessageWithContext("Failed to apply update message event", reporter.Context{
|
||||
"error": err,
|
||||
}); rerr != nil {
|
||||
@ -443,11 +479,16 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto
|
||||
return fmt.Errorf("failed to handle update message event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case proton.EventDelete:
|
||||
if err := user.handleDeleteMessageEvent(
|
||||
updates, err := user.handleDeleteMessageEvent(
|
||||
logging.WithLogrusField(ctx, "action", "delete message"),
|
||||
event,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
if rerr := user.reporter.ReportMessageWithContext("Failed to apply delete message event", reporter.Context{
|
||||
"error": err,
|
||||
}); rerr != nil {
|
||||
@ -455,25 +496,30 @@ func (user *User) handleMessageEvents(ctx context.Context, messageEvents []proto
|
||||
}
|
||||
return fmt.Errorf("failed to handle delete message event: %w", err)
|
||||
}
|
||||
|
||||
if err := waitOnIMAPUpdates(ctx, updates); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (user *User) handleCreateMessageEvent(ctx context.Context, event proton.MessageEvent) error {
|
||||
func (user *User) handleCreateMessageEvent(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) {
|
||||
full, err := user.client.GetFullMessage(ctx, event.Message.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get full message: %w", err)
|
||||
return nil, fmt.Errorf("failed to get full message: %w", err)
|
||||
}
|
||||
|
||||
return safe.RLockRet(func() error {
|
||||
return safe.RLockRetErr(func() ([]imap.Update, error) {
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"messageID": event.ID,
|
||||
"subject": logging.Sensitive(event.Message.Subject),
|
||||
}).Info("Handling message created event")
|
||||
|
||||
return withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
||||
var update imap.Update
|
||||
if err := withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
||||
res := buildRFC822(user.apiLabels, full, addrKR)
|
||||
|
||||
if res.err != nil {
|
||||
@ -497,45 +543,56 @@ func (user *User) handleCreateMessageEvent(ctx context.Context, event proton.Mes
|
||||
user.log.WithError(err).Error("Failed to remove failed message ID from vault")
|
||||
}
|
||||
|
||||
user.updateCh[full.AddressID].Enqueue(imap.NewMessagesCreated(false, res.update))
|
||||
update = imap.NewMessagesCreated(false, res.update)
|
||||
user.updateCh[full.AddressID].Enqueue(update)
|
||||
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []imap.Update{update}, nil
|
||||
}, user.apiUserLock, user.apiAddrsLock, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
func (user *User) handleUpdateMessageEvent(ctx context.Context, event proton.MessageEvent) error { //nolint:unparam
|
||||
return safe.RLockRet(func() error {
|
||||
func (user *User) handleUpdateMessageEvent(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.RLockRetErr(func() ([]imap.Update, error) {
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"messageID": event.ID,
|
||||
"subject": logging.Sensitive(event.Message.Subject),
|
||||
}).Info("Handling message updated event")
|
||||
|
||||
user.updateCh[event.Message.AddressID].Enqueue(imap.NewMessageMailboxesUpdated(
|
||||
update := imap.NewMessageMailboxesUpdated(
|
||||
imap.MessageID(event.ID),
|
||||
mapTo[string, imap.MailboxID](wantLabels(user.apiLabels, event.Message.LabelIDs)),
|
||||
event.Message.Seen(),
|
||||
event.Message.Starred(),
|
||||
))
|
||||
)
|
||||
|
||||
return nil
|
||||
user.updateCh[event.Message.AddressID].Enqueue(update)
|
||||
|
||||
return []imap.Update{update}, nil
|
||||
}, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
func (user *User) handleDeleteMessageEvent(ctx context.Context, event proton.MessageEvent) error { //nolint:unparam
|
||||
return safe.RLockRet(func() error {
|
||||
func (user *User) handleDeleteMessageEvent(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.RLockRetErr(func() ([]imap.Update, error) {
|
||||
user.log.WithField("messageID", event.ID).Info("Handling message deleted event")
|
||||
|
||||
var updates []imap.Update
|
||||
|
||||
for _, updateCh := range xslices.Unique(maps.Values(user.updateCh)) {
|
||||
updateCh.Enqueue(imap.NewMessagesDeleted(imap.MessageID(event.ID)))
|
||||
update := imap.NewMessagesDeleted(imap.MessageID(event.ID))
|
||||
updateCh.Enqueue(update)
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
return nil
|
||||
return updates, nil
|
||||
}, user.updateChLock)
|
||||
}
|
||||
|
||||
func (user *User) handleUpdateDraftEvent(ctx context.Context, event proton.MessageEvent) error { //nolint:unparam
|
||||
return safe.RLockRet(func() error {
|
||||
func (user *User) handleUpdateDraftEvent(ctx context.Context, event proton.MessageEvent) ([]imap.Update, error) { //nolint:unparam
|
||||
return safe.RLockRetErr(func() ([]imap.Update, error) {
|
||||
user.log.WithFields(logrus.Fields{
|
||||
"messageID": event.ID,
|
||||
"subject": logging.Sensitive(event.Message.Subject),
|
||||
@ -543,10 +600,12 @@ func (user *User) handleUpdateDraftEvent(ctx context.Context, event proton.Messa
|
||||
|
||||
full, err := user.client.GetFullMessage(ctx, event.Message.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get full draft: %w", err)
|
||||
return nil, fmt.Errorf("failed to get full draft: %w", err)
|
||||
}
|
||||
|
||||
return withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
||||
var update imap.Update
|
||||
|
||||
if err := withAddrKR(user.apiUser, user.apiAddrs[event.Message.AddressID], user.vault.KeyPass(), func(_, addrKR *crypto.KeyRing) error {
|
||||
res := buildRFC822(user.apiLabels, full, addrKR)
|
||||
|
||||
if res.err != nil {
|
||||
@ -570,15 +629,21 @@ func (user *User) handleUpdateDraftEvent(ctx context.Context, event proton.Messa
|
||||
user.log.WithError(err).Error("Failed to remove failed message ID from vault")
|
||||
}
|
||||
|
||||
user.updateCh[full.AddressID].Enqueue(imap.NewMessageUpdated(
|
||||
update = imap.NewMessageUpdated(
|
||||
res.update.Message,
|
||||
res.update.Literal,
|
||||
res.update.MailboxIDs,
|
||||
res.update.ParsedMessage,
|
||||
))
|
||||
)
|
||||
|
||||
user.updateCh[full.AddressID].Enqueue(update)
|
||||
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []imap.Update{update}, nil
|
||||
}, user.apiUserLock, user.apiAddrsLock, user.apiLabelsLock, user.updateChLock)
|
||||
}
|
||||
|
||||
@ -602,3 +667,14 @@ func getMailboxName(label proton.Label) []string {
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func waitOnIMAPUpdates(ctx context.Context, updates []imap.Update) error {
|
||||
for _, update := range updates {
|
||||
err, ok := update.WaitContext(ctx)
|
||||
if ok && err != nil {
|
||||
return fmt.Errorf("failed to apply gluon update %v :%w", update.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user