Implement new SearchCriteria from latest go-imap

This commit is contained in:
Michal Horejsek
2020-04-22 14:14:24 +02:00
parent cabcb3ae2b
commit 313e803fdd
4 changed files with 158 additions and 125 deletions

1
go.sum
View File

@ -60,6 +60,7 @@ github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a h1:bM
github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ= github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e h1:L7bswVJZcf2YHofgom49oFRwVqmBj/qZqDy9/SJpZMY= github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e h1:L7bswVJZcf2YHofgom49oFRwVqmBj/qZqDy9/SJpZMY=
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78= github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78=
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
github.com/emersion/go-imap-quota v0.0.0-20171113212021-e883a2bc54d6 h1:CQ9z5Gk5HBUI8+kM5XNZXUoAv8RF1GnXmYrN4OkDvZU= github.com/emersion/go-imap-quota v0.0.0-20171113212021-e883a2bc54d6 h1:CQ9z5Gk5HBUI8+kM5XNZXUoAv8RF1GnXmYrN4OkDvZU=
github.com/emersion/go-imap-quota v0.0.0-20171113212021-e883a2bc54d6/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0= github.com/emersion/go-imap-quota v0.0.0-20171113212021-e883a2bc54d6/go.mod h1:iApyhIQBiU4XFyr+3kdJyyGqle82TbQyuP2o+OZHrV0=
github.com/emersion/go-imap-quota v0.0.0-20200423100218-dcfd1b7d2b41 h1:z5lDGnSURauBEDdNLj3o0+HogVYKQCGeY3Anl/xyRfU= github.com/emersion/go-imap-quota v0.0.0-20200423100218-dcfd1b7d2b41 h1:z5lDGnSURauBEDdNLj3o0+HogVYKQCGeY3Anl/xyRfU=

View File

