From: Stefan Eissing Date: Wed, 28 Aug 2024 09:09:43 +0000 (+0200) Subject: POP3: fix multi-line responses X-Git-Tag: curl-8_10_0~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4cd10ee28b5ee5ad9206c34c2c7887b2ac0cbfd2;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/pop3.c b/lib/pop3.c index 47271252e8..1d12a71632 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -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; } diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 3a893e8ead..54efa41909 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -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 \ diff --git a/tests/data/test997 b/tests/data/test997 new file mode 100644 index 0000000000..2883330f85 --- /dev/null +++ b/tests/data/test997 @@ -0,0 +1,47 @@ + + + +POP3 +Clear Text +STAT +CUSTOMREQUEST +RFC2449 + + + +# +# Server-side + + +CAPA TOP USER + + + + + +# +# Client-side + + +pop3 + + +POP3 retrieve STAT (CUSTOMREQUEST) + + +pop3://%HOSTIP:%POP3PORT -u user:secret -X 'STAT' + + + +# +# Verify data after the test has been "shot" + + +CAPA +USER user +PASS secret +STAT +QUIT + + +