From 7020ee36230173671cfb0d03da955b4009cfafb8 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 3 Dec 2017 16:05:24 +0100 Subject: [PATCH] imap: implement basic User.ListMailboxes and User.GetMailbox --- cmd/hydroxide/hydroxide.go | 2 ++ imap/imap.go | 7 +---- imap/mailbox.go | 32 +++++++++++++++++++++-- imap/user.go | 53 +++++++++++++++++++++++++++++++++++--- protonmail/labels.go | 14 ++++++++++ 5 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 protonmail/labels.go diff --git a/cmd/hydroxide/hydroxide.go b/cmd/hydroxide/hydroxide.go index ad824a6..ba9f1b0 100644 --- a/cmd/hydroxide/hydroxide.go +++ b/cmd/hydroxide/hydroxide.go @@ -11,6 +11,7 @@ import ( "time" imapserver "github.com/emersion/go-imap/server" + imapspacialuse "github.com/emersion/go-imap-specialuse" "github.com/emersion/go-smtp" "github.com/howeyc/gopass" @@ -162,6 +163,7 @@ func main() { s := imapserver.New(be) s.Addr = "127.0.0.1:" + port s.AllowInsecureAuth = true // TODO: remove this + s.Enable(imapspacialuse.NewExtension()) log.Println("Starting IMAP server at", s.Addr) log.Fatal(s.ListenAndServe()) diff --git a/imap/imap.go b/imap/imap.go index b0b919b..563291d 100644 --- a/imap/imap.go +++ b/imap/imap.go @@ -27,12 +27,7 @@ func (be *backend) Login(username, password string) (imapbackend.User, error) { // TODO: decrypt private keys in u.Addresses - return &user{ - username: username, - c: c, - u: u, - privateKeys: privateKeys, - }, nil + return newUser(c, u, privateKeys) } func New(sessions *auth.Manager) imapbackend.Backend { diff --git a/imap/mailbox.go b/imap/mailbox.go index 873675c..e3ee2f9 100644 --- a/imap/mailbox.go +++ b/imap/mailbox.go @@ -6,8 +6,12 @@ import ( "github.com/emersion/go-imap" ) +const delimiter = "/" + type mailbox struct { name string + label string + flags []string } func (mbox *mailbox) Name() string { @@ -15,11 +19,35 @@ func (mbox *mailbox) Name() string { } func (mbox *mailbox) Info() (*imap.MailboxInfo, error) { - return nil, errNotYetImplemented // TODO + return &imap.MailboxInfo{ + Attributes: append(mbox.flags, imap.NoInferiorsAttr), + Delimiter: delimiter, + Name: mbox.name, + }, nil } func (mbox *mailbox) Status(items []imap.StatusItem) (*imap.MailboxStatus, error) { - return nil, errNotYetImplemented // TODO + status := imap.NewMailboxStatus(mbox.name, items) + status.Flags = mbox.flags + status.PermanentFlags = []string{imap.SeenFlag, imap.AnsweredFlag, imap.FlaggedFlag, imap.DeletedFlag, imap.DraftFlag} + status.UnseenSeqNum = 0 // TODO + + for _, name := range items { + switch name { + case imap.StatusMessages: + status.Messages = 0 // TODO + case imap.StatusUidNext: + status.UidNext = 1 // TODO + case imap.StatusUidValidity: + status.UidValidity = 1 + case imap.StatusRecent: + status.Recent = 0 // TODO + case imap.StatusUnseen: + status.Unseen = 0 // TODO + } + } + + return status, nil } func (mbox *mailbox) SetSubscribed(subscribed bool) error { diff --git a/imap/user.go b/imap/user.go index d73c448..5199565 100644 --- a/imap/user.go +++ b/imap/user.go @@ -2,28 +2,73 @@ package imap import ( "golang.org/x/crypto/openpgp" + "github.com/emersion/go-imap" imapbackend "github.com/emersion/go-imap/backend" + "github.com/emersion/go-imap-specialuse" "github.com/emersion/hydroxide/protonmail" ) +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}}, +} + type user struct { - username string c *protonmail.Client u *protonmail.User privateKeys openpgp.EntityList + + 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, + mailboxes: make(map[string]*mailbox), + } + + for _, data := range systemMailboxes { + uu.mailboxes[data.name] = &mailbox{ + name: data.name, + label: data.label, + flags: data.flags, + } + } + + return uu, nil } func (u *user) Username() string { - return u.username + return u.u.Name } func (u *user) ListMailboxes(subscribed bool) ([]imapbackend.Mailbox, error) { - return nil, errNotYetImplemented // TODO + list := make([]imapbackend.Mailbox, 0, len(u.mailboxes)) + for _, mbox := range u.mailboxes { + list = append(list, mbox) + } + return list, nil } func (u *user) GetMailbox(name string) (imapbackend.Mailbox, error) { - return &mailbox{}, errNotYetImplemented // TODO + if mbox, ok := u.mailboxes[name]; ok { + return mbox, nil + } else { + return nil, imapbackend.ErrNoSuchMailbox + } } func (u *user) CreateMailbox(name string) error { diff --git a/protonmail/labels.go b/protonmail/labels.go new file mode 100644 index 0000000..b019981 --- /dev/null +++ b/protonmail/labels.go @@ -0,0 +1,14 @@ +package protonmail + +const ( + LabelInbox = "0" + LabelAllDraft = "1" + LabelAllSent = "2" + LabelTrash = "3" + LabelSpam = "4" + LabelAllMail = "5" + LabelArchive = "6" + LabelSent = "7" + LabelDraft = "8" + LabelStarred = "10" +)