2018-01-08 00:38:13 +02:00
|
|
|
package database
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
|
|
|
|
"github.com/emersion/hydroxide/protonmail"
|
|
|
|
)
|
|
|
|
|
|
|
|
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-01-08 00:38:13 +02:00
|
|
|
u *User
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|