Skip to content

Commit

Permalink
imapclient/search: handle UID SEARCH results without hits
Browse files Browse the repository at this point in the history
When searching for unseen mails we can get the following error if there
are none.

> in response-data: imapwire: expected SP, got "\r"

The reason is that the IMAP server is sending a response like this in
case there are no search hits.

T7 UID SEARCH RETURN (ALL) (UNSEEN)
* ESEARCH (TAG "T7") UID T7 OK SEARCH completed (Success)

The wire protocol parser is expecting a search option (like "ALL") to be
present in any search result. As we can see above, this is not the case
if there are no results, which results in the parser hitting a CRLF when
it expected a space.

To account for this we peek into the reply of the server to see whether
there is a CRLF after the "UID" atom. If there is, we assume there were
no results and return an empty result.
  • Loading branch information
Shugyousha authored Mar 19, 2024
1 parent ea10af8 commit f438355
Showing 1 changed file with 14 additions and 7 deletions.
21 changes: 14 additions & 7 deletions imapclient/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,9 @@ func flagSearchKey(flag imap.Flag) string {

func readESearchResponse(dec *imapwire.Decoder) (tag string, data *imap.SearchData, err error) {
data = &imap.SearchData{}

if dec.Special('(') { // search-correlator
var correlator string
if !dec.ExpectAtom(&correlator) || !dec.ExpectSP() || !dec.ExpectAString(&tag) || !dec.ExpectSpecial(')') || !dec.ExpectSP() {
if !dec.ExpectAtom(&correlator) || !dec.ExpectSP() || !dec.ExpectAString(&tag) || !dec.ExpectSpecial(')') {
return "", nil, dec.Err()
}
if correlator != "TAG" {
Expand All @@ -289,16 +288,26 @@ func readESearchResponse(dec *imapwire.Decoder) (tag string, data *imap.SearchDa
}

var name string
if !dec.ExpectAtom(&name) || !dec.ExpectSP() {
if !dec.SP() {
return tag, data, nil
} else if !dec.ExpectAtom(&name) {
return "", nil, dec.Err()
}
data.UID = name == "UID"

if data.UID {
if !dec.ExpectAtom(&name) || !dec.ExpectSP() {
if !dec.SP() {
return tag, data, nil
} else if !dec.ExpectAtom(&name) {
return "", nil, dec.Err()
}
}

for {
if !dec.ExpectSP() {
return "", nil, dec.Err()
}

switch strings.ToUpper(name) {
case "MIN":
var num uint32
Expand Down Expand Up @@ -343,9 +352,7 @@ func readESearchResponse(dec *imapwire.Decoder) (tag string, data *imap.SearchDa

if !dec.SP() {
break
}

if !dec.ExpectAtom(&name) || !dec.ExpectSP() {
} else if !dec.ExpectAtom(&name) {
return "", nil, dec.Err()
}
}
Expand Down

0 comments on commit f438355

Please sign in to comment.