hydroxide-push/imap/database/mailbox.go

198 lines
3.7 KiB
Go
Raw Permalink Normal View History

2018-01-08 00:38:13 +02:00
package database
import (
"bytes"
"encoding/binary"
"errors"
"github.com/boltdb/bolt"
2024-03-26 21:26:28 +02:00
"github.com/0ranki/hydroxide-push/protonmail"
2018-01-08 00:38:13 +02:00
)
func serializeUID(uid uint32) []byte {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uid)
return b
}
func unserializeUID(b []byte) uint32 {
return binary.BigEndian.Uint32(b)
}
2018-01-12 16:16:26 +02:00
func mailboxCreateMessage(b *bolt.Bucket, apiID string) (seqNum uint32, err error) {
2018-01-11 15:40:05 +02:00
want := []byte(apiID)
c := b.Cursor()
2018-01-12 16:16:26 +02:00
var n uint32 = 1
2018-01-11 15:40:05 +02:00
for k, v := c.First(); k != nil; k, v = c.Next() {
if bytes.Equal(v, want) {
2018-01-12 16:16:26 +02:00
return n, nil
2018-01-11 15:40:05 +02:00
}
2018-01-12 16:16:26 +02:00
n++
2018-01-11 15:40:05 +02:00
}
id, _ := b.NextSequence()
uid := uint32(id)
2018-01-12 16:16:26 +02:00
return n, b.Put(serializeUID(uid), want)
2018-01-11 15:40:05 +02:00
}
2018-01-12 16:16:26 +02:00
func mailboxDeleteMessage(b *bolt.Bucket, apiID string) (seqNum uint32, err error) {
2018-01-11 15:40:05 +02:00
want := []byte(apiID)
c := b.Cursor()
2018-01-12 16:16:26 +02:00
var n uint32 = 1
2018-01-11 15:40:05 +02:00
for k, v := c.First(); k != nil; k, v = c.Next() {
if bytes.Equal(v, want) {
2018-01-12 16:16:26 +02:00
return n, b.Delete(k)
2018-01-11 15:40:05 +02:00
}
2018-01-12 16:16:26 +02:00
n++
2018-01-11 15:40:05 +02:00
}
2018-01-12 16:16:26 +02:00
return 0, nil
2018-01-11 15:40:05 +02:00
}
2018-01-08 00:38:13 +02:00
type Mailbox struct {
2018-01-11 15:40:05 +02:00
labelID string
2018-10-21 13:15:20 +03:00
u *User
2018-01-08 00:38:13 +02:00
}
func (mbox *Mailbox) bucket(tx *bolt.Tx) (*bolt.Bucket, error) {
b := tx.Bucket(mailboxesBucket)
if b == nil {
return nil, errors.New("cannot find mailboxes bucket")
}
2018-01-11 15:40:05 +02:00
b = b.Bucket([]byte(mbox.labelID))
2018-01-08 00:38:13 +02:00
if b == nil {
return nil, errors.New("cannot find mailbox bucket")
}
return b, nil
}
func (mbox *Mailbox) Sync(messages []*protonmail.Message) error {
2018-01-11 15:40:05 +02:00
return mbox.u.db.Update(func(tx *bolt.Tx) error {
2018-01-08 00:38:13 +02:00
b, err := mbox.bucket(tx)
if err != nil {
return err
}
for _, msg := range messages {
2018-01-12 16:16:26 +02:00
if _, err := mailboxCreateMessage(b, msg.ID); err != nil {
2018-01-08 00:38:13 +02:00
return err
}
}
2018-01-11 15:40:05 +02:00
return userSync(tx, messages)
2018-01-08 00:38:13 +02:00
})
}
func (mbox *Mailbox) UidNext() (uint32, error) {
var uid uint32
err := mbox.u.db.View(func(tx *bolt.Tx) error {
b, err := mbox.bucket(tx)
if err != nil {
return err
}
uid = uint32(b.Sequence() + 1)
return nil
})
return uid, err
}
2018-01-12 16:16:26 +02:00
func (mbox *Mailbox) FromUid(uid uint32) (apiID string, err error) {
err = mbox.u.db.View(func(tx *bolt.Tx) error {
2018-01-08 00:38:13 +02:00
b, err := mbox.bucket(tx)
if err != nil {
return err
}
k := serializeUID(uid)
v := b.Get(k)
if v == nil {
return ErrNotFound
}
apiID = string(v)
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 (mbox *Mailbox) FromSeqNum(seqNum uint32) (apiID string, err error) {
err = mbox.u.db.View(func(tx *bolt.Tx) error {
2018-01-08 00:38:13 +02:00
b, err := mbox.bucket(tx)
if err != nil {
return err
}
c := b.Cursor()
var n uint32 = 1
for k, v := c.First(); k != nil; k, v = c.Next() {
if seqNum == n {
apiID = string(v)
return nil
}
n++
}
return ErrNotFound
})
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 (mbox *Mailbox) FromApiID(apiID string) (seqNum uint32, uid uint32, err error) {
err = mbox.u.db.View(func(tx *bolt.Tx) error {
2018-01-08 00:38:13 +02:00
b, err := mbox.bucket(tx)
if err != nil {
return err
}
want := []byte(apiID)
c := b.Cursor()
var n uint32 = 1
for k, v := c.First(); k != nil; k, v = c.Next() {
if bytes.Equal(v, want) {
seqNum = n
uid = unserializeUID(k)
return nil
}
n++
}
return ErrNotFound
})
2018-01-12 16:16:26 +02:00
return
2018-01-08 00:38:13 +02:00
}
2018-01-09 21:03:19 +02:00
func (mbox *Mailbox) ForEach(f func(seqNum, uid uint32, apiID string) error) error {
return mbox.u.db.View(func(tx *bolt.Tx) error {
b, err := mbox.bucket(tx)
if err != nil {
return err
}
c := b.Cursor()
var n uint32 = 1
for k, v := c.First(); k != nil; k, v = c.Next() {
if err := f(n, unserializeUID(k), string(v)); err != nil {
return err
}
n++
}
return nil
})
}
2018-01-11 13:39:32 +02:00
func (mbox *Mailbox) Reset() error {
return mbox.u.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(mailboxesBucket)
if b == nil {
return errors.New("cannot find mailboxes bucket")
}
2018-01-11 15:40:05 +02:00
k := []byte(mbox.labelID)
2018-01-11 13:39:32 +02:00
if err := b.DeleteBucket(k); err != nil {
return err
}
_, err := b.CreateBucket(k)
return err
})
}