1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC1734 POP3 Authentication
22 * RFC1939 POP3 protocol
23 * RFC2195 CRAM-MD5 authentication
24 * RFC2384 POP URL Scheme
25 * RFC2449 POP3 Extension Mechanism
26 * RFC2595 Using TLS with IMAP, POP3 and ACAP
27 * RFC2831 DIGEST-MD5 authentication
28 * RFC4422 Simple Authentication and Security Layer (SASL)
29 * RFC4616 PLAIN authentication
30 * RFC5034 POP3 SASL Authentication Mechanism
32 ***************************************************************************/
34 #include "curl_setup.h"
36 #ifndef CURL_DISABLE_POP3
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
45 #include <sys/utsname.h>
55 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #define in_addr_t unsigned long
60 #include <curl/curl.h>
68 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
81 #include "curl_sasl.h"
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
88 #include "curl_memory.h"
89 /* The last #include file should be: */
92 /* Local API functions */
93 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
94 static CURLcode pop3_do(struct connectdata *conn, bool *done);
95 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
97 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
98 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
99 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
100 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
102 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
103 static CURLcode pop3_setup_connection(struct connectdata *conn);
104 static CURLcode pop3_parse_url_options(struct connectdata *conn);
105 static CURLcode pop3_parse_url_path(struct connectdata *conn);
106 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
109 * POP3 protocol handler.
112 const struct Curl_handler Curl_handler_pop3 = {
114 pop3_setup_connection, /* setup_connection */
116 pop3_done, /* done */
117 ZERO_NULL, /* do_more */
118 pop3_connect, /* connect_it */
119 pop3_multi_statemach, /* connecting */
120 pop3_doing, /* doing */
121 pop3_getsock, /* proto_getsock */
122 pop3_getsock, /* doing_getsock */
123 ZERO_NULL, /* domore_getsock */
124 ZERO_NULL, /* perform_getsock */
125 pop3_disconnect, /* disconnect */
126 ZERO_NULL, /* readwrite */
127 PORT_POP3, /* defport */
128 CURLPROTO_POP3, /* protocol */
129 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
134 * POP3S protocol handler.
137 const struct Curl_handler Curl_handler_pop3s = {
138 "POP3S", /* scheme */
139 pop3_setup_connection, /* setup_connection */
141 pop3_done, /* done */
142 ZERO_NULL, /* do_more */
143 pop3_connect, /* connect_it */
144 pop3_multi_statemach, /* connecting */
145 pop3_doing, /* doing */
146 pop3_getsock, /* proto_getsock */
147 pop3_getsock, /* doing_getsock */
148 ZERO_NULL, /* domore_getsock */
149 ZERO_NULL, /* perform_getsock */
150 pop3_disconnect, /* disconnect */
151 ZERO_NULL, /* readwrite */
152 PORT_POP3S, /* defport */
153 CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
154 PROTOPT_CLOSEACTION | PROTOPT_SSL
155 | PROTOPT_NOURLQUERY /* flags */
159 #ifndef CURL_DISABLE_HTTP
161 * HTTP-proxyed POP3 protocol handler.
164 static const struct Curl_handler Curl_handler_pop3_proxy = {
166 ZERO_NULL, /* setup_connection */
167 Curl_http, /* do_it */
168 Curl_http_done, /* done */
169 ZERO_NULL, /* do_more */
170 ZERO_NULL, /* connect_it */
171 ZERO_NULL, /* connecting */
172 ZERO_NULL, /* doing */
173 ZERO_NULL, /* proto_getsock */
174 ZERO_NULL, /* doing_getsock */
175 ZERO_NULL, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ZERO_NULL, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_POP3, /* defport */
180 CURLPROTO_HTTP, /* protocol */
181 PROTOPT_NONE /* flags */
186 * HTTP-proxyed POP3S protocol handler.
189 static const struct Curl_handler Curl_handler_pop3s_proxy = {
190 "POP3S", /* scheme */
191 ZERO_NULL, /* setup_connection */
192 Curl_http, /* do_it */
193 Curl_http_done, /* done */
194 ZERO_NULL, /* do_more */
195 ZERO_NULL, /* connect_it */
196 ZERO_NULL, /* connecting */
197 ZERO_NULL, /* doing */
198 ZERO_NULL, /* proto_getsock */
199 ZERO_NULL, /* doing_getsock */
200 ZERO_NULL, /* domore_getsock */
201 ZERO_NULL, /* perform_getsock */
202 ZERO_NULL, /* disconnect */
203 ZERO_NULL, /* readwrite */
204 PORT_POP3S, /* defport */
205 CURLPROTO_HTTP, /* protocol */
206 PROTOPT_NONE /* flags */
212 static void pop3_to_pop3s(struct connectdata *conn)
214 conn->handler = &Curl_handler_pop3s;
217 #define pop3_to_pop3s(x) Curl_nop_stmt
220 /* Function that checks for an ending POP3 status code at the start of the
221 given string, but also detects the APOP timestamp from the server greeting
222 and various capabilities from the CAPA response including the supported
223 authentication types and allowed SASL mechanisms. */
224 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
227 struct pop3_conn *pop3c = &conn->proto.pop3c;
231 /* Do we have an error response? */
232 if(len >= 4 && !memcmp("-ERR", line, 4)) {
238 /* Are we processing servergreet responses? */
239 if(pop3c->state == POP3_SERVERGREET) {
240 /* Look for the APOP timestamp */
241 if(len >= 3 && line[len - 3] == '>') {
242 for(i = 0; i < len - 3; ++i) {
244 /* Calculate the length of the timestamp */
245 size_t timestamplen = len - 2 - i;
247 /* Allocate some memory for the timestamp */
248 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
250 if(!pop3c->apoptimestamp)
253 /* Copy the timestamp */
254 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
255 pop3c->apoptimestamp[timestamplen] = '\0';
261 /* Are we processing CAPA command responses? */
262 else if(pop3c->state == POP3_CAPA) {
263 /* Do we have the terminating line? */
264 if(len >= 1 && !memcmp(line, ".", 1)) {
270 /* Does the server support the STLS capability? */
271 if(len >= 4 && !memcmp(line, "STLS", 4))
272 pop3c->tls_supported = TRUE;
274 /* Does the server support clear text authentication? */
275 else if(len >= 4 && !memcmp(line, "USER", 4))
276 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
278 /* Does the server support APOP authentication? */
279 else if(len >= 4 && !memcmp(line, "APOP", 4))
280 pop3c->authtypes |= POP3_TYPE_APOP;
282 /* Does the server support SASL based authentication? */
283 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
284 pop3c->authtypes |= POP3_TYPE_SASL;
286 /* Advance past the SASL keyword */
290 /* Loop through the data line */
293 (*line == ' ' || *line == '\t' ||
294 *line == '\r' || *line == '\n')) {
303 /* Extract the word */
304 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
305 line[wordlen] != '\t' && line[wordlen] != '\r' &&
306 line[wordlen] != '\n';)
309 /* Test the word for a matching authentication mechanism */
310 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
311 pop3c->authmechs |= SASL_MECH_LOGIN;
312 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
313 pop3c->authmechs |= SASL_MECH_PLAIN;
314 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
315 pop3c->authmechs |= SASL_MECH_CRAM_MD5;
316 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
317 pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
318 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
319 pop3c->authmechs |= SASL_MECH_GSSAPI;
320 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
321 pop3c->authmechs |= SASL_MECH_EXTERNAL;
322 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
323 pop3c->authmechs |= SASL_MECH_NTLM;
333 /* Do we have a command or continuation response? */
334 if((len >= 3 && !memcmp("+OK", line, 3)) ||
335 (len >= 1 && !memcmp("+", line, 1))) {
341 return FALSE; /* Nothing for us */
344 /* This is the ONLY way to change POP3 state! */
345 static void state(struct connectdata *conn, pop3state newstate)
347 struct pop3_conn *pop3c = &conn->proto.pop3c;
348 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
349 /* for debug purposes */
350 static const char * const names[] = {
361 "AUTH_DIGESTMD5_RESP",
363 "AUTH_NTLM_TYPE2MSG",
373 if(pop3c->state != newstate)
374 infof(conn->data, "POP3 %p state change from %s to %s\n",
375 pop3c, names[pop3c->state], names[newstate]);
378 pop3c->state = newstate;
381 static CURLcode pop3_state_capa(struct connectdata *conn)
383 CURLcode result = CURLE_OK;
384 struct pop3_conn *pop3c = &conn->proto.pop3c;
386 pop3c->authmechs = 0; /* No known authentication mechanisms yet */
387 pop3c->authused = 0; /* Clear the authentication mechanism used */
388 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
390 /* Send the CAPA command */
391 result = Curl_pp_sendf(&pop3c->pp, "CAPA");
394 state(conn, POP3_CAPA);
399 static CURLcode pop3_state_starttls(struct connectdata *conn)
401 CURLcode result = CURLE_OK;
403 /* Send the STLS command */
404 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "STLS");
407 state(conn, POP3_STARTTLS);
412 static CURLcode pop3_state_upgrade_tls(struct connectdata *conn)
414 CURLcode result = CURLE_OK;
415 struct pop3_conn *pop3c = &conn->proto.pop3c;
417 /* Start the SSL connection */
418 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
421 if(pop3c->state != POP3_UPGRADETLS)
422 state(conn, POP3_UPGRADETLS);
426 result = pop3_state_capa(conn);
433 static CURLcode pop3_state_user(struct connectdata *conn)
435 CURLcode result = CURLE_OK;
437 /* Check we have a username and password to authenticate with and end the
438 connect phase if we don't */
439 if(!conn->bits.user_passwd) {
440 state(conn, POP3_STOP);
445 /* Send the USER command */
446 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
447 conn->user ? conn->user : "");
449 state(conn, POP3_USER);
454 #ifndef CURL_DISABLE_CRYPTO_AUTH
455 static CURLcode pop3_state_apop(struct connectdata *conn)
457 CURLcode result = CURLE_OK;
458 struct pop3_conn *pop3c = &conn->proto.pop3c;
461 unsigned char digest[MD5_DIGEST_LEN];
462 char secret[2 * MD5_DIGEST_LEN + 1];
464 /* Check we have a username and password to authenticate with and end the
465 connect phase if we don't */
466 if(!conn->bits.user_passwd) {
467 state(conn, POP3_STOP);
472 /* Create the digest */
473 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
475 return CURLE_OUT_OF_MEMORY;
477 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
478 curlx_uztoui(strlen(pop3c->apoptimestamp)));
480 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
481 curlx_uztoui(strlen(conn->passwd)));
483 /* Finalise the digest */
484 Curl_MD5_final(ctxt, digest);
486 /* Convert the calculated 16 octet digest into a 32 byte hex string */
487 for(i = 0; i < MD5_DIGEST_LEN; i++)
488 snprintf(&secret[2 * i], 3, "%02x", digest[i]);
490 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
493 state(conn, POP3_APOP);
499 static CURLcode pop3_authenticate(struct connectdata *conn)
501 CURLcode result = CURLE_OK;
502 struct pop3_conn *pop3c = &conn->proto.pop3c;
503 const char *mech = NULL;
504 pop3state authstate = POP3_STOP;
506 /* Check we have a username and password to authenticate with and end the
507 connect phase if we don't */
508 if(!conn->bits.user_passwd) {
509 state(conn, POP3_STOP);
514 /* Calculate the supported authentication mechanism by decreasing order of
516 if(pop3c->authtypes & POP3_TYPE_SASL) {
517 #ifndef CURL_DISABLE_CRYPTO_AUTH
518 if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
519 (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
521 authstate = POP3_AUTH_DIGESTMD5;
522 pop3c->authused = SASL_MECH_DIGEST_MD5;
524 else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
525 (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
527 authstate = POP3_AUTH_CRAMMD5;
528 pop3c->authused = SASL_MECH_CRAM_MD5;
533 if((pop3c->authmechs & SASL_MECH_NTLM) &&
534 (pop3c->prefmech & SASL_MECH_NTLM)) {
536 authstate = POP3_AUTH_NTLM;
537 pop3c->authused = SASL_MECH_NTLM;
541 if((pop3c->authmechs & SASL_MECH_LOGIN) &&
542 (pop3c->prefmech & SASL_MECH_LOGIN)) {
544 authstate = POP3_AUTH_LOGIN;
545 pop3c->authused = SASL_MECH_LOGIN;
547 else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
548 (pop3c->prefmech & SASL_MECH_PLAIN)) {
550 authstate = POP3_AUTH_PLAIN;
551 pop3c->authused = SASL_MECH_PLAIN;
555 if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
556 /* Perform SASL based authentication */
557 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
560 state(conn, authstate);
562 #ifndef CURL_DISABLE_CRYPTO_AUTH
563 else if((pop3c->authtypes & POP3_TYPE_APOP) &&
564 (pop3c->preftype & POP3_TYPE_APOP))
565 /* Perform APOP authentication */
566 result = pop3_state_apop(conn);
568 else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
569 (pop3c->preftype & POP3_TYPE_CLEARTEXT))
570 /* Perform clear text authentication */
571 result = pop3_state_user(conn);
573 /* Other mechanisms not supported */
574 infof(conn->data, "No known authentication mechanisms supported!\n");
575 result = CURLE_LOGIN_DENIED;
581 /* Start the DO phase for the command */
582 static CURLcode pop3_command(struct connectdata *conn)
584 CURLcode result = CURLE_OK;
585 struct SessionHandle *data = conn->data;
586 struct POP3 *pop3 = data->state.proto.pop3;
587 const char *command = NULL;
589 /* Calculate the default command */
590 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
593 if(pop3->id[0] != '\0')
594 /* Message specific LIST so skip the BODY transfer */
595 pop3->transfer = FTPTRANSFER_INFO;
600 /* Send the command */
601 if(pop3->id[0] != '\0')
602 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
603 (pop3->custom && pop3->custom[0] != '\0' ?
604 pop3->custom : command), pop3->id);
606 result = Curl_pp_sendf(&conn->proto.pop3c.pp,
607 (pop3->custom && pop3->custom[0] != '\0' ?
608 pop3->custom : command));
611 state(conn, POP3_COMMAND);
616 /* For the initial server greeting */
617 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
621 CURLcode result = CURLE_OK;
622 struct SessionHandle *data = conn->data;
624 (void)instate; /* no use for this yet */
626 if(pop3code != '+') {
627 failf(data, "Got unexpected pop3-server response");
628 return CURLE_FTP_WEIRD_SERVER_REPLY;
631 result = pop3_state_capa(conn);
636 /* For CAPA responses */
637 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
640 CURLcode result = CURLE_OK;
641 struct SessionHandle *data = conn->data;
642 struct pop3_conn *pop3c = &conn->proto.pop3c;
644 (void)instate; /* no use for this yet */
647 result = pop3_state_user(conn);
648 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
649 /* We don't have a SSL/TLS connection yet, but SSL is requested */
650 if(pop3c->tls_supported)
651 /* Switch to TLS connection now */
652 result = pop3_state_starttls(conn);
653 else if(data->set.use_ssl == CURLUSESSL_TRY)
654 /* Fallback and carry on with authentication */
655 result = pop3_authenticate(conn);
657 failf(data, "STLS not supported.");
658 result = CURLE_USE_SSL_FAILED;
662 result = pop3_authenticate(conn);
667 /* For STARTTLS responses */
668 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
672 CURLcode result = CURLE_OK;
673 struct SessionHandle *data = conn->data;
675 (void)instate; /* no use for this yet */
677 if(pop3code != '+') {
678 if(data->set.use_ssl != CURLUSESSL_TRY) {
679 failf(data, "STARTTLS denied. %c", pop3code);
680 result = CURLE_USE_SSL_FAILED;
683 result = pop3_authenticate(conn);
686 result = pop3_state_upgrade_tls(conn);
691 /* For AUTH PLAIN responses */
692 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
696 CURLcode result = CURLE_OK;
697 struct SessionHandle *data = conn->data;
699 char *plainauth = NULL;
701 (void)instate; /* no use for this yet */
703 if(pop3code != '+') {
704 failf(data, "Access denied. %c", pop3code);
705 result = CURLE_LOGIN_DENIED;
708 /* Create the authorisation message */
709 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
712 /* Send the message */
715 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
718 state(conn, POP3_AUTH_FINAL);
721 Curl_safefree(plainauth);
728 /* For AUTH LOGIN responses */
729 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
733 CURLcode result = CURLE_OK;
734 struct SessionHandle *data = conn->data;
736 char *authuser = NULL;
738 (void)instate; /* no use for this yet */
740 if(pop3code != '+') {
741 failf(data, "Access denied: %d", pop3code);
742 result = CURLE_LOGIN_DENIED;
745 /* Create the user message */
746 result = Curl_sasl_create_login_message(data, conn->user,
752 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
755 state(conn, POP3_AUTH_LOGIN_PASSWD);
758 Curl_safefree(authuser);
765 /* For AUTH LOGIN user entry responses */
766 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
770 CURLcode result = CURLE_OK;
771 struct SessionHandle *data = conn->data;
773 char *authpasswd = NULL;
775 (void)instate; /* no use for this yet */
777 if(pop3code != '+') {
778 failf(data, "Access denied: %d", pop3code);
779 result = CURLE_LOGIN_DENIED;
782 /* Create the password message */
783 result = Curl_sasl_create_login_message(data, conn->passwd,
786 /* Send the password */
789 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
792 state(conn, POP3_AUTH_FINAL);
795 Curl_safefree(authpasswd);
802 #ifndef CURL_DISABLE_CRYPTO_AUTH
803 /* For AUTH CRAM-MD5 responses */
804 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
808 CURLcode result = CURLE_OK;
809 struct SessionHandle *data = conn->data;
810 char *chlg64 = data->state.buffer;
812 char *rplyb64 = NULL;
814 (void)instate; /* no use for this yet */
816 if(pop3code != '+') {
817 failf(data, "Access denied: %d", pop3code);
818 return CURLE_LOGIN_DENIED;
821 /* Get the challenge */
822 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
825 /* Terminate the challenge */
827 for(len = strlen(chlg64); len--;)
828 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
837 /* Create the response message */
838 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
839 conn->passwd, &rplyb64, &len);
841 /* Send the response */
844 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
847 state(conn, POP3_AUTH_FINAL);
850 Curl_safefree(rplyb64);
856 /* For AUTH DIGEST-MD5 challenge responses */
857 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
861 CURLcode result = CURLE_OK;
862 struct SessionHandle *data = conn->data;
863 char *chlg64 = data->state.buffer;
865 char *rplyb64 = NULL;
867 (void)instate; /* no use for this yet */
869 if(pop3code != '+') {
870 failf(data, "Access denied: %d", pop3code);
871 return CURLE_LOGIN_DENIED;
874 /* Get the challenge */
875 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
878 /* Create the response message */
879 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
883 /* Send the response */
886 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
889 state(conn, POP3_AUTH_DIGESTMD5_RESP);
892 Curl_safefree(rplyb64);
898 /* For AUTH DIGEST-MD5 challenge-response responses */
899 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
903 CURLcode result = CURLE_OK;
904 struct SessionHandle *data = conn->data;
906 (void)instate; /* no use for this yet */
908 if(pop3code != '+') {
909 failf(data, "Authentication failed: %d", pop3code);
910 result = CURLE_LOGIN_DENIED;
913 /* Send an empty response */
914 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
917 state(conn, POP3_AUTH_FINAL);
925 /* For AUTH NTLM responses */
926 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
930 CURLcode result = CURLE_OK;
931 struct SessionHandle *data = conn->data;
933 char *type1msg = NULL;
935 (void)instate; /* no use for this yet */
937 if(pop3code != '+') {
938 failf(data, "Access denied: %d", pop3code);
939 result = CURLE_LOGIN_DENIED;
942 /* Create the type-1 message */
943 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
947 /* Send the message */
950 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
953 state(conn, POP3_AUTH_NTLM_TYPE2MSG);
956 Curl_safefree(type1msg);
963 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
964 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
968 CURLcode result = CURLE_OK;
969 struct SessionHandle *data = conn->data;
971 char *type3msg = NULL;
973 (void)instate; /* no use for this yet */
975 if(pop3code != '+') {
976 failf(data, "Access denied: %d", pop3code);
977 result = CURLE_LOGIN_DENIED;
980 /* Create the type-3 message */
981 result = Curl_sasl_create_ntlm_type3_message(data,
982 data->state.buffer + 2,
983 conn->user, conn->passwd,
987 /* Send the message */
990 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
993 state(conn, POP3_AUTH_FINAL);
996 Curl_safefree(type3msg);
1004 /* For final responses to the AUTH sequence */
1005 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1009 CURLcode result = CURLE_OK;
1010 struct SessionHandle *data = conn->data;
1012 (void)instate; /* no use for this yet */
1014 if(pop3code != '+') {
1015 failf(data, "Authentication failed: %d", pop3code);
1016 result = CURLE_LOGIN_DENIED;
1019 /* End of connect phase */
1020 state(conn, POP3_STOP);
1025 #ifndef CURL_DISABLE_CRYPTO_AUTH
1026 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1029 CURLcode result = CURLE_OK;
1030 struct SessionHandle *data = conn->data;
1032 (void)instate; /* no use for this yet */
1034 if(pop3code != '+') {
1035 failf(data, "Authentication failed: %d", pop3code);
1036 result = CURLE_LOGIN_DENIED;
1039 /* End of connect phase */
1040 state(conn, POP3_STOP);
1046 /* For USER responses */
1047 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1050 CURLcode result = CURLE_OK;
1051 struct SessionHandle *data = conn->data;
1053 (void)instate; /* no use for this yet */
1055 if(pop3code != '+') {
1056 failf(data, "Access denied. %c", pop3code);
1057 result = CURLE_LOGIN_DENIED;
1060 /* Send the PASS command */
1061 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1062 conn->passwd ? conn->passwd : "");
1064 state(conn, POP3_PASS);
1069 /* For PASS responses */
1070 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1073 CURLcode result = CURLE_OK;
1074 struct SessionHandle *data = conn->data;
1076 (void)instate; /* no use for this yet */
1078 if(pop3code != '+') {
1079 failf(data, "Access denied. %c", pop3code);
1080 result = CURLE_LOGIN_DENIED;
1083 /* End of connect phase */
1084 state(conn, POP3_STOP);
1089 /* For command responses */
1090 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1094 CURLcode result = CURLE_OK;
1095 struct SessionHandle *data = conn->data;
1096 struct POP3 *pop3 = data->state.proto.pop3;
1097 struct pop3_conn *pop3c = &conn->proto.pop3c;
1098 struct pingpong *pp = &pop3c->pp;
1100 (void)instate; /* no use for this yet */
1102 if(pop3code != '+') {
1103 state(conn, POP3_STOP);
1104 return CURLE_RECV_ERROR;
1107 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1108 EOB string so count this is two matching bytes. This is necessary to make
1109 the code detect the EOB if the only data than comes now is %2e CR LF like
1110 when there is no body to return. */
1113 /* But since this initial CR LF pair is not part of the actual body, we set
1114 the strip counter here so that these bytes won't be delivered. */
1117 if(pop3->transfer == FTPTRANSFER_BODY) {
1119 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1122 /* The header "cache" contains a bunch of data that is actually body
1123 content so send it as such. Note that there may even be additional
1124 "headers" after the body */
1126 if(!data->set.opt_no_body) {
1127 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1132 /* Free the cache */
1133 Curl_safefree(pp->cache);
1135 /* Reset the cache size */
1140 /* End of DO phase */
1141 state(conn, POP3_STOP);
1146 static CURLcode pop3_statemach_act(struct connectdata *conn)
1148 CURLcode result = CURLE_OK;
1149 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1151 struct pop3_conn *pop3c = &conn->proto.pop3c;
1152 struct pingpong *pp = &pop3c->pp;
1155 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1156 if(pop3c->state == POP3_UPGRADETLS)
1157 return pop3_state_upgrade_tls(conn);
1159 /* Flush any data that needs to be sent */
1161 return Curl_pp_flushsend(pp);
1163 /* Read the response from the server */
1164 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1169 /* We have now received a full POP3 server response */
1170 switch(pop3c->state) {
1171 case POP3_SERVERGREET:
1172 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1176 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1180 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1183 case POP3_AUTH_PLAIN:
1184 result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1187 case POP3_AUTH_LOGIN:
1188 result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1191 case POP3_AUTH_LOGIN_PASSWD:
1192 result = pop3_state_auth_login_password_resp(conn, pop3code,
1196 #ifndef CURL_DISABLE_CRYPTO_AUTH
1197 case POP3_AUTH_CRAMMD5:
1198 result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1201 case POP3_AUTH_DIGESTMD5:
1202 result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1205 case POP3_AUTH_DIGESTMD5_RESP:
1206 result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1211 case POP3_AUTH_NTLM:
1212 result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1215 case POP3_AUTH_NTLM_TYPE2MSG:
1216 result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1221 case POP3_AUTH_FINAL:
1222 result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1225 #ifndef CURL_DISABLE_CRYPTO_AUTH
1227 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1232 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1236 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1240 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1244 /* fallthrough, just stop! */
1246 /* internal error */
1247 state(conn, POP3_STOP);
1255 /* Called repeatedly until done from multi.c */
1256 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1258 CURLcode result = CURLE_OK;
1259 struct pop3_conn *pop3c = &conn->proto.pop3c;
1261 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone)
1262 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1264 result = Curl_pp_statemach(&pop3c->pp, FALSE);
1266 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1271 static CURLcode pop3_block_statemach(struct connectdata *conn)
1273 CURLcode result = CURLE_OK;
1274 struct pop3_conn *pop3c = &conn->proto.pop3c;
1276 while(pop3c->state != POP3_STOP && !result)
1277 result = Curl_pp_statemach(&pop3c->pp, TRUE);
1282 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1284 static CURLcode pop3_init(struct connectdata *conn)
1286 CURLcode result = CURLE_OK;
1287 struct SessionHandle *data = conn->data;
1288 struct POP3 *pop3 = data->state.proto.pop3;
1291 pop3 = data->state.proto.pop3 = calloc(sizeof(struct POP3), 1);
1293 result = CURLE_OUT_OF_MEMORY;
1299 /* For the POP3 "protocol connect" and "doing" phases only */
1300 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1303 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1306 /***********************************************************************
1310 * This function should do everything that is to be considered a part of the
1313 * The variable 'done' points to will be TRUE if the protocol-layer connect
1314 * phase is done when this function returns, or FALSE is not. When called as
1315 * a part of the easy interface, it will always be TRUE.
1317 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1319 CURLcode result = CURLE_OK;
1320 struct pop3_conn *pop3c = &conn->proto.pop3c;
1321 struct pingpong *pp = &pop3c->pp;
1323 *done = FALSE; /* default to not done yet */
1325 /* If there already is a protocol-specific struct allocated for this
1326 sessionhandle, deal with it */
1327 Curl_reset_reqproto(conn);
1329 /* Initialise the POP3 layer */
1330 result = pop3_init(conn);
1334 /* We always support persistent connections in POP3 */
1335 conn->bits.close = FALSE;
1337 /* Set the default response time-out */
1338 pp->response_time = RESP_TIMEOUT;
1339 pp->statemach_act = pop3_statemach_act;
1340 pp->endofresp = pop3_endofresp;
1343 /* Set the default preferred authentication type and mechanism */
1344 pop3c->preftype = POP3_TYPE_ANY;
1345 pop3c->prefmech = SASL_AUTH_ANY;
1347 /* Initialise the pingpong layer */
1350 /* Parse the URL options */
1351 result = pop3_parse_url_options(conn);
1355 /* Start off waiting for the server greeting response */
1356 state(conn, POP3_SERVERGREET);
1358 result = pop3_multi_statemach(conn, done);
1363 /***********************************************************************
1367 * The DONE function. This does what needs to be done after a single DO has
1370 * Input argument is already checked for validity.
1372 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1375 CURLcode result = CURLE_OK;
1376 struct SessionHandle *data = conn->data;
1377 struct POP3 *pop3 = data->state.proto.pop3;
1382 /* When the easy handle is removed from the multi interface while libcurl
1383 is still trying to resolve the host name, the POP3 struct is not yet
1384 initialized. However, the removal action calls Curl_done() which in
1385 turn calls this function, so we simply return success. */
1389 conn->bits.close = TRUE; /* marked for closure */
1390 result = status; /* use the already set error code */
1393 /* Cleanup our per-request based variables */
1394 Curl_safefree(pop3->id);
1395 Curl_safefree(pop3->custom);
1397 /* Clear the transfer mode for the next request */
1398 pop3->transfer = FTPTRANSFER_BODY;
1403 /***********************************************************************
1407 * This is the actual DO function for POP3. Get a message/listing according to
1408 * the options previously setup.
1410 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1413 /* This is POP3 and no proxy */
1414 CURLcode result = CURLE_OK;
1416 DEBUGF(infof(conn->data, "DO phase starts\n"));
1418 if(conn->data->set.opt_no_body) {
1419 /* Requested no body means no transfer */
1420 struct POP3 *pop3 = conn->data->state.proto.pop3;
1421 pop3->transfer = FTPTRANSFER_INFO;
1424 *dophase_done = FALSE; /* not done yet */
1426 /* Start the first command in the DO phase */
1427 result = pop3_command(conn);
1431 /* Run the state-machine */
1432 result = pop3_multi_statemach(conn, dophase_done);
1434 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1437 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1442 /***********************************************************************
1446 * This function is registered as 'curl_do' function. It decodes the path
1447 * parts etc as a wrapper to the actual DO function (pop3_perform).
1449 * The input argument is already checked for validity.
1451 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1453 CURLcode result = CURLE_OK;
1455 *done = FALSE; /* default to false */
1457 /* Since connections can be re-used between SessionHandles, there might be a
1458 connection already existing but on a fresh SessionHandle struct. As such
1459 we make sure we have a good POP3 struct to play with. For new connections
1460 the POP3 struct is allocated and setup in the pop3_connect() function. */
1461 Curl_reset_reqproto(conn);
1462 result = pop3_init(conn);
1466 /* Parse the URL path */
1467 result = pop3_parse_url_path(conn);
1471 /* Parse the custom request */
1472 result = pop3_parse_custom_request(conn);
1476 result = pop3_regular_transfer(conn, done);
1481 /***********************************************************************
1485 * Performs the quit action prior to sclose() be called.
1487 static CURLcode pop3_quit(struct connectdata *conn)
1489 CURLcode result = CURLE_OK;
1491 /* Send the QUIT command */
1492 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT");
1495 state(conn, POP3_QUIT);
1500 /***********************************************************************
1504 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1505 * resources. BLOCKING.
1507 static CURLcode pop3_disconnect(struct connectdata *conn,
1508 bool dead_connection)
1510 struct pop3_conn *pop3c = &conn->proto.pop3c;
1512 /* We cannot send quit unconditionally. If this connection is stale or
1513 bad in any way, sending quit and waiting around here will make the
1514 disconnect wait in vain and cause more problems than we need to. */
1516 /* The POP3 session may or may not have been allocated/setup at this
1518 if(!dead_connection && pop3c->pp.conn)
1519 if(!pop3_quit(conn))
1520 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1522 /* Disconnect from the server */
1523 Curl_pp_disconnect(&pop3c->pp);
1525 /* Cleanup the SASL module */
1526 Curl_sasl_cleanup(conn, pop3c->authused);
1528 /* Cleanup our connection based variables */
1529 Curl_safefree(pop3c->apoptimestamp);
1534 /* Call this when the DO phase has completed */
1535 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1543 /* Called from multi.c while DOing */
1544 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1546 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1549 DEBUGF(infof(conn->data, "DO phase failed\n"));
1550 else if(*dophase_done) {
1551 result = pop3_dophase_done(conn, FALSE /* not connected */);
1553 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1559 /***********************************************************************
1561 * pop3_regular_transfer()
1563 * The input argument is already checked for validity.
1565 * Performs all commands done before a regular transfer between a local and a
1568 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1571 CURLcode result = CURLE_OK;
1572 bool connected = FALSE;
1573 struct SessionHandle *data = conn->data;
1575 /* Make sure size is unknown at this point */
1576 data->req.size = -1;
1578 /* Set the progress data */
1579 Curl_pgrsSetUploadCounter(data, 0);
1580 Curl_pgrsSetDownloadCounter(data, 0);
1581 Curl_pgrsSetUploadSize(data, 0);
1582 Curl_pgrsSetDownloadSize(data, 0);
1584 /* Carry out the perform */
1585 result = pop3_perform(conn, &connected, dophase_done);
1587 /* Perform post DO phase operations if necessary */
1588 if(!result && *dophase_done)
1589 result = pop3_dophase_done(conn, connected);
1594 static CURLcode pop3_setup_connection(struct connectdata *conn)
1596 struct SessionHandle *data = conn->data;
1598 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1599 /* Unless we have asked to tunnel POP3 operations through the proxy, we
1600 switch and use HTTP operations only */
1601 #ifndef CURL_DISABLE_HTTP
1602 if(conn->handler == &Curl_handler_pop3)
1603 conn->handler = &Curl_handler_pop3_proxy;
1606 conn->handler = &Curl_handler_pop3s_proxy;
1608 failf(data, "POP3S not supported!");
1609 return CURLE_UNSUPPORTED_PROTOCOL;
1613 /* We explicitly mark this connection as persistent here as we're doing
1614 POP3 over HTTP and thus we accidentally avoid setting this value
1616 conn->bits.close = FALSE;
1618 failf(data, "POP3 over http proxy requires HTTP support built-in!");
1619 return CURLE_UNSUPPORTED_PROTOCOL;
1623 data->state.path++; /* don't include the initial slash */
1628 /***********************************************************************
1630 * pop3_parse_url_options()
1632 * Parse the URL login options.
1634 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1636 CURLcode result = CURLE_OK;
1637 struct pop3_conn *pop3c = &conn->proto.pop3c;
1638 const char *options = conn->options;
1639 const char *ptr = options;
1642 const char *key = ptr;
1644 while(*ptr && *ptr != '=')
1647 if(strnequal(key, "AUTH", 4)) {
1648 const char *value = ptr + 1;
1650 if(strequal(value, "*")) {
1651 pop3c->preftype = POP3_TYPE_ANY;
1652 pop3c->prefmech = SASL_AUTH_ANY;
1654 else if(strequal(value, "+APOP")) {
1655 pop3c->preftype = POP3_TYPE_APOP;
1656 pop3c->prefmech = SASL_AUTH_NONE;
1658 else if(strequal(value, "LOGIN")) {
1659 pop3c->preftype = POP3_TYPE_SASL;
1660 pop3c->prefmech = SASL_MECH_LOGIN;
1662 else if(strequal(value, "PLAIN")) {
1663 pop3c->preftype = POP3_TYPE_SASL;
1664 pop3c->prefmech = SASL_MECH_PLAIN;
1666 else if(strequal(value, "CRAM-MD5")) {
1667 pop3c->preftype = POP3_TYPE_SASL;
1668 pop3c->prefmech = SASL_MECH_CRAM_MD5;
1670 else if(strequal(value, "DIGEST-MD5")) {
1671 pop3c->preftype = POP3_TYPE_SASL;
1672 pop3c->prefmech = SASL_MECH_DIGEST_MD5;
1674 else if(strequal(value, "GSSAPI")) {
1675 pop3c->preftype = POP3_TYPE_SASL;
1676 pop3c->prefmech = SASL_MECH_GSSAPI;
1678 else if(strequal(value, "NTLM")) {
1679 pop3c->preftype = POP3_TYPE_SASL;
1680 pop3c->prefmech = SASL_MECH_NTLM;
1683 pop3c->preftype = POP3_TYPE_NONE;
1684 pop3c->prefmech = SASL_AUTH_NONE;
1688 result = CURLE_URL_MALFORMAT;
1694 /***********************************************************************
1696 * pop3_parse_url_path()
1698 * Parse the URL path into separate path components.
1700 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1702 /* The POP3 struct is already initialised in pop3_connect() */
1703 struct SessionHandle *data = conn->data;
1704 struct POP3 *pop3 = data->state.proto.pop3;
1705 const char *path = data->state.path;
1707 /* URL decode the path for the message ID */
1708 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1711 /***********************************************************************
1713 * pop3_parse_custom_request()
1715 * Parse the custom request.
1717 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1719 CURLcode result = CURLE_OK;
1720 struct SessionHandle *data = conn->data;
1721 struct POP3 *pop3 = data->state.proto.pop3;
1722 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1724 /* URL decode the custom request */
1726 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1731 /***********************************************************************
1735 * This function scans the body after the end-of-body and writes everything
1736 * until the end is found.
1738 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1740 /* This code could be made into a special function in the handler struct */
1741 CURLcode result = CURLE_OK;
1742 struct SessionHandle *data = conn->data;
1743 struct SingleRequest *k = &data->req;
1745 struct pop3_conn *pop3c = &conn->proto.pop3c;
1746 bool strip_dot = FALSE;
1750 /* Search through the buffer looking for the end-of-body marker which is
1751 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1752 the eob so the server will have prefixed it with an extra dot which we
1753 need to strip out. Additionally the marker could of course be spread out
1754 over 5 different data chunks. */
1755 for(i = 0; i < nread; i++) {
1756 size_t prev = pop3c->eob;
1760 if(pop3c->eob == 0) {
1764 /* Write out the body part that didn't match */
1765 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1774 else if(pop3c->eob == 3)
1777 /* If the character match wasn't at position 0 or 3 then restart the
1783 if(pop3c->eob == 1 || pop3c->eob == 4)
1786 /* If the character match wasn't at position 1 or 4 then start the
1794 else if(pop3c->eob == 3) {
1795 /* We have an extra dot after the CRLF which we need to strip off */
1800 /* If the character match wasn't at position 2 then start the search
1810 /* Did we have a partial match which has subsequently failed? */
1811 if(prev && prev >= pop3c->eob) {
1812 /* Strip can only be non-zero for the very first mismatch after CRLF
1813 and then both prev and strip are equal and nothing will be output
1815 while(prev && pop3c->strip) {
1821 /* If the partial match was the CRLF and dot then only write the CRLF
1822 as the server would have inserted the dot */
1823 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1824 strip_dot ? prev - 1 : prev);
1835 if(pop3c->eob == POP3_EOB_LEN) {
1836 /* We have a full match so the transfer is done, however we must transfer
1837 the CRLF at the start of the EOB as this is considered to be part of the
1838 message as per RFC-1939, sect. 3 */
1839 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1841 k->keepon &= ~KEEP_RECV;
1848 /* While EOB is matching nothing should be output */
1852 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1859 #endif /* CURL_DISABLE_POP3 */