2017-12-03 16:08:30 +02:00
|
|
|
package imap
|
|
|
|
|
|
|
|
import (
|
2018-01-11 13:39:32 +02:00
|
|
|
"log"
|
|
|
|
"sync"
|
|
|
|
|
2017-12-03 16:08:30 +02:00
|
|
|
"golang.org/x/crypto/openpgp"
|
2017-12-03 17:05:24 +02:00
|
|
|
"github.com/emersion/go-imap"
|
2017-12-03 16:08:30 +02:00
|
|
|
imapbackend "github.com/emersion/go-imap/backend"
|
2017-12-03 17:05:24 +02:00
|
|
|
"github.com/emersion/go-imap-specialuse"
|
2017-12-03 16:08:30 +02:00
|
|
|
|
2018-01-08 00:38:13 +02:00
|
|
|
"github.com/emersion/hydroxide/imap/database"
|
2017-12-03 16:08:30 +02:00
|
|
|
"github.com/emersion/hydroxide/protonmail"
|
|
|
|
)
|
|
|
|
|
2017-12-03 17:05:24 +02:00
|
|
|
var systemMailboxes = []struct{
|
|
|
|
name string
|
|
|
|
label string
|
|
|
|
flags []string
|
|
|
|
}{
|
|
|
|
{imap.InboxName, protonmail.LabelInbox, nil},
|
|
|
|
{"All Mail", protonmail.LabelAllMail, []string{specialuse.All}},
|
|
|
|
{"Archive", protonmail.LabelArchive, []string{specialuse.Archive}},
|
|
|
|
{"Drafts", protonmail.LabelDraft, []string{specialuse.Drafts}},
|
|
|
|
{"Starred", protonmail.LabelStarred, []string{specialuse.Flagged}},
|
|
|
|
{"Spam", protonmail.LabelSpam, []string{specialuse.Junk}},
|
|
|
|
{"Sent", protonmail.LabelSent, []string{specialuse.Sent}},
|
|
|
|
{"Trash", protonmail.LabelTrash, []string{specialuse.Trash}},
|
|
|
|
}
|
|
|
|
|
2017-12-03 16:08:30 +02:00
|
|
|
type user struct {
|
|
|
|
c *protonmail.Client
|
|
|
|
u *protonmail.User
|
|
|
|
privateKeys openpgp.EntityList
|
2017-12-03 17:05:24 +02:00
|
|
|
|
2018-01-08 00:38:13 +02:00
|
|
|
db *database.User
|
2018-01-11 13:39:32 +02:00
|
|
|
|
|
|
|
locker sync.Mutex
|
2017-12-03 17:05:24 +02:00
|
|
|
mailboxes map[string]*mailbox
|
|
|
|
}
|
|
|
|
|
|
|
|
func newUser(c *protonmail.Client, u *protonmail.User, privateKeys openpgp.EntityList) (*user, error) {
|
|
|
|
uu := &user{
|
|
|
|
c: c,
|
|
|
|
u: u,
|
|
|
|
privateKeys: privateKeys,
|
|
|
|
}
|
|
|
|
|
2018-01-08 00:38:13 +02:00
|
|
|
db, err := database.Open(u.Name+".db")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
uu.db = db
|
|
|
|
|
2018-01-11 13:39:32 +02:00
|
|
|
if err := uu.initMailboxes(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
2017-12-03 17:05:24 +02:00
|
|
|
for _, data := range systemMailboxes {
|
2018-01-11 13:39:32 +02:00
|
|
|
mboxDB, err := u.db.Mailbox(data.label)
|
2018-01-08 00:38:13 +02:00
|
|
|
if err != nil {
|
2018-01-11 13:39:32 +02:00
|
|
|
return err
|
2018-01-08 00:38:13 +02:00
|
|
|
}
|
|
|
|
|
2018-01-11 13:39:32 +02:00
|
|
|
u.mailboxes[data.label] = &mailbox{
|
2017-12-03 17:05:24 +02:00
|
|
|
name: data.name,
|
|
|
|
label: data.label,
|
|
|
|
flags: data.flags,
|
2018-01-11 13:39:32 +02:00
|
|
|
u: u,
|
2018-01-08 00:38:13 +02:00
|
|
|
db: mboxDB,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-11 13:39:32 +02:00
|
|
|
counts, err := u.c.CountMessages("")
|
2018-01-08 00:38:13 +02:00
|
|
|
if err != nil {
|
2018-01-11 13:39:32 +02:00
|
|
|
return err
|
2018-01-08 00:38:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, count := range counts {
|
2018-01-11 13:39:32 +02:00
|
|
|
if mbox, ok := u.mailboxes[count.LabelID]; ok {
|
2018-01-08 00:38:13 +02:00
|
|
|
mbox.total = count.Total
|
|
|
|
mbox.unread = count.Unread
|
2017-12-03 17:05:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-11 13:39:32 +02:00
|
|
|
return nil
|
2017-12-03 16:08:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) Username() string {
|
2017-12-03 17:05:24 +02:00
|
|
|
return u.u.Name
|
2017-12-03 16:08:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) ListMailboxes(subscribed bool) ([]imapbackend.Mailbox, error) {
|
2018-01-11 13:39:32 +02:00
|
|
|
u.locker.Lock()
|
|
|
|
defer u.locker.Unlock()
|
|
|
|
|
2017-12-03 17:05:24 +02:00
|
|
|
list := make([]imapbackend.Mailbox, 0, len(u.mailboxes))
|
|
|
|
for _, mbox := range u.mailboxes {
|
|
|
|
list = append(list, mbox)
|
|
|
|
}
|
|
|
|
return list, nil
|
2017-12-03 16:08:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) GetMailbox(name string) (imapbackend.Mailbox, error) {
|
2018-01-11 13:39:32 +02:00
|
|
|
u.locker.Lock()
|
|
|
|
defer u.locker.Unlock()
|
|
|
|
|
2018-01-08 00:38:13 +02:00
|
|
|
for _, mbox := range u.mailboxes {
|
|
|
|
if mbox.name == name {
|
|
|
|
return mbox, nil
|
|
|
|
}
|
2017-12-03 17:05:24 +02:00
|
|
|
}
|
2018-01-08 00:38:13 +02:00
|
|
|
return nil, imapbackend.ErrNoSuchMailbox
|
2017-12-03 16:08:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) CreateMailbox(name string) error {
|
|
|
|
return errNotYetImplemented // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) DeleteMailbox(name string) error {
|
|
|
|
return errNotYetImplemented // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) RenameMailbox(existingName, newName string) error {
|
|
|
|
return errNotYetImplemented // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *user) Logout() error {
|
2018-01-11 13:39:32 +02:00
|
|
|
if err := u.db.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-03 16:08:30 +02:00
|
|
|
u.c = nil
|
|
|
|
u.u = nil
|
|
|
|
u.privateKeys = nil
|
|
|
|
return nil
|
|
|
|
}
|
2018-01-11 13:39:32 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|