imap: add User.receiveEvents

This commit is contained in:
emersion 2018-01-11 12:39:32 +01:00
parent 60b4122c1d
commit 38af52e857
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
6 changed files with 139 additions and 13 deletions

View File

@ -335,7 +335,7 @@ func (ab *addressBook) CreateAddressObject(card vcard.Card) (carddav.AddressObje
func (ab *addressBook) receiveEvents(events <-chan *protonmail.Event) { func (ab *addressBook) receiveEvents(events <-chan *protonmail.Event) {
for event := range events { for event := range events {
ab.locker.Lock() ab.locker.Lock()
if event.Refresh == 1 { if event.Refresh&protonmail.EventRefreshContacts != 0 {
ab.cache = make(map[string]*addressObject) ab.cache = make(map[string]*addressObject)
ab.total = -1 ab.total = -1
} else if len(event.Contacts) > 0 { } else if len(event.Contacts) > 0 {

View File

@ -174,3 +174,18 @@ func (mbox *Mailbox) ForEach(f func(seqNum, uid uint32, apiID string) error) err
return nil return nil
}) })
} }
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")
}
k := []byte(mbox.name)
if err := b.DeleteBucket(k); err != nil {
return err
}
_, err := b.CreateBucket(k)
return err
})
}

View File

@ -78,6 +78,16 @@ func (u *User) Message(apiID string) (*protonmail.Message, error) {
return msg, err return msg, err
} }
func (u *User) ResetMessages() error {
return u.db.Update(func(tx *bolt.Tx) error {
return tx.DeleteBucket(messagesBucket)
})
}
func (u *User) Close() error {
return u.db.Close()
}
func Open(path string) (*User, error) { func Open(path string) (*User, error) {
db, err := bolt.Open(path, 0700, nil) db, err := bolt.Open(path, 0700, nil)
if err != nil { if err != nil {

View File

@ -130,6 +130,15 @@ func (mbox *mailbox) init() error {
return nil return nil
} }
func (mbox *mailbox) reset() error {
mbox.initializedLock.Lock()
defer mbox.initializedLock.Unlock()
mbox.initialized = false
return mbox.db.Reset()
}
func (mbox *mailbox) fetchMessage(isUid bool, id uint32, items []imap.FetchItem) (*imap.Message, error) { func (mbox *mailbox) fetchMessage(isUid bool, id uint32, items []imap.FetchItem) (*imap.Message, error) {
var apiID string var apiID string
var err error var err error

View File

@ -1,6 +1,9 @@
package imap package imap
import ( import (
"log"
"sync"
"golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
imapbackend "github.com/emersion/go-imap/backend" imapbackend "github.com/emersion/go-imap/backend"
@ -31,6 +34,8 @@ type user struct {
privateKeys openpgp.EntityList privateKeys openpgp.EntityList
db *database.User db *database.User
locker sync.Mutex
mailboxes map[string]*mailbox mailboxes map[string]*mailbox
} }
@ -39,7 +44,6 @@ func newUser(c *protonmail.Client, u *protonmail.User, privateKeys openpgp.Entit
c: c, c: c,
u: u, u: u,
privateKeys: privateKeys, privateKeys: privateKeys,
mailboxes: make(map[string]*mailbox),
} }
db, err := database.Open(u.Name+".db") db, err := database.Open(u.Name+".db")
@ -48,34 +52,49 @@ func newUser(c *protonmail.Client, u *protonmail.User, privateKeys openpgp.Entit
} }
uu.db = db uu.db = db
for _, data := range systemMailboxes { if err := uu.initMailboxes(); err != nil {
mboxDB, err := db.Mailbox(data.label)
if err != nil {
return nil, err return nil, err
} }
uu.mailboxes[data.label] = &mailbox{ // TODO: go uu.receiveEvents(events)
return uu, nil
}
func (u *user) initMailboxes() error {
u.locker.Lock()
defer u.locker.Unlock()
u.mailboxes = make(map[string]*mailbox)
for _, data := range systemMailboxes {
mboxDB, err := u.db.Mailbox(data.label)
if err != nil {
return err
}
u.mailboxes[data.label] = &mailbox{
name: data.name, name: data.name,
label: data.label, label: data.label,
flags: data.flags, flags: data.flags,
u: uu, u: u,
db: mboxDB, db: mboxDB,
} }
} }
counts, err := c.CountMessages("") counts, err := u.c.CountMessages("")
if err != nil { if err != nil {
return nil, err return err
} }
for _, count := range counts { for _, count := range counts {
if mbox, ok := uu.mailboxes[count.LabelID]; ok { if mbox, ok := u.mailboxes[count.LabelID]; ok {
mbox.total = count.Total mbox.total = count.Total
mbox.unread = count.Unread mbox.unread = count.Unread
} }
} }
return uu, nil return nil
} }
func (u *user) Username() string { func (u *user) Username() string {
@ -83,6 +102,9 @@ func (u *user) Username() string {
} }
func (u *user) ListMailboxes(subscribed bool) ([]imapbackend.Mailbox, error) { func (u *user) ListMailboxes(subscribed bool) ([]imapbackend.Mailbox, error) {
u.locker.Lock()
defer u.locker.Unlock()
list := make([]imapbackend.Mailbox, 0, len(u.mailboxes)) list := make([]imapbackend.Mailbox, 0, len(u.mailboxes))
for _, mbox := range u.mailboxes { for _, mbox := range u.mailboxes {
list = append(list, mbox) list = append(list, mbox)
@ -91,6 +113,9 @@ func (u *user) ListMailboxes(subscribed bool) ([]imapbackend.Mailbox, error) {
} }
func (u *user) GetMailbox(name string) (imapbackend.Mailbox, error) { func (u *user) GetMailbox(name string) (imapbackend.Mailbox, error) {
u.locker.Lock()
defer u.locker.Unlock()
for _, mbox := range u.mailboxes { for _, mbox := range u.mailboxes {
if mbox.name == name { if mbox.name == name {
return mbox, nil return mbox, nil
@ -112,8 +137,59 @@ func (u *user) RenameMailbox(existingName, newName string) error {
} }
func (u *user) Logout() error { func (u *user) Logout() error {
if err := u.db.Close(); err != nil {
return err
}
u.c = nil u.c = nil
u.u = nil u.u = nil
u.privateKeys = nil u.privateKeys = nil
return nil return nil
} }
func (u *user) receiveEvents(events <-chan *protonmail.Event) {
for event := range events {
if event.Refresh&protonmail.EventRefreshMail != 0 {
log.Println("Reinitializing the whole IMAP database")
u.locker.Lock()
for _, mbox := range u.mailboxes {
if err := mbox.reset(); err != nil {
log.Printf("cannot reset mailbox %s: %v", mbox.name, err)
}
}
u.locker.Unlock()
if err := u.db.ResetMessages(); err != nil {
log.Printf("cannot reset user: %v", err)
}
if err := u.initMailboxes(); err != nil {
log.Printf("cannot reinitialize mailboxes: %v", err)
}
} else {
u.locker.Lock()
for _, count := range event.MessageCounts {
if mbox, ok := u.mailboxes[count.LabelID]; ok {
mbox.total = count.Total
mbox.unread = count.Unread
// TODO: send update
}
}
u.locker.Unlock()
for _, eventMessage := range event.Messages {
switch eventMessage.Action {
case protonmail.EventCreate:
// TODO
case protonmail.EventUpdate:
// TODO
case protonmail.EventUpdateFlags:
// TODO
case protonmail.EventDelete:
// TODO
}
}
}
}
}

View File

@ -4,10 +4,17 @@ import (
"net/http" "net/http"
) )
type EventRefresh int
const (
EventRefreshMail EventRefresh = 1 << iota
EventRefreshContacts
)
type Event struct { type Event struct {
ID string `json:"EventID"` ID string `json:"EventID"`
Refresh int Refresh EventRefresh
//Messages Messages []*EventMessage
Contacts []*EventContact Contacts []*EventContact
//ContactEmails //ContactEmails
//Labels //Labels
@ -15,7 +22,7 @@ type Event struct {
//Members //Members
//Domains //Domains
//Organization //Organization
//MessageCounts MessageCounts []*MessageCount
//ConversationCounts //ConversationCounts
//UsedSpace //UsedSpace
Notices []string Notices []string
@ -27,8 +34,17 @@ const (
EventDelete EventAction = iota EventDelete EventAction = iota
EventCreate EventCreate
EventUpdate EventUpdate
// For messages
EventUpdateFlags
) )
type EventMessage struct {
ID string
Action EventAction
Message *Message
}
type EventContact struct { type EventContact struct {
ID string ID string
Action EventAction Action EventAction