hydroxide-push/imap/database/user.go

240 lines
4.9 KiB
Go
Raw Normal View History

2018-01-08 00:38:13 +02:00
package database
import (
"encoding/json"
"errors"
"github.com/boltdb/bolt"
2024-03-26 21:26:28 +02:00
"github.com/0ranki/hydroxide-push/config"
"github.com/0ranki/hydroxide-push/protonmail"
2018-01-08 00:38:13 +02:00
)
var ErrNotFound = errors.New("message not found in local database")
var (
mailboxesBucket = []byte("mailboxes")
2018-10-21 13:15:20 +03:00
messagesBucket = []byte("messages")
2018-01-08 00:38:13 +02:00
)
2018-01-11 15:40:05 +02:00
func userMessage(b *bolt.Bucket, apiID string) (*protonmail.Message, error) {
k := []byte(apiID)
v := b.Get(k)
if v == nil {
return nil, ErrNotFound
}
msg := &protonmail.Message{}
err := json.Unmarshal(v, msg)
return msg, err
}
func userCreateMessage(b *bolt.Bucket, msg *protonmail.Message) error {
k := []byte(msg.ID)
v, err := json.Marshal(msg)
if err != nil {
return err
}
return b.Put(k, v)
}
func userSync(tx *bolt.Tx, messages []*protonmail.Message) error {
b, err := tx.CreateBucketIfNotExists(messagesBucket)
if err != nil {
return err
}
for _, msg := range messages {
if err := userCreateMessage(b, msg); err != nil {
return err
}
}
return nil
}
2018-01-08 00:38:13 +02:00
type User struct {
db *bolt.DB
}
2018-01-11 15:40:05 +02:00
func (u *User) Mailbox(labelID string) (*Mailbox, error) {
2018-01-08 00:38:13 +02:00
err := u.db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists(mailboxesBucket)
if err != nil {
return err
}
2018-01-11 15:40:05 +02:00
_, err = b.CreateBucketIfNotExists([]byte(labelID))
2018-01-08 00:38:13 +02:00
return err
})
if err != nil {
return nil, err
}
2018-01-11 15:40:05 +02:00
return &Mailbox{labelID, u}, nil
}
func (u *User) Message(apiID string) (*protonmail.Message, error) {
var msg *protonmail.Message
2018-10-21 13:15:20 +03:00
err := u.db.View(func(tx *bolt.Tx) error {
2018-01-11 15:40:05 +02:00
b := tx.Bucket(messagesBucket)
if b == nil {
return ErrNotFound
}
var err error
msg, err = userMessage(b, apiID)
return err
})
return msg, err
2018-01-08 00:38:13 +02:00
}
2018-01-11 15:40:05 +02:00
func (u *User) ResetMessages() error {
2018-01-08 00:38:13 +02:00
return u.db.Update(func(tx *bolt.Tx) error {
2018-01-11 15:40:05 +02:00
return tx.DeleteBucket(messagesBucket)
})
}
2018-01-12 16:16:26 +02:00
func (u *User) CreateMessage(msg *protonmail.Message) (seqNums map[string]uint32, err error) {
seqNums = make(map[string]uint32)
err = u.db.Update(func(tx *bolt.Tx) error {
2018-01-11 15:40:05 +02:00
messages, err := tx.CreateBucketIfNotExists(messagesBucket)
2018-01-08 00:38:13 +02:00
if err != nil {
return err
}
2018-01-11 15:40:05 +02:00
if err := userCreateMessage(messages, msg); err != nil {
return err
}
mailboxes, err := tx.CreateBucketIfNotExists(mailboxesBucket)
if err != nil {
return err
}
for _, labelID := range msg.LabelIDs {
mbox, err := mailboxes.CreateBucketIfNotExists([]byte(labelID))
2018-01-10 00:42:12 +02:00
if err != nil {
2018-01-08 00:38:13 +02:00
return err
}
2018-01-11 15:40:05 +02:00
2018-01-12 16:16:26 +02:00
seqNum, err := mailboxCreateMessage(mbox, msg.ID)
if err != nil {
2018-01-08 00:38:13 +02:00
return err
}
2018-01-12 16:16:26 +02:00
seqNums[labelID] = seqNum
2018-01-08 00:38:13 +02:00
}
return nil
})
2018-01-12 16:16:26 +02:00
return
2018-01-08 00:38:13 +02:00
}
2018-01-12 16:16:26 +02:00
func (u *User) UpdateMessage(apiID string, update *protonmail.EventMessageUpdate) (createdSeqNums map[string]uint32, deletedSeqNums map[string]uint32, err error) {
createdSeqNums = make(map[string]uint32)
deletedSeqNums = make(map[string]uint32)
err = u.db.Update(func(tx *bolt.Tx) error {
2018-01-11 15:40:05 +02:00
messages := tx.Bucket(messagesBucket)
if messages == nil {
return errors.New("cannot update message in local DB: messages bucket doesn't exist")
2018-01-08 00:38:13 +02:00
}
2018-01-12 14:20:17 +02:00
msg, err := userMessage(messages, apiID)
2018-01-11 15:40:05 +02:00
if err != nil {
return err
}
addedLabels, removedLabels := update.DiffLabelIDs(msg.LabelIDs)
mailboxes, err := tx.CreateBucketIfNotExists(mailboxesBucket)
if err != nil {
return err
}
for _, labelID := range addedLabels {
mbox, err := mailboxes.CreateBucketIfNotExists([]byte(labelID))
if err != nil {
return err
}
2018-01-12 16:16:26 +02:00
seqNum, err := mailboxCreateMessage(mbox, apiID)
if err != nil {
2018-01-11 15:40:05 +02:00
return err
}
2018-01-12 16:16:26 +02:00
createdSeqNums[labelID] = seqNum
2018-01-11 15:40:05 +02:00
}
for _, labelID := range removedLabels {
mbox := mailboxes.Bucket([]byte(labelID))
if mbox == nil {
continue
}
2018-01-12 16:16:26 +02:00
seqNum, err := mailboxDeleteMessage(mbox, apiID)
if err != nil {
2018-01-11 15:40:05 +02:00
return err
}
2018-01-12 16:16:26 +02:00
deletedSeqNums[labelID] = seqNum
2018-01-08 00:38:13 +02:00
}
2018-01-11 15:40:05 +02:00
update.Patch(msg)
return userCreateMessage(messages, msg)
2018-01-08 00:38:13 +02:00
})
2018-01-12 16:16:26 +02:00
return
2018-01-08 00:38:13 +02:00
}
2018-01-12 16:16:26 +02:00
func (u *User) DeleteMessage(apiID string) (seqNums map[string]uint32, err error) {
seqNums = make(map[string]uint32)
err = u.db.Update(func(tx *bolt.Tx) error {
2018-10-21 13:15:20 +03:00
messages := tx.Bucket(messagesBucket)
2018-01-11 15:40:05 +02:00
if messages == nil {
return nil
}
msg, err := userMessage(messages, apiID)
if err == ErrNotFound {
return nil
} else if err != nil {
return err
}
if err := messages.Delete([]byte(apiID)); err != nil {
return err
}
mailboxes := tx.Bucket(mailboxesBucket)
if mailboxes == nil {
return nil
}
for _, labelID := range msg.LabelIDs {
mbox := mailboxes.Bucket([]byte(labelID))
if mbox == nil {
continue
}
2018-01-12 16:16:26 +02:00
seqNum, err := mailboxDeleteMessage(mbox, msg.ID)
if err != nil {
2018-01-11 15:40:05 +02:00
return err
}
2018-01-12 16:16:26 +02:00
seqNums[labelID] = seqNum
2018-01-11 15:40:05 +02:00
}
return nil
2018-01-11 13:39:32 +02:00
})
2018-01-12 16:16:26 +02:00
return
2018-01-11 13:39:32 +02:00
}
func (u *User) Close() error {
return u.db.Close()
}
func Open(filename string) (*User, error) {
p, err := config.Path(filename)
if err != nil {
return nil, err
}
db, err := bolt.Open(p, 0700, nil)
2018-01-08 00:38:13 +02:00
if err != nil {
return nil, err
}
return &User{db}, nil
}