imap: fix custom listing

Add test 1847 and 1848.

Fixes #20356
Closes #20360
This commit is contained in:
Christian Schmitz 2026-01-19 17:01:16 +01:00 committed by Daniel Stenberg
parent cf4164fa8d
commit e788d9d2c7
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 152 additions and 2 deletions

View file

@ -1175,6 +1175,43 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data,
return result;
}
/* Detect IMAP listings vs. downloading a single email */
static bool is_custom_fetch_listing_match(const char *params)
{
/* match " 1:* (FLAGS ..." or " 1,2,3 (FLAGS ..." */
if(*params++ != ' ')
return FALSE;
while(ISDIGIT(*params)) {
params++;
if(*params == 0)
return FALSE;
}
if(*params == ':')
return true;
if(*params == ',')
return true;
return FALSE;
}
static bool is_custom_fetch_listing(struct IMAP *imap)
{
/* filter out "UID FETCH 1:* (FLAGS ..." queries to list emails */
if(!imap->custom)
return FALSE;
else if(curl_strequal(imap->custom, "FETCH") && imap->custom_params) {
const char *p = imap->custom_params;
return is_custom_fetch_listing_match(p);
}
else if(curl_strequal(imap->custom, "UID") && imap->custom_params) {
if(curl_strnequal(imap->custom_params, " FETCH ", 7)) {
const char *p = imap->custom_params + 6;
return is_custom_fetch_listing_match(p);
}
}
return FALSE;
}
/* For LIST and SEARCH responses */
static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
struct imap_conn *imapc,
@ -1184,10 +1221,14 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
CURLcode result = CURLE_OK;
char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
size_t len = imapc->pp.nfinal;
struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
(void)instate;
if(imapcode == '*') {
if(imapcode == '*' && is_custom_fetch_listing(imap)) {
/* custom FETCH or UID FETCH for listing is not handled here */
}
else if(imapcode == '*') {
/* Check if this response contains a literal (e.g. FETCH responses with
body data). Literal syntax is {size}\r\n */
const char *cr = memchr(line, '\r', len);

View file

@ -231,7 +231,7 @@ test1680 test1681 test1682 test1683 \
test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
test1708 test1709 test1710 test1711 \
\
test1800 test1801 test1802 \
test1800 test1801 test1802 test1847 test1848 \
\
test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \
test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \

58
tests/data/test1847 Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
IMAP
Clear Text
FETCH
CUSTOMREQUEST
</keywords>
</info>
# Server-side
<reply>
<data crlf="yes">
From: me@somewhere
To: fake@nowhere
body
--
yours sincerely
</data>
<datacheck crlf="yes">
* FETCH FETCH (1 BODY[] {71}
From: me@somewhere
To: fake@nowhere
body
--
yours sincerely
</datacheck>
</reply>
# Client-side
<client>
<server>
imap
</server>
<name>
IMAP FETCH message with custom request
</name>
<command>
'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER' --request 'UID FETCH 1 BODY[]' -u '"user:sec"ret{'
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
A001 CAPABILITY
A002 LOGIN "\"user" "sec\"ret{"
A003 SELECT %TESTNUMBER
A004 UID FETCH 1 BODY[]
A005 LOGOUT
</protocol>
</verify>
</testcase>

51
tests/data/test1848 Normal file
View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
IMAP
Clear Text
FETCH
CUSTOMREQUEST
</keywords>
</info>
# Server-side
<reply>
<data crlf="yes">
From: me@somewhere
To: fake@nowhere
body
--
yours sincerely
</data>
# listing goes to debug output, so data output is empty
<datacheck crlf="yes">
</datacheck>
</reply>
# Client-side
<client>
<server>
imap
</server>
<name>
IMAP FETCH message with custom request to get list of email.
</name>
<command>
'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER' --request 'UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (Message-Id DATE FROM SUBJECT TO SENDER REPLY-TO CC BCC)])' -u '"user:sec"ret{'
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
A001 CAPABILITY
A002 LOGIN "\"user" "sec\"ret{"
A003 SELECT %TESTNUMBER
A004 UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (Message-Id DATE FROM SUBJECT TO SENDER REPLY-TO CC BCC)])
A005 LOGOUT
</protocol>
</verify>
</testcase>