@ -170,20 +170,15 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
return nil, err return nil, err
} }
var apiIDsFromUID []string
if criteria.Uid != nil { if criteria.Uid != nil {
if apiIDs, err := im.apiIDsFromSeqSet(true, criteria.Uid); err == nil { apiIDsByUID, err := im.apiIDsFromSeqSet(true, criteria.Uid)
apiIDsFromUID = append(apiIDsFromUID, apiIDs...) if err != nil {
return nil, err
} }
apiIDs = arrayIntersection(apiIDs, apiIDsByUID)
} }
// Apply filters.
for _, apiID := range apiIDs { for _, apiID := range apiIDs {
// Filter on UIDs.
if len(apiIDsFromUID) > 0 && !isStringInList(apiIDsFromUID, apiID) {
continue
}
// Get message. // Get message.
storeMessage, err := im.storeMailbox.GetMessage(apiID) storeMessage, err := im.storeMailbox.GetMessage(apiID)
if err != nil { if err != nil {
@ -192,79 +187,7 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
} }
m := storeMessage.Message() m := storeMessage.Message()
// Filter addresses. // Filter by time.
/*if criteria.From != "" && !addressMatch([]*mail.Address{m.Sender}, criteria.From) {
continue
}
if criteria.To != "" && !addressMatch(m.ToList, criteria.To) {
continue
}
if criteria.Cc != "" && !addressMatch(m.CCList, criteria.Cc) {
continue
}
if criteria.Bcc != "" && !addressMatch(m.BCCList, criteria.Bcc) {
continue
}*/
// Filter strings.
/*if criteria.Subject != "" && !strings.Contains(strings.ToLower(m.Subject), strings.ToLower(criteria.Subject)) {
continue
}
if criteria.Keyword != "" && !hasKeyword(m, criteria.Keyword) {
continue
}
if criteria.Unkeyword != "" && hasKeyword(m, criteria.Unkeyword) {
continue
}
if criteria.Header[0] != "" {
h := message.GetHeader(m)
if val := h.Get(criteria.Header[0]); val == "" {
continue // Field is not in header.
} else if criteria.Header[1] != "" && !strings.Contains(strings.ToLower(val), strings.ToLower(criteria.Header[1])) {
continue // Field is in header, second criteria is non-zero and field value not matched (case insensitive).
}
}
// Filter flags.
if criteria.Flagged && !isStringInList(m.LabelIDs, pmapi.StarredLabel) {
continue
}
if criteria.Unflagged && isStringInList(m.LabelIDs, pmapi.StarredLabel) {
continue
}
if criteria.Seen && m.Unread == 1 {
continue
}
if criteria.Unseen && m.Unread == 0 {
continue
}
if criteria.Deleted {
continue
}
// if criteria.Undeleted { // All messages matches this criteria }
if criteria.Draft && (m.Has(pmapi.FlagSent) || m.Has(pmapi.FlagReceived)) {
continue
}
if criteria.Undraft && !(m.Has(pmapi.FlagSent) || m.Has(pmapi.FlagReceived)) {
continue
}
if criteria.Answered && !(m.Has(pmapi.FlagReplied) || m.Has(pmapi.FlagRepliedAll)) {
continue
}
if criteria.Unanswered && (m.Has(pmapi.FlagReplied) || m.Has(pmapi.FlagRepliedAll)) {
continue
}
if criteria.Recent && m.Has(pmapi.FlagOpened) { // opened means not recent
continue
}
if criteria.Old && !m.Has(pmapi.FlagOpened) {
continue
}
if criteria.New && !(!m.Has(pmapi.FlagOpened) && m.Unread == 1) {
continue
}*/
// Filter internal date.
if !criteria.Before.IsZero() { if !criteria.Before.IsZero() {
if truncated := criteria.Before.Truncate(24 * time.Hour); m.Time > truncated.Unix() { if truncated := criteria.Before.Truncate(24 * time.Hour); m.Time > truncated.Unix() {
continue continue
@ -275,15 +198,8 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
continue continue
} }
} }
/*if !criteria.On.IsZero() { if !criteria.SentBefore.IsZero() || !criteria.SentSince.IsZero() {
truncated := criteria.On.Truncate(24 * time.Hour)
if m.Time < truncated.Unix() || m.Time > truncated.Add(24*time.Hour).Unix() {
continue
}
}*/
if !(criteria.SentBefore.IsZero() && criteria.SentSince.IsZero() /*&& criteria.SentOn.IsZero()*/) {
if t, err := m.Header.Date(); err == nil && !t.IsZero() { if t, err := m.Header.Date(); err == nil && !t.IsZero() {
// Filter header date.
if !criteria.SentBefore.IsZero() { if !criteria.SentBefore.IsZero() {
if truncated := criteria.SentBefore.Truncate(24 * time.Hour); t.Unix() > truncated.Unix() { if truncated := criteria.SentBefore.Truncate(24 * time.Hour); t.Unix() > truncated.Unix() {
continue continue
@ -294,16 +210,81 @@ func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria)
continue continue
} }
} }
/*if !criteria.SentOn.IsZero() {
truncated := criteria.SentOn.Truncate(24 * time.Hour)
if t.Unix() < truncated.Unix() || t.Unix() > truncated.Add(24*time.Hour).Unix() {
continue
}
}*/
} }
} }
// Filter size (only if size was already calculated). // Filter by headers.
header := message.GetHeader(m)
headerMatch := true
for criteriaKey, criteriaValues := range criteria.Header {
for _, criteriaValue := range criteriaValues {
if criteriaValue == "" {
continue
}
switch criteriaKey {
case "From":
headerMatch = addressMatch([]*mail.Address{m.Sender}, criteriaValue)
case "To":
headerMatch = addressMatch(m.ToList, criteriaValue)
case "Cc":
headerMatch = addressMatch(m.CCList, criteriaValue)
case "Bcc":
headerMatch = addressMatch(m.BCCList, criteriaValue)
default:
if messageValue := header.Get(criteriaKey); messageValue == "" {
headerMatch = false // Field is not in header.
} else if !strings.Contains(strings.ToLower(messageValue), strings.ToLower(criteriaValue)) {
headerMatch = false // Field is in header but value not matched (case insensitive).
}
}
if !headerMatch {
break
}
}
if !headerMatch {
break
}
}
if !headerMatch {
continue
}
// Filter by flags.
messageFlagsMap := make(map[string]bool)
if isStringInList(m.LabelIDs, pmapi.StarredLabel) {
messageFlagsMap[imap.FlaggedFlag] = true
}
if m.Unread == 0 {
messageFlagsMap[imap.SeenFlag] = true
}
if m.Has(pmapi.FlagReplied) || m.Has(pmapi.FlagRepliedAll) {
messageFlagsMap[imap.AnsweredFlag] = true
}
if m.Has(pmapi.FlagSent) || m.Has(pmapi.FlagReceived) {
messageFlagsMap[imap.DraftFlag] = true
}
if !m.Has(pmapi.FlagOpened) {
messageFlagsMap[imap.RecentFlag] = true
}
flagMatch := true
for _, flag := range criteria.WithFlags {
if !messageFlagsMap[flag] {
flagMatch = false
break
}
}
for _, flag := range criteria.WithoutFlags {
if messageFlagsMap[flag] {
flagMatch = false
break
}
}
if !flagMatch {
continue
}
// Filter by size (only if size was already calculated).
if m.Size > 0 { if m.Size > 0 {
if criteria.Larger != 0 && m.Size <= int64(criteria.Larger) { if criteria.Larger != 0 && m.Size <= int64(criteria.Larger) {
continue continue
@ -452,13 +433,17 @@ func (im *imapMailbox) apiIDsFromSeqSet(uid bool, seqSet *imap.SeqSet) ([]string
return apiIDs, nil return apiIDs, nil
} }
func isAddressInList(addrs []*mail.Address, query string) bool { //nolint[deadcode] func arrayIntersection(a, b []string) (c []string) {
for _, addr := range addrs { m := make(map[string]bool)
if strings.Contains(addr.Address, query) || strings.Contains(addr.Name, query) { for _, item := range a {
return true m[item] = true
}
for _, item := range b {
if _, ok := m[item]; ok {
c = append(c, item)
} }
} }
return false return
} }
func isStringInList(list []string, s string) bool { func isStringInList(list []string, s string) bool {
@ -478,12 +463,3 @@ func addressMatch(addresses []*mail.Address, criteria string) bool {
} }
return false return false
} }
func hasKeyword(m *pmapi.Message, keyword string) bool {
for _, v := range message.GetHeader(m) {
if strings.Contains(strings.ToLower(strings.Join(v, " ")), strings.ToLower(keyword)) {
return true
}
}
return false
}

View File

@ -2,28 +2,79 @@ Feature: IMAP search messages
Background: Background:
Given there is connected user "user" Given there is connected user "user"
Given there are messages in mailbox "INBOX" for "user" Given there are messages in mailbox "INBOX" for "user"
| from | to | subject | body | | from | to | cc | subject | read | starred | body |
| john.doe@mail.com | user@pm.me | foo | hello | | john.doe@email.com | user@pm.me | | foo | false | false | hello |
| jane.doe@mail.com | name@pm.me | bar | world | | jane.doe@email.com | user@pm.me | name@pm.me | bar | true | true | world |
| jane.doe@email.com | name@pm.me | | baz | true | false | bye |
And there is IMAP client logged in as "user" And there is IMAP client logged in as "user"
And there is IMAP client selected in "INBOX" And there is IMAP client selected in "INBOX"
Scenario: Search by subject Scenario: Search by Sequence numbers
When IMAP client searches for "1"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1[^0-9]*$"
Scenario: Search by UID
When IMAP client searches for "UID 2"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 2[^0-9]*$"
Scenario: Search by Sequence numbers and UID
When IMAP client searches for "1 UID 1"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1[^0-9]*$"
Scenario: Search by Sequence numbers and UID without match
When IMAP client searches for "1 UID 2"
Then IMAP response is "OK"
And IMAP response contains "SEARCH[^0-9]*$"
Scenario: Search by Subject
When IMAP client searches for "SUBJECT foo" When IMAP client searches for "SUBJECT foo"
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response has 1 message And IMAP response contains "SEARCH 3[^0-9]*$"
Scenario: Search by text Scenario: Search by From
When IMAP client searches for "TEXT world"
Then IMAP response is "OK"
And IMAP response has 1 message
Scenario: Search by from
When IMAP client searches for "FROM jane.doe@email.com" When IMAP client searches for "FROM jane.doe@email.com"
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response has 1 message And IMAP response contains "SEARCH 1 2[^0-9]*$"
Scenario: Search by to Scenario: Search by To
When IMAP client searches for "TO user@pm.me" When IMAP client searches for "TO user@pm.me"
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response has 1 message And IMAP response contains "SEARCH 2 3[^0-9]*$"
Scenario: Search by CC
When IMAP client searches for "CC name@pm.me"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 2[^0-9]*$"
Scenario: Search flagged messages
When IMAP client searches for "FLAGGED"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 2[^0-9]*$"
Scenario: Search not flagged messages
When IMAP client searches for "UNFLAGGED"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1 3[^0-9]*$"
Scenario: Search seen messages
When IMAP client searches for "SEEN"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1 2[^0-9]*$"
Scenario: Search unseen messages
When IMAP client searches for "UNSEEN"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 3[^0-9]*$"
Scenario: Search recent messages
When IMAP client searches for "RECENT"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1 2 3[^0-9]*$"
Scenario: Search by more criterias
When IMAP client searches for "SUBJECT baz TO name@pm.me SEEN UNFLAGGED"
Then IMAP response is "OK"
And IMAP response contains "SEARCH 1[^0-9]*$"

View File

@ -97,6 +97,11 @@ func thereAreMessagesInMailboxesForAddressOfUser(mailboxNames, bddAddressID, bdd
message.ToList = []*mail.Address{{ message.ToList = []*mail.Address{{
Address: ctx.EnsureAddress(account.Username(), cell.Value), Address: ctx.EnsureAddress(account.Username(), cell.Value),
}} }}
case "cc":
message.AddressID = ctx.EnsureAddressID(account.Username(), cell.Value)
message.CCList = []*mail.Address{{
Address: ctx.EnsureAddress(account.Username(), cell.Value),
}}
case "subject": case "subject":
message.Subject = cell.Value message.Subject = cell.Value
case "body": case "body":