Update vendor/
This commit is contained in:
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func matchString(s, substr string) bool {
|
||||
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||||
}
|
||||
|
||||
func bufferBody(e *message.Entity) (*bytes.Buffer, error) {
|
||||
b := new(bytes.Buffer)
|
||||
if _, err := io.Copy(b, e.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Body = b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func matchBody(e *message.Entity, substr string) (bool, error) {
|
||||
if s, ok := e.Body.(fmt.Stringer); ok {
|
||||
return matchString(s.String(), substr), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return matchString(b.String(), substr), nil
|
||||
}
|
||||
|
||||
type lengther interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
func bodyLen(e *message.Entity) (int, error) {
|
||||
if l, ok := e.Body.(lengther); ok {
|
||||
return l.Len(), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.Len(), nil
|
||||
}
|
||||
|
||||
// Match returns true if a message matches the provided criteria. Sequence
|
||||
// number, UID, flag and internal date contrainsts are not checked.
|
||||
func Match(e *message.Entity, c *imap.SearchCriteria) (bool, error) {
|
||||
// TODO: support encoded header fields for Bcc, Cc, From, To
|
||||
// TODO: add header size for Larger and Smaller
|
||||
|
||||
h := mail.Header{e.Header}
|
||||
|
||||
if !c.SentBefore.IsZero() || !c.SentSince.IsZero() {
|
||||
t, err := h.Date()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
t = t.Round(24 * time.Hour)
|
||||
|
||||
if !c.SentBefore.IsZero() && !t.Before(c.SentBefore) {
|
||||
return false, nil
|
||||
}
|
||||
if !c.SentSince.IsZero() && !t.After(c.SentSince) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for key, wantValues := range c.Header {
|
||||
values, ok := e.Header[key]
|
||||
for _, wantValue := range wantValues {
|
||||
if wantValue == "" && !ok {
|
||||
return false, nil
|
||||
}
|
||||
if wantValue != "" {
|
||||
ok := false
|
||||
for _, v := range values {
|
||||
if matchString(v, wantValue) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, body := range c.Body {
|
||||
if ok, err := matchBody(e, body); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, text := range c.Text {
|
||||
// TODO: also match header fields
|
||||
if ok, err := matchBody(e, text); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Larger > 0 || c.Smaller > 0 {
|
||||
n, err := bodyLen(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if c.Larger > 0 && uint32(n) < c.Larger {
|
||||
return false, nil
|
||||
}
|
||||
if c.Smaller > 0 && uint32(n) > c.Smaller {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
ok, err := Match(e, not)
|
||||
if err != nil || ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
ok1, err := Match(e, or[0])
|
||||
if err != nil {
|
||||
return ok1, err
|
||||
}
|
||||
|
||||
ok2, err := Match(e, or[1])
|
||||
if err != nil || (!ok1 && !ok2) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func matchFlags(flags map[string]bool, c *imap.SearchCriteria) bool {
|
||||
for _, f := range c.WithFlags {
|
||||
if !flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, f := range c.WithoutFlags {
|
||||
if flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if matchFlags(flags, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !matchFlags(flags, or[0]) && !matchFlags(flags, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchFlags returns true if a flag list matches the provided criteria.
|
||||
func MatchFlags(flags []string, c *imap.SearchCriteria) bool {
|
||||
flagsMap := make(map[string]bool)
|
||||
for _, f := range flags {
|
||||
flagsMap[f] = true
|
||||
}
|
||||
|
||||
return matchFlags(flagsMap, c)
|
||||
}
|
||||
|
||||
// MatchSeqNumAndUid returns true if a sequence number and a UID matches the
|
||||
// provided criteria.
|
||||
func MatchSeqNumAndUid(seqNum uint32, uid uint32, c *imap.SearchCriteria) bool {
|
||||
if c.SeqNum != nil && !c.SeqNum.Contains(seqNum) {
|
||||
return false
|
||||
}
|
||||
if c.Uid != nil && !c.Uid.Contains(uid) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchSeqNumAndUid(seqNum, uid, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchSeqNumAndUid(seqNum, uid, or[0]) && !MatchSeqNumAndUid(seqNum, uid, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchDate returns true if a date matches the provided criteria.
|
||||
func MatchDate(date time.Time, c *imap.SearchCriteria) bool {
|
||||
date = date.Round(24 * time.Hour)
|
||||
if !c.Since.IsZero() && !date.After(c.Since) {
|
||||
return false
|
||||
}
|
||||
if !c.Before.IsZero() && !date.Before(c.Before) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchDate(date, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchDate(date, or[0]) && !MatchDate(date, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
Reference in New Issue
Block a user