Other(refactor): Use normal value + mutex for user.apiLabels
This commit is contained in:
parent
0bc99dbd4f
commit
cab5ee6752
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2022 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 events
|
||||
|
||||
type UserAddressCreated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type UserAddressUpdated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type UserAddressDeleted struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2022 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 events
|
||||
|
||||
type UserLabelCreated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
LabelID string
|
||||
Name string
|
||||
}
|
||||
|
||||
type UserLabelUpdated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
LabelID string
|
||||
Name string
|
||||
}
|
||||
|
||||
type UserLabelDeleted struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
LabelID string
|
||||
Name string
|
||||
}
|
|
@ -59,30 +59,6 @@ type UserChanged struct {
|
|||
UserID string
|
||||
}
|
||||
|
||||
type UserAddressCreated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type UserAddressUpdated struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type UserAddressDeleted struct {
|
||||
eventBase
|
||||
|
||||
UserID string
|
||||
AddressID string
|
||||
Email string
|
||||
}
|
||||
|
||||
type AddressModeChanged struct {
|
||||
eventBase
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
// Copyright (c) 2022 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 safe
|
||||
|
||||
type Mutex interface {
|
||||
|
|
|
@ -119,12 +119,6 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.Ad
|
|||
user.updateCh.Set(event.Address.ID, queue.NewQueuedChannel[imap.Update](0, 0))
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserAddressCreated{
|
||||
UserID: user.ID(),
|
||||
AddressID: event.Address.ID,
|
||||
Email: event.Address.Email,
|
||||
})
|
||||
|
||||
if user.vault.AddressMode() == vault.SplitMode {
|
||||
if ok, err := user.updateCh.GetErr(event.Address.ID, func(updateCh *queue.QueuedChannel[imap.Update]) error {
|
||||
return syncLabels(ctx, user.client, updateCh)
|
||||
|
@ -135,14 +129,20 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.Ad
|
|||
}
|
||||
}
|
||||
|
||||
user.eventCh.Enqueue(events.UserAddressCreated{
|
||||
UserID: user.ID(),
|
||||
AddressID: event.Address.ID,
|
||||
Email: event.Address.Email,
|
||||
})
|
||||
|
||||
return nil
|
||||
}, &user.apiAddrsLock)
|
||||
}
|
||||
|
||||
func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.AddressEvent) error { //nolint:unparam
|
||||
return safe.LockRet(func() error {
|
||||
if _, ok := user.apiAddrs[event.Address.ID]; ok {
|
||||
return fmt.Errorf("address %q already exists", event.ID)
|
||||
if _, ok := user.apiAddrs[event.Address.ID]; !ok {
|
||||
return fmt.Errorf("address %q does not exist", event.Address.ID)
|
||||
}
|
||||
|
||||
user.apiAddrs[event.Address.ID] = event.Address
|
||||
|
@ -207,33 +207,70 @@ func (user *User) handleLabelEvents(ctx context.Context, labelEvents []liteapi.L
|
|||
}
|
||||
|
||||
func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||
user.apiLabels.Set(event.Label.ID, event.Label)
|
||||
return safe.LockRet(func() error {
|
||||
if _, ok := user.apiLabels[event.Label.ID]; ok {
|
||||
return fmt.Errorf("label %q already exists", event.ID)
|
||||
}
|
||||
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(newMailboxCreatedUpdate(imap.MailboxID(event.ID), getMailboxName(event.Label)))
|
||||
})
|
||||
user.apiLabels[event.Label.ID] = event.Label
|
||||
|
||||
return nil
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(newMailboxCreatedUpdate(imap.MailboxID(event.ID), getMailboxName(event.Label)))
|
||||
})
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelCreated{
|
||||
UserID: user.ID(),
|
||||
LabelID: event.Label.ID,
|
||||
Name: event.Label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
}, &user.apiLabelsLock)
|
||||
}
|
||||
|
||||
func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||
user.apiLabels.Set(event.Label.ID, event.Label)
|
||||
return safe.LockRet(func() error {
|
||||
if _, ok := user.apiLabels[event.Label.ID]; !ok {
|
||||
return fmt.Errorf("label %q does not exist", event.ID)
|
||||
}
|
||||
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(imap.NewMailboxUpdated(imap.MailboxID(event.ID), getMailboxName(event.Label)))
|
||||
})
|
||||
user.apiLabels[event.Label.ID] = event.Label
|
||||
|
||||
return nil
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(imap.NewMailboxUpdated(imap.MailboxID(event.ID), getMailboxName(event.Label)))
|
||||
})
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelUpdated{
|
||||
UserID: user.ID(),
|
||||
LabelID: event.Label.ID,
|
||||
Name: event.Label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
}, &user.apiLabelsLock)
|
||||
}
|
||||
|
||||
func (user *User) handleDeleteLabelEvent(_ context.Context, event liteapi.LabelEvent) error { //nolint:unparam
|
||||
user.apiLabels.Delete(event.Label.ID)
|
||||
return safe.LockRet(func() error {
|
||||
label, ok := user.apiLabels[event.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf("label %q does not exist", event.ID)
|
||||
}
|
||||
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(imap.NewMailboxDeleted(imap.MailboxID(event.ID)))
|
||||
})
|
||||
delete(user.apiLabels, event.ID)
|
||||
|
||||
return nil
|
||||
user.updateCh.IterValues(func(updateCh *queue.QueuedChannel[imap.Update]) {
|
||||
updateCh.Enqueue(imap.NewMailboxDeleted(imap.MailboxID(event.ID)))
|
||||
})
|
||||
|
||||
user.eventCh.Enqueue(events.UserLabelDeleted{
|
||||
UserID: user.ID(),
|
||||
LabelID: event.ID,
|
||||
Name: label.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
}, &user.apiLabelsLock)
|
||||
}
|
||||
|
||||
// handleMessageEvents handles the given message events.
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
|
||||
"github.com/bradenaw/juniper/stream"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gitlab.protontech.ch/go/liteapi"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
@ -83,14 +82,14 @@ func (conn *imapConnector) Authorize(username string, password []byte) bool {
|
|||
|
||||
// GetMailbox returns information about the mailbox with the given ID.
|
||||
func (conn *imapConnector) GetMailbox(ctx context.Context, mailboxID imap.MailboxID) (imap.Mailbox, error) {
|
||||
mailbox, ok := safe.MapGetRet(conn.apiLabels, string(mailboxID), func(label liteapi.Label) imap.Mailbox {
|
||||
return toIMAPMailbox(label, conn.flags, conn.permFlags, conn.attrs)
|
||||
})
|
||||
if !ok {
|
||||
return imap.Mailbox{}, fmt.Errorf("no such mailbox: %s", mailboxID)
|
||||
}
|
||||
return safe.RLockRetErr(func() (imap.Mailbox, error) {
|
||||
mailbox, ok := conn.apiLabels[string(mailboxID)]
|
||||
if !ok {
|
||||
return imap.Mailbox{}, fmt.Errorf("no such mailbox: %s", mailboxID)
|
||||
}
|
||||
|
||||
return mailbox, nil
|
||||
return toIMAPMailbox(mailbox, conn.flags, conn.permFlags, conn.attrs), nil
|
||||
}, &conn.apiLabelsLock)
|
||||
}
|
||||
|
||||
// CreateMailbox creates a label with the given name.
|
||||
|
@ -129,29 +128,37 @@ func (conn *imapConnector) createLabel(ctx context.Context, name []string) (imap
|
|||
}
|
||||
|
||||
func (conn *imapConnector) createFolder(ctx context.Context, name []string) (imap.Mailbox, error) {
|
||||
var parentID string
|
||||
return safe.RLockRetErr(func() (imap.Mailbox, error) {
|
||||
var parentID string
|
||||
|
||||
if len(name) > 1 {
|
||||
if ok := conn.apiLabels.GetFunc(func(label liteapi.Label) bool {
|
||||
return cmp.Equal(label.Path, name[:len(name)-1])
|
||||
}, func(label liteapi.Label) {
|
||||
parentID = label.ID
|
||||
}); !ok {
|
||||
return imap.Mailbox{}, fmt.Errorf("parent folder %q does not exist", name[:len(name)-1])
|
||||
if len(name) > 1 {
|
||||
for _, label := range conn.apiLabels {
|
||||
if !slices.Equal(label.Path, name[:len(name)-1]) {
|
||||
continue
|
||||
}
|
||||
|
||||
parentID = label.ID
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if parentID == "" {
|
||||
return imap.Mailbox{}, fmt.Errorf("parent folder %q does not exist", name[:len(name)-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label, err := conn.client.CreateLabel(ctx, liteapi.CreateLabelReq{
|
||||
Name: name[len(name)-1],
|
||||
Color: "#f66",
|
||||
Type: liteapi.LabelTypeFolder,
|
||||
ParentID: parentID,
|
||||
})
|
||||
if err != nil {
|
||||
return imap.Mailbox{}, err
|
||||
}
|
||||
label, err := conn.client.CreateLabel(ctx, liteapi.CreateLabelReq{
|
||||
Name: name[len(name)-1],
|
||||
Color: "#f66",
|
||||
Type: liteapi.LabelTypeFolder,
|
||||
ParentID: parentID,
|
||||
})
|
||||
if err != nil {
|
||||
return imap.Mailbox{}, err
|
||||
}
|
||||
|
||||
return toIMAPMailbox(label, conn.flags, conn.permFlags, conn.attrs), nil
|
||||
return toIMAPMailbox(label, conn.flags, conn.permFlags, conn.attrs), nil
|
||||
}, &conn.apiLabelsLock)
|
||||
}
|
||||
|
||||
// UpdateMailboxName sets the name of the label with the given ID.
|
||||
|
@ -193,32 +200,40 @@ func (conn *imapConnector) updateLabel(ctx context.Context, labelID imap.Mailbox
|
|||
}
|
||||
|
||||
func (conn *imapConnector) updateFolder(ctx context.Context, labelID imap.MailboxID, name []string) error {
|
||||
var parentID string
|
||||
return safe.RLockRet(func() error {
|
||||
var parentID string
|
||||
|
||||
if len(name) > 1 {
|
||||
if ok := conn.apiLabels.GetFunc(func(label liteapi.Label) bool {
|
||||
return cmp.Equal(label.Path, name[:len(name)-1])
|
||||
}, func(label liteapi.Label) {
|
||||
parentID = label.ID
|
||||
}); !ok {
|
||||
return fmt.Errorf("parent folder %q does not exist", name[:len(name)-1])
|
||||
if len(name) > 1 {
|
||||
for _, label := range conn.apiLabels {
|
||||
if !slices.Equal(label.Path, name[:len(name)-1]) {
|
||||
continue
|
||||
}
|
||||
|
||||
parentID = label.ID
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if parentID == "" {
|
||||
return fmt.Errorf("parent folder %q does not exist", name[:len(name)-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label, err := conn.client.GetLabel(ctx, string(labelID), liteapi.LabelTypeFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
label, err := conn.client.GetLabel(ctx, string(labelID), liteapi.LabelTypeFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := conn.client.UpdateLabel(ctx, string(labelID), liteapi.UpdateLabelReq{
|
||||
Name: name[len(name)-1],
|
||||
Color: label.Color,
|
||||
ParentID: parentID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := conn.client.UpdateLabel(ctx, string(labelID), liteapi.UpdateLabelReq{
|
||||
Name: name[len(name)-1],
|
||||
Color: label.Color,
|
||||
ParentID: parentID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}, &conn.apiLabelsLock)
|
||||
}
|
||||
|
||||
// DeleteMailbox deletes the label with the given ID.
|
||||
|
|
|
@ -59,9 +59,11 @@ type User struct {
|
|||
apiAddrs map[string]liteapi.Address
|
||||
apiAddrsLock sync.RWMutex
|
||||
|
||||
apiLabels *safe.Map[string, liteapi.Label]
|
||||
updateCh *safe.Map[string, *queue.QueuedChannel[imap.Update]]
|
||||
sendHash *sendRecorder
|
||||
apiLabels map[string]liteapi.Label
|
||||
apiLabelsLock sync.RWMutex
|
||||
|
||||
updateCh *safe.Map[string, *queue.QueuedChannel[imap.Update]]
|
||||
sendHash *sendRecorder
|
||||
|
||||
tasks *xsync.Group
|
||||
abortable async.Abortable
|
||||
|
@ -138,7 +140,7 @@ func New(
|
|||
|
||||
apiUser: apiUser,
|
||||
apiAddrs: groupBy(apiAddrs, func(addr liteapi.Address) string { return addr.ID }),
|
||||
apiLabels: safe.NewMapFrom(groupBy(apiLabels, func(label liteapi.Label) string { return label.ID }), nil),
|
||||
apiLabels: groupBy(apiLabels, func(label liteapi.Label) string { return label.ID }),
|
||||
updateCh: safe.NewMapFrom(updateCh, nil),
|
||||
sendHash: newSendRecorder(sendEntryExpiry),
|
||||
|
||||
|
|
Loading…
Reference in New Issue