]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
imap: fix custom listing
authorChristian Schmitz <support@monkeybreadsoftware.de>
Mon, 19 Jan 2026 16:01:16 +0000 (17:01 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 28 Jan 2026 10:37:26 +0000 (11:37 +0100)
Add test 1847 and 1848.

Fixes #20356
Closes #20360

lib/imap.c
tests/data/Makefile.am
tests/data/test1847 [new file with mode: 0644]
tests/data/test1848 [new file with mode: 0644]

index 202919d748062171090799ce5ad1e97a1eaf8e6a..b6ecc6a5a18000fa9189e3f58a6bc27cf93727fd 100644 (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);
index d7115003bfaaa8da56bd7bcb04d2058c12829a50..e0893806115af673404e0d29d34289c62f8e2b13 100644 (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 \
diff --git a/tests/data/test1847 b/tests/data/test1847
new file mode 100644 (file)
index 0000000..7ccc2d4
--- /dev/null
@@ -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>
diff --git a/tests/data/test1848 b/tests/data/test1848
new file mode 100644 (file)
index 0000000..60a31a3
--- /dev/null
@@ -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>