imap: implement mailbox.SearchMessages

This commit is contained in:
emersion 2018-01-09 20:03:19 +01:00
parent 650e37cb4f
commit eef2d8ba22
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
2 changed files with 119 additions and 2 deletions

View File

@ -154,3 +154,23 @@ func (mbox *Mailbox) FromApiID(apiID string) (uint32, uint32, error) {
}) })
return seqNum, uid, err return seqNum, uid, err
} }
func (mbox *Mailbox) ForEach(f func(seqNum, uid uint32, apiID string) error) error {
return mbox.u.db.View(func(tx *bolt.Tx) error {
b, err := mbox.bucket(tx)
if err != nil {
return err
}
c := b.Cursor()
var n uint32 = 1
for k, v := c.First(); k != nil; k, v = c.Next() {
if err := f(n, unserializeUID(k), string(v)); err != nil {
return err
}
n++
}
return nil
})
}

View File

@ -1,6 +1,8 @@
package imap package imap
import ( import (
"errors"
"strings"
"time" "time"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
@ -196,8 +198,103 @@ func (mbox *mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.Fe
return nil return nil
} }
func (mbox *mailbox) SearchMessages(uid bool, criteria *imap.SearchCriteria) ([]uint32, error) { func matchString(s, substr string) bool {
return nil, errNotYetImplemented // TODO return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
}
func (mbox *mailbox) SearchMessages(isUID bool, c *imap.SearchCriteria) ([]uint32, error) {
// 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")
}
var results []uint32
err := mbox.db.ForEach(func(seqNum, uid uint32, apiID string) error {
if c.SeqNum != nil && !c.SeqNum.Contains(seqNum) {
return nil
}
if c.Uid != nil && !c.Uid.Contains(uid) {
return nil
}
// TODO: fetch message from local DB only if needed
msg, err := mbox.u.db.Message(apiID)
if err != nil {
return err
}
flags := make(map[string]bool)
for _, flag := range fetchFlags(msg) {
flags[flag] = true
}
for _, f := range c.WithFlags {
if !flags[f] {
return nil
}
}
for _, f := range c.WithoutFlags {
if flags[f] {
return nil
}
}
date := time.Unix(msg.Time, 0).Round(24 * time.Hour)
if !c.Since.IsZero() && !date.After(c.Since) {
return nil
}
if !c.Before.IsZero() && !date.Before(c.Before) {
return nil
}
// TODO: this date should be from the Date MIME header
if !c.SentBefore.IsZero() && !date.Before(c.SentBefore) {
return nil
}
if !c.SentSince.IsZero() && !date.After(c.SentSince) {
return nil
}
h := messageHeader(msg)
for key, wantValues := range c.Header {
values, ok := h[key]
for _, wantValue := range wantValues {
if wantValue == "" && !ok {
return nil
}
if wantValue != "" {
ok := false
for _, v := range values {
if matchString(v, wantValue) {
ok = true
break
}
}
if !ok {
return nil
}
}
}
}
// TODO: c.Body, c.Text
if c.Larger > 0 && uint32(msg.Size) < c.Larger {
return nil
}
if c.Smaller > 0 && uint32(msg.Size) > c.Smaller {
return nil
}
if isUID {
results = append(results, uid)
} else {
results = append(results, seqNum)
}
return nil
})
if err != nil {
return nil, err
}
return results, nil
} }
func (mbox *mailbox) CreateMessage(flags []string, date time.Time, body imap.Literal) error { func (mbox *mailbox) CreateMessage(flags []string, date time.Time, body imap.Literal) error {