From: Christian Schmitz Date: Mon, 19 Jan 2026 16:01:16 +0000 (+0100) Subject: imap: fix custom listing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e788d9d2c76b8b28ef0ba548b6b335c56e22c841;p=thirdparty%2Fcurl.git imap: fix custom listing Add test 1847 and 1848. Fixes #20356 Closes #20360 --- diff --git a/lib/imap.c b/lib/imap.c index 202919d748..b6ecc6a5a1 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -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); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index d7115003bf..e089380611 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -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 \ diff --git a/tests/data/test1847 b/tests/data/test1847 new file mode 100644 index 0000000000..7ccc2d4025 --- /dev/null +++ b/tests/data/test1847 @@ -0,0 +1,58 @@ + + + + +IMAP +Clear Text +FETCH +CUSTOMREQUEST + + + +# Server-side + + +From: me@somewhere +To: fake@nowhere + +body + +-- + yours sincerely + + +* FETCH FETCH (1 BODY[] {71} +From: me@somewhere +To: fake@nowhere + +body + +-- + yours sincerely + + + +# Client-side + + +imap + + +IMAP FETCH message with custom request + + +'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER' --request 'UID FETCH 1 BODY[]' -u '"user:sec"ret{' + + + +# Verify data after the test has been "shot" + + +A001 CAPABILITY +A002 LOGIN "\"user" "sec\"ret{" +A003 SELECT %TESTNUMBER +A004 UID FETCH 1 BODY[] +A005 LOGOUT + + + diff --git a/tests/data/test1848 b/tests/data/test1848 new file mode 100644 index 0000000000..60a31a3db8 --- /dev/null +++ b/tests/data/test1848 @@ -0,0 +1,51 @@ + + + + +IMAP +Clear Text +FETCH +CUSTOMREQUEST + + + +# Server-side + + +From: me@somewhere +To: fake@nowhere + +body + +-- + yours sincerely + +# listing goes to debug output, so data output is empty + + + + +# Client-side + + +imap + + +IMAP FETCH message with custom request to get list of email. + + +'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{' + + + +# Verify data after the test has been "shot" + + +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 + + +