POP3: fix multi-line responses

Some POP3 commands are multi-line, e.g. have responses terminated by a
last line with '.', but some are not. Define the known command
properties and fix response handling.

Add test case for STAT.

Fixes #14677
Reported-by: ralfjunker on github
Closes #14707
This commit is contained in:
Stefan Eissing 2024-08-28 11:09:43 +02:00 committed by Daniel Stenberg
parent bc81292ea6
commit 4cd10ee28b
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
3 changed files with 107 additions and 7 deletions

View File

@ -83,6 +83,10 @@
#include "curl_memory.h"
#include "memdebug.h"
#ifndef ARRAYSIZE
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
/* Local API functions */
static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
static CURLcode pop3_do(struct Curl_easy *data, bool *done);
@ -199,6 +203,53 @@ static void pop3_to_pop3s(struct connectdata *conn)
#define pop3_to_pop3s(x) Curl_nop_stmt
#endif
struct pop3_cmd {
const char *name;
unsigned short nlen;
BIT(multiline); /* response is multi-line with last '.' line */
BIT(multiline_with_args); /* is multi-line when command has args */
};
static const struct pop3_cmd pop3cmds[] = {
{ "APOP", 4, FALSE, FALSE },
{ "AUTH", 4, FALSE, FALSE },
{ "CAPA", 4, TRUE, TRUE },
{ "DELE", 4, FALSE, FALSE },
{ "LIST", 4, TRUE, TRUE },
{ "MSG", 3, TRUE, TRUE },
{ "NOOP", 4, FALSE, FALSE },
{ "PASS", 4, FALSE, FALSE },
{ "QUIT", 4, FALSE, FALSE },
{ "RETR", 4, TRUE, TRUE },
{ "RSET", 4, FALSE, FALSE },
{ "STAT", 4, FALSE, FALSE },
{ "STLS", 4, FALSE, FALSE },
{ "TOP", 3, TRUE, TRUE },
{ "UIDL", 4, TRUE, FALSE },
{ "USER", 4, FALSE, FALSE },
{ "UTF8", 4, FALSE, FALSE },
{ "XTND", 4, TRUE, TRUE },
};
/* Return iff a command is defined as "multi-line" (RFC 1939),
* has a response terminated by a last line with a '.'.
*/
static bool pop3_is_multiline(const char *cmdline)
{
size_t i;
for(i = 0; i < ARRAYSIZE(pop3cmds); ++i) {
if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) {
if(!cmdline[pop3cmds[i].nlen])
return pop3cmds[i].multiline;
else if(cmdline[pop3cmds[i].nlen] == ' ')
return pop3cmds[i].multiline_with_args;
}
}
/* Unknown command, assume multi-line for backward compatibility with
* earlier curl versions that only could do multi-line responses. */
return TRUE;
}
/***********************************************************************
*
* pop3_endofresp()
@ -614,18 +665,20 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
else
command = "RETR";
if(pop3->custom && pop3->custom[0] != '\0')
command = pop3->custom;
/* Send the command */
if(pop3->id[0] != '\0')
result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s",
(pop3->custom && pop3->custom[0] != '\0' ?
pop3->custom : command), pop3->id);
command, pop3->id);
else
result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s",
(pop3->custom && pop3->custom[0] != '\0' ?
pop3->custom : command));
result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", command);
if(!result)
if(!result) {
pop3_state(data, POP3_COMMAND);
data->req.no_body = !pop3_is_multiline(command);
}
return result;
}

View File

@ -131,7 +131,7 @@ test961 test962 test963 test964 test965 test966 test967 test968 test969 \
test970 test971 test972 test973 test974 test975 test976 test977 test978 \
test979 test980 test981 test982 test983 test984 test985 test986 test987 \
test988 test989 test990 test991 test992 test993 test994 test995 test996 \
\
test997 \
test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \
test1016 test1017 test1018 test1019 test1020 test1021 test1022 test1023 \

47
tests/data/test997 Normal file
View File

@ -0,0 +1,47 @@
<testcase>
<info>
<keywords>
POP3
Clear Text
STAT
CUSTOMREQUEST
RFC2449
</keywords>
</info>
#
# Server-side
<reply>
<servercmd>
CAPA TOP USER
</servercmd>
<data>
</data>
</reply>
#
# Client-side
<client>
<server>
pop3
</server>
<name>
POP3 retrieve STAT (CUSTOMREQUEST)
</name>
<command>
pop3://%HOSTIP:%POP3PORT -u user:secret -X 'STAT'
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol>
CAPA
USER user
PASS secret
STAT
QUIT
</protocol>
</verify>
</testcase>