imap: misc fixes

This commit is contained in:
emersion 2018-01-09 23:42:12 +01:00
parent fc7e906d1b
commit 7d67375d85
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
4 changed files with 63 additions and 14 deletions

View File

@ -30,7 +30,7 @@ func (mbox *Mailbox) bucket(tx *bolt.Tx) (*bolt.Bucket, error) {
if b == nil { if b == nil {
return nil, errors.New("cannot find mailboxes bucket") return nil, errors.New("cannot find mailboxes bucket")
} }
b = tx.Bucket([]byte(mbox.name)) b = b.Bucket([]byte(mbox.name))
if b == nil { if b == nil {
return nil, errors.New("cannot find mailbox bucket") return nil, errors.New("cannot find mailbox bucket")
} }

View File

@ -46,7 +46,7 @@ func (u *User) sync(messages []*protonmail.Message) error {
for _, msg := range messages { for _, msg := range messages {
k := []byte(msg.ID) k := []byte(msg.ID)
v, err := json.Marshal(msg) v, err := json.Marshal(msg)
if err != nil{ if err != nil {
return err return err
} }
if err := b.Put(k, v); err != nil { if err := b.Put(k, v); err != nil {
@ -72,6 +72,7 @@ func (u *User) Message(apiID string) (*protonmail.Message, error) {
return ErrNotFound return ErrNotFound
} }
msg = &protonmail.Message{}
return json.Unmarshal(v, msg) return json.Unmarshal(v, msg)
}) })
return msg, err return msg, err

View File

@ -2,7 +2,9 @@ package imap
import ( import (
"errors" "errors"
"log"
"strings" "strings"
"sync"
"time" "time"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
@ -21,6 +23,9 @@ type mailbox struct {
u *user u *user
db *database.Mailbox db *database.Mailbox
initialized bool
initializedLock sync.Mutex
total, unread int total, unread int
} }
@ -73,6 +78,8 @@ func (mbox *mailbox) Check() error {
} }
func (mbox *mailbox) sync() error { func (mbox *mailbox) sync() error {
log.Printf("Synchronizing mailbox %v...", mbox.name)
filter := &protonmail.MessageFilter{ filter := &protonmail.MessageFilter{
PageSize: 150, PageSize: 150,
Label: mbox.label, Label: mbox.label,
@ -101,6 +108,25 @@ func (mbox *mailbox) sync() error {
filter.Page++ 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 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 { func (mbox *mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error {
defer close(ch) defer close(ch)
if err := mbox.init(); err != nil {
return err
}
for _, seq := range seqSet.Set { for _, seq := range seqSet.Set {
start := seq.Start start := seq.Start
if start == 0 { if start == 0 {
@ -203,6 +233,10 @@ func matchString(s, substr string) bool {
} }
func (mbox *mailbox) SearchMessages(isUID bool, c *imap.SearchCriteria) ([]uint32, error) { 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 // TODO: c.Not, c.Or
if c.Not != nil || c.Or != nil { if c.Not != nil || c.Or != nil {
return nil, errors.New("search queries with NOT or OR clauses or not yet implemented") return nil, errors.New("search queries with NOT or OR clauses or not yet implemented")

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"io" "io"
"log"
"strings" "strings"
"time" "time"
@ -146,9 +147,6 @@ func (mbox *mailbox) fetchBodyStructure(msg *protonmail.Message, extended bool)
} }
func (mbox *mailbox) inlineBody(msg *protonmail.Message) (io.Reader, error) { 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) md, err := msg.Read(mbox.u.privateKeys, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@ -175,13 +173,19 @@ func (mbox *mailbox) attachmentBody(att *protonmail.Attachment) (io.Reader, erro
func inlineHeader(msg *protonmail.Message) message.Header { func inlineHeader(msg *protonmail.Message) message.Header {
h := mail.NewTextHeader() h := mail.NewTextHeader()
if msg.MIMEType != "" {
h.SetContentType(msg.MIMEType, nil) 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 return h.Header
} }
func attachmentHeader(att *protonmail.Attachment) message.Header { func attachmentHeader(att *protonmail.Attachment) message.Header {
h := mail.NewAttachmentHeader() h := mail.NewAttachmentHeader()
h.SetContentType(att.MIMEType, nil) h.SetContentType(att.MIMEType, nil)
h.Set("Content-Transfer-Encoding", "base64")
h.SetFilename(att.Name) h.SetFilename(att.Name)
if att.ContentID != "" { if att.ContentID != "" {
h.Set("Content-Id", 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 { func messageHeader(msg *protonmail.Message) message.Header {
h := mail.NewHeader() h := mail.NewHeader()
h.SetContentType("multipart/mixed", nil)
h.SetDate(time.Unix(msg.Time, 0)) h.SetDate(time.Unix(msg.Time, 0))
h.SetSubject(msg.Subject) h.SetSubject(msg.Subject)
h.SetAddressList("From", []*mail.Address{mailAddress(msg.Sender)}) h.SetAddressList("From", []*mail.Address{mailAddress(msg.Sender)})
if msg.ReplyTo != nil { if msg.ReplyTo != nil {
h.SetAddressList("Reply-To", []*mail.Address{mailAddress(msg.ReplyTo)}) h.SetAddressList("Reply-To", []*mail.Address{mailAddress(msg.ReplyTo)})
} }
if len(msg.ToList) > 0 {
h.SetAddressList("To", mailAddressList(msg.ToList)) h.SetAddressList("To", mailAddressList(msg.ToList))
}
if len(msg.CCList) > 0 {
h.SetAddressList("Cc", mailAddressList(msg.CCList)) h.SetAddressList("Cc", mailAddressList(msg.CCList))
}
if len(msg.BCCList) > 0 {
h.SetAddressList("Bcc", mailAddressList(msg.BCCList)) h.SetAddressList("Bcc", mailAddressList(msg.BCCList))
}
// TODO: In-Reply-To // TODO: In-Reply-To
h.Set("Message-Id", messageID(msg)) h.Set("Message-Id", messageID(msg))
return h.Header return h.Header
@ -280,13 +291,16 @@ func (mbox *mailbox) fetchBodySection(msg *protonmail.Message, section *imap.Bod
var h message.Header var h message.Header
var getBody func() (io.Reader, error) var getBody func() (io.Reader, error)
if part := section.Path[0]; part == 1 { if part := section.Path[0]; part == 1 {
h = inlineHeader(msg) // TODO: only fetch the message if the body is needed
getBody = func() (io.Reader, error) { // 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) msg, err := mbox.u.c.GetMessage(msg.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
h = inlineHeader(msg)
getBody = func() (io.Reader, error) {
return mbox.inlineBody(msg) return mbox.inlineBody(msg)
} }
} else { } else {