From f4383550ac555df1541694e7b32b35553bc3512a Mon Sep 17 00:00:00 2001 From: Shugyousha Date: Wed, 20 Mar 2024 00:32:49 +0100 Subject: [PATCH] imapclient/search: handle UID SEARCH results without hits 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. --- imapclient/search.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/imapclient/search.go b/imapclient/search.go index 1ef3292f..58c79583 100644 --- a/imapclient/search.go +++ b/imapclient/search.go @@ -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" { @@ -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 @@ -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() } }