From 7d67375d851cf71f33acca75d472c429cf94602f Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 9 Jan 2018 23:42:12 +0100 Subject: [PATCH] imap: misc fixes --- imap/database/mailbox.go | 2 +- imap/database/user.go | 3 ++- imap/mailbox.go | 34 ++++++++++++++++++++++++++++++++++ imap/message.go | 38 ++++++++++++++++++++++++++------------ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/imap/database/mailbox.go b/imap/database/mailbox.go index f6916eb..63bdd2f 100644 --- a/imap/database/mailbox.go +++ b/imap/database/mailbox.go @@ -30,7 +30,7 @@ func (mbox *Mailbox) bucket(tx *bolt.Tx) (*bolt.Bucket, error) { if b == nil { return nil, errors.New("cannot find mailboxes bucket") } - b = tx.Bucket([]byte(mbox.name)) + b = b.Bucket([]byte(mbox.name)) if b == nil { return nil, errors.New("cannot find mailbox bucket") } diff --git a/imap/database/user.go b/imap/database/user.go index 115f72e..27af30c 100644 --- a/imap/database/user.go +++ b/imap/database/user.go @@ -46,7 +46,7 @@ func (u *User) sync(messages []*protonmail.Message) error { for _, msg := range messages { k := []byte(msg.ID) v, err := json.Marshal(msg) - if err != nil{ + if err != nil { return err } if err := b.Put(k, v); err != nil { @@ -72,6 +72,7 @@ func (u *User) Message(apiID string) (*protonmail.Message, error) { return ErrNotFound } + msg = &protonmail.Message{} return json.Unmarshal(v, msg) }) return msg, err diff --git a/imap/mailbox.go b/imap/mailbox.go index 4e375b0..75ad27c 100644 --- a/imap/mailbox.go +++ b/imap/mailbox.go @@ -2,7 +2,9 @@ package imap import ( "errors" + "log" "strings" + "sync" "time" "github.com/emersion/go-imap" @@ -21,6 +23,9 @@ type mailbox struct { u *user db *database.Mailbox + initialized bool + initializedLock sync.Mutex + total, unread int } @@ -73,6 +78,8 @@ func (mbox *mailbox) Check() error { } func (mbox *mailbox) sync() error { + log.Printf("Synchronizing mailbox %v...", mbox.name) + filter := &protonmail.MessageFilter{ PageSize: 150, Label: mbox.label, @@ -101,6 +108,25 @@ func (mbox *mailbox) sync() error { filter.Page++ } + log.Printf("Synchronizing mailbox %v: done.", mbox.name) + + return nil +} + +func (mbox *mailbox) init() error { + mbox.initializedLock.Lock() + defer mbox.initializedLock.Unlock() + + if mbox.initialized { + return nil + } + + // TODO: sync only the first time + if err := mbox.sync(); err != nil { + return err + } + + mbox.initialized = true return nil } @@ -165,6 +191,10 @@ func (mbox *mailbox) fetchMessage(isUid bool, id uint32, items []imap.FetchItem) func (mbox *mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error { defer close(ch) + if err := mbox.init(); err != nil { + return err + } + for _, seq := range seqSet.Set { start := seq.Start if start == 0 { @@ -203,6 +233,10 @@ func matchString(s, substr string) bool { } func (mbox *mailbox) SearchMessages(isUID bool, c *imap.SearchCriteria) ([]uint32, error) { + if err := mbox.init(); err != nil { + return nil, err + } + // TODO: c.Not, c.Or if c.Not != nil || c.Or != nil { return nil, errors.New("search queries with NOT or OR clauses or not yet implemented") diff --git a/imap/message.go b/imap/message.go index f5ae153..6aee89b 100644 --- a/imap/message.go +++ b/imap/message.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "io" + "log" "strings" "time" @@ -146,9 +147,6 @@ func (mbox *mailbox) fetchBodyStructure(msg *protonmail.Message, extended bool) } func (mbox *mailbox) inlineBody(msg *protonmail.Message) (io.Reader, error) { - h := mail.NewTextHeader() - h.SetContentType(msg.MIMEType, nil) - md, err := msg.Read(mbox.u.privateKeys, nil) if err != nil { return nil, err @@ -175,13 +173,19 @@ func (mbox *mailbox) attachmentBody(att *protonmail.Attachment) (io.Reader, erro func inlineHeader(msg *protonmail.Message) message.Header { h := mail.NewTextHeader() - h.SetContentType(msg.MIMEType, nil) + if msg.MIMEType != "" { + h.SetContentType(msg.MIMEType, nil) + } else { + log.Println("Sending an inline header without its proper MIME type") + } + h.Set("Content-Transfer-Encoding", "quoted-printable") return h.Header } func attachmentHeader(att *protonmail.Attachment) message.Header { h := mail.NewAttachmentHeader() h.SetContentType(att.MIMEType, nil) + h.Set("Content-Transfer-Encoding", "base64") h.SetFilename(att.Name) if att.ContentID != "" { h.Set("Content-Id", att.ContentID) @@ -206,15 +210,22 @@ func mailAddressList(addresses []*protonmail.MessageAddress) []*mail.Address { func messageHeader(msg *protonmail.Message) message.Header { h := mail.NewHeader() + h.SetContentType("multipart/mixed", nil) h.SetDate(time.Unix(msg.Time, 0)) h.SetSubject(msg.Subject) h.SetAddressList("From", []*mail.Address{mailAddress(msg.Sender)}) if msg.ReplyTo != nil { h.SetAddressList("Reply-To", []*mail.Address{mailAddress(msg.ReplyTo)}) } - h.SetAddressList("To", mailAddressList(msg.ToList)) - h.SetAddressList("Cc", mailAddressList(msg.CCList)) - h.SetAddressList("Bcc", mailAddressList(msg.BCCList)) + if len(msg.ToList) > 0 { + h.SetAddressList("To", mailAddressList(msg.ToList)) + } + if len(msg.CCList) > 0 { + h.SetAddressList("Cc", mailAddressList(msg.CCList)) + } + if len(msg.BCCList) > 0 { + h.SetAddressList("Bcc", mailAddressList(msg.BCCList)) + } // TODO: In-Reply-To h.Set("Message-Id", messageID(msg)) return h.Header @@ -280,13 +291,16 @@ func (mbox *mailbox) fetchBodySection(msg *protonmail.Message, section *imap.Bod var h message.Header var getBody func() (io.Reader, error) if part := section.Path[0]; part == 1 { + // TODO: only fetch the message if the body is needed + // For now we fetch it in all cases because the MIME type is not included + // in the cached message, and inlineHeader needs it + msg, err := mbox.u.c.GetMessage(msg.ID) + if err != nil { + return nil, err + } + h = inlineHeader(msg) getBody = func() (io.Reader, error) { - msg, err := mbox.u.c.GetMessage(msg.ID) - if err != nil { - return nil, err - } - return mbox.inlineBody(msg) } } else